What would be a set of nifty preprocessor hacks (ANSI C89/ISO C90 compatible) which enable some kind of ugly (but usable) object-orientation in C?
I am familiar with a few different object-oriented languages, so please don't respond with answers like "Learn C++!". I have read "Object-Oriented Programming With ANSI C" (beware: PDF format) and several other interesting solutions, but I'm mostly interested in yours :-)!
The GNOME desktop for Linux is written in object-oriented C, and it has an object model called "GObject" which supports properties, inheritance, polymorphism, as well as some other goodies like references, event handling (called "signals"), runtime typing, private data, etc.
It includes preprocessor hacks to do things like typecasting around in the class hierarchy, etc. Here's an example class I wrote for GNOME (things like gchar are typedefs):
Class Source
Class Header
Inside the GObject structure there's a GType integer which is used as a magic number for GLib's dynamic typing system (you can cast the entire struct to a "GType" to find it's type).
If you need to write a little code try this: https://github.com/fulminati/class-framework
I used to do this kind of thing in C, before I knew what OOP was.
Following is an example, which implements a data-buffer which grows on demand, given a minimum size, increment and maximum size. This particular implementation was "element" based, which is to say it was designed to allow a list-like collection of any C type, not just a variable length byte-buffer.
The idea is that the object is instantiated using the xxx_crt() and deleted using xxx_dlt(). Each of the "member" methods takes a specifically typed pointer to operate on.
I implemented a linked list, cyclic buffer, and a number of other things in this manner.
I must confess, I have never given any thought on how to implement inheritance with this approach. I imagine that some blend of that offered by Kieveli might be a good path.
dtb.c:
dtb.h
PS: vint was simply a typedef of int - I used it to remind me that it's length was variable from platform to platform (for porting).
I think what Adam Rosenfield posted is the correct way of doing OOP in C. I'd like to add that what he shows is the implementation of the object. In other words the actual implementation would be put in the
.c
file, while the interface would be put in the header.h
file. For example, using the monkey example above:The interface would look like:
You can see in the interface
.h
file you are only defining prototypes. You can then compile the implementation part ".c
file" into a static or dynamic library. This creates encapsulation and also you can change the implementation at will. The user of your object needs to know almost nothing about the implementation of it. This also places focus on the overall design of the object.It's my personal belief that oop is a way of conceptualizing your code structure and reusability and has really nothing to do with those other things that are added to c++ like overloading or templates. Yes those are very nice useful features but they are not representative of what object oriented programming really is.
If you think of methods called on objects as static methods that pass an implicit '
this
' into the function it can make thinking OO in C easier.For example:
becomes:
Or something like that.
Output:
Here is a show of what is OO programming with C.
This is real, pure C, no preprocessor macros. We have inheritance, polymorphism and data encapsulation (including data private to classes or objects). There is no chance for protected qualifier equivalent, that is, private data is private down the innheritance chain too. But this is not an inconvenience because I don't think it is necessary.
CPolygon
is not instantiated because we only use it to manipulate objects of down the innheritance chain that have common aspects but different implementation of them (Polymorphism).