4 ms·
> Making Scheme object-oriented is a sophomore homework assignment. On the other hand, adding object orientation to C requires the programming chops of Bjarne S
by badsectoracula 5y ago
> Making Scheme object-oriented is a sophomore homework assignment. On the other hand, adding object orientation to C requires the programming chops of Bjarne Stroustrup.
C++ added a ton of additional stuff to C. The original Objective-C is a much simpler approach on adding object orientation to C and at its core it'd all be about finding expressions like
[foo bar:baz boo:hoo]
and replacing them with something like
obj_send(foo, msg_foobarboo, baz, hoo);
and linking with an underlying runtime that handles message passing of course.
An alternative would be Turbo Pascal 5.5-style OOP where
struct foo : base {
foo();
~foo();
void blah();
}
foo::foo(){ base(); }
foo::~foo(){ ~base(); }
void foo::blah(){}
would be accepted but aside from the C++-like syntax for constructors, destructors (which handle the hidden VMT field) and methods nothing is done automatically and instead you are expected to explicitly construct and destroy the objects (if needed), e.g.
struct foo foo;
foo.foo(); /* call constructor chain */
foo.~foo(); /* call destructor chain */
or
struct foo* foo = malloc(sizeof(struct foo));
foo->foo(); /* call constructor chain */
foo->~foo(); /* call destructor chain */
free(foo);
Note that Objective-C handles (or handled at the past, not sure how things changed nowadays) allocation and construction in separate steps too.
Of course unlike a language with Lisp-like macros you'd need to modify a compiler or write a preprocessor to do the above, which makes it a bit harder, but in any case adding object orientation to C does not mean you have to make it as complex as C++.
- edgyquant 5y agoWhat’s the point of a constructor that isn’t called immediately? Couldn’t you just use any arbitrary function at that point?
- badsectoracula 5y agoIf a constructor is called automatically then it also needs the destructor to be called automatically and both introduce a bunch of additional complexity, especially for stack allocated functions, the need to take into account the concept of a default constructor (not necessarily requiring it but still needing to consider it), etc so that someone doing... while (something) { foo foos[42]; /* use foos here */ } ...will still have their 42 foo constructors and destructors called at the appropriate times. Meanwhile by doing it manually you just bypass all that complexity at the language level and it becomes the programmer's responsibility to do it (or not) properly. Also while you often want the above, this isn't something you always want, e.g. when you need to differentiate between where the memory of "foo" comes from and its lifecycle as an object (e.g. custom memory allocators, reusing objects without allocating/releasing their memory, etc). Of course all the above could be done with functions too, after all there are a bunch of C programs and libraries doing OOP without the language itself providing any support for it.
- xscott 5y agoYeah, I agree. If you're willing to use a garbage collection library [0], you can even have a pleasant syntax for ObjectiveC style OOP in C: var result = call(foo, "bar: %s boo: %f", baz, hoo); The garbage collector will pick up the stray memory and release other resources (files) as needed, and all the standard compilers will check the varargs format string for you. Dynamic typing, arbitrary messages, and no memory leaks. Just like Lisp. It's not perfect (string message could have typos, and might use %p for objects), but it's not too bad. [0] https://en.wikipedia.org/wiki/Boehm_garbage_collector https://en.wikipedia.org/wiki/Boehm_garbage_collector
- lisper 5y agoWhy do you need GC to support this syntax? The syntax has nothing to do with whether or not the semantics of the language permit memory leaks.
- xscott 5y agoYou don't need the GC of course, but manually tracking and releasing the memory takes all the fun out of this kind of high level programming, particularly when you start to nest expressions. As a hypothetical example: call( window, "draw: %d %d %p", x, y, alloc("Circle radius: %d color: %u", r, 0xFF0000) ); That ignores the return value and loses track of the temporary argument. This is all in response to the article claiming you "need to be Bjarne Stroustrup to add OOP to C, but it's a sophomore assignment to add it to Lisp". If I hadn't mentioned the GC, someone else would've jumped on the example for how unpleasant manual memory management is in comparison to Lisp.
- lisper 5y agoI was responding to this: > If you're willing to use a garbage collection library [0], you can even have a pleasant syntax for ObjectiveC style OOP in C: That sounded to me as if you were claiming that GC is a pre-requisite for having a particular syntax. But I gather that's not what you meant. I'm still a little unclear about what you actually meant. You can have memory leaks like the one in your example above in vanilla C without any OOP features at all. GC is completely orthogonal to OOP, and both are completely orthogonal to syntax.