You can implement polymorphism with regular functions and virtual tables (vtables). Here's a pretty neat system that I invented (based on C++) for a programming exercise:
The constructors allocate memory and then call the class's init function where the memory is initialized. Each init function should also contain a static vtable struct that contains the virtual function pointers (NULL for pure virtual). Derived class init functions call the superclass init function before doing anything else.
A very nice API can be created by implementing the virtual function wrappers (not to be confused with the functions pointed to by the vtables) as follows (add static inline
in front of it, if you do this in the header):
int playerGuess(Player* this) { return this->vtable->guess(this); }
Single inheritance can be done by abusing the binary layout of a struct:
Notice that multiple inheritance is messier as then you often need to adjust the pointer value when casting between types of the hierarchy.
Other type-specific data can be added to the virtual tables as well. Examples include runtime type info (e.g. type name as a string), linking to superclass vtable and the destructor chain. You probably want virtual destructors where derived class destructor demotes the object to its super class and then recursively calls the destructor of that and so on, until the base class destructor is reached and that finally frees the struct.
Encapsulation was done by defining the structs in player_protected.h and implementing the functions (pointed to by the vtable) in player_protected.c, and similarly for derived classes, but this is quite clumsy and it degrades performance (as virtual wrappers cannot be put to headers), so I would recommend against it.
Have you read the "bible" on the subject? See Object Oriented C...
How would you emulate encapsulation and inheritance though?
Actually, encapsulation is the easiest part. Encapsulation is a design philosophy, it has nothing at all to do with the language and everything to to with how you think about problems.
For example, the Windows FILE api is completely encapsulated. When you open a file, you get back an opaque object that contains all of the state information for the file 'object'. You hand this handle back to each of the file io apis. The encapsulation is actually much better than C++ because there is no public header file that people can look at and see the names of your private variables.
Inheritance is harder, but it isn't at all necessary in order for your code to be object oriented. In some ways aggregation is better than inheritance anyway, and aggregation is just as easy in C as in C++. see this for instance.
In response to Neil see Wikipedia for an explanation of why inheritance isn't necessary for polymorphism.
Us old-timers wrote object oriented code years before C++ compilers were available, it's a mind-set not a tool-set.
Apple's C-based CoreFoundation framework was actually written so that its "objects" could double as objects in Objective-C, an actual OO language. A fairly large subset of the framework is open source on Apple's site as CF-Lite. Might be a useful case study in a major OS-level framework done this way.
From a little bit higher altitude and considering the problem rather more open-minded than as the OOP mainstream may suggest, Object-Oriented Programming means thinking about objects as of data with associated functions. It does not necessarily mean an function has to be physically attached to an object as it is in popular languages which support paradigm of OOP, for instance in C++:
struct T
{
int data;
int get_data() const { return data; }
};
I would suggest to take a closer look at GTK+ Object and Type System. It is a brilliant example of OOP realised in C programming language:
GTK+ implements its own custom object system, which offers standard object-oriented features such as inheritance and virtual function
The association can also be contractual and conventional.
Regarding encapsulation and data hiding techniques, popular and simple one may be Opaque Pointer (or Opaque Data Type) - you can pass it around but in order to load or store any information, you have to call associated function which knows how to talk to the object hidden behind that opaque pointer.
Another one, similar but different is Shadow Data type - check this link where Jon Jagger gives excellent explanation of this not-so-well-known-technique.
the gtk and glib libraries use macros to cast objects to various types.
add_widget(GTK_WIDGET(myButton));
I can't say how it's done but you can read their source to find out exactly how it's done.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With