is this how you guys would create a "class" in C? How about an interface? I'm guessing we just use function pointers for everything and we can change where the function pointers point to?
#ifndef CLASS
#define CLASS
void method1() { printf("method #1"); }
void method2() { printf("method #2"); }
typedef struct {
void (*method1)(void);
void (*method2)(void);
} Class;
Class class;
void class_setup()
{
class.method1 = &method1;
class.method2 = &method2;
}
#endif
int main()
{
class_setup();
class.method1();
class.method2();
return 0;
}
Actually, there are two ways to do OOP in C. And while function pointers do play a huge role in them, we do not put them inside the "class" structures that much - primarily because we do not have this
in C.
It is a most common and, while not exactly pretty, still usable approach. Just have a bunch of functions to do something with an object. For example, please take a look at Glib and GTK+. These two libraries are de-facto the most used and usable object oriented libraries in C. In case of GTK+, to work with "sibling" classes you use "sibling" functions:
void gtk_window_set_child (GtkWindow* window, GtkWidget* child);
void gtk_button_set_child (GtkButton* button, GtkWidget* child);
As you can see, you need to pass a pointer to object (this
) as a first parameter and distinction to which class this method belongs to is done by function names.
At the same time, both classes (GtkWindow
and GtkButton
) are children of GtkWidget
and can be passed directly to gtk_widget_*()
functions with a manual type conversion:
GtkButton *button = gtk_button_new();
gtk_widget_set_size_request((GtkWidget*)button, 100, 100);
Actually, early C++ compilers were just preprocessors which made a C code using same approach. A method cls::foo()
becomes a function cls_foo(cls *this)
. And even now, if you compile C++ code into a shared library, you will see inside just a bunch of functions with names like this. Read about "name mangling" and how it is done by different compilers.
This approach to do OOP, is much less used. In commercial product it was implemented in Photon - GUI library for old QNX (newer version of OS switched to C++ for GUI). As example of open source project: IUP
It uses a different approach to methods - a hash array of function pointers. When you creating a new class, you need to register its unique type in the list of classes and register all methods. Also there is no parent-child data types, only a base data type with a class identifier inside an object. Not very easy inside, but this allows a more convenient way for practical application:
PtWidget_t *window = PtCreateWidget( PtWindow, Pt_NO_PARENT, 0, NULL);
PtArg_t const btn_args[] = {...};
PtWidget_t *button = PtCreateWidget( PtButton, window, btn_arg_cnt, btn_args);
// Here PtWindow and PtButton are references to registered classes
// and both widgets can be passed to a single function
PtSetResource (window, Pt_ARG_TEXT_STRING, "Window title", 0);
PtSetResource (button, Pt_ARG_TEXT_STRING, "Click Me!", 0);
And behind the scene the PtSetResource()
function will use a two dimensional hash table of function pointers keyed on class references (PtWindow
or PtButton
read from inside an object) and on method selector (Pt_ARG_TEXT_STRING
) passed directly to the function.