26

I know it sounds silly and I know that C is not an Object Oriented Language.

But is there any way that dynamic method dispatching can be achieved in C? I thought about function pointers but don't get the entire idea.

How could I implement this?

jxh
  • 69,070
  • 8
  • 110
  • 193
Dineshkumar
  • 4,165
  • 5
  • 29
  • 43
  • 5
    You can do this by mimicking the way that it is done in other languages "under the hood". In c++ for example dispatch is performed by looking up an index into a table of function pointers. When you subclass something you add your porters to the end of the table. You can use a struct of function porters to represent the table and just include the parent class's strict at the beginning of yours for inheritance. – Will Jul 12 '13 at 18:26
  • 1
    Let's make this a practical discussion by telling what you want to achieve in your application. Then we can supply code examples and workable ideas. Your current question is rather vague and can be answered with Google in 15 minutes. – meaning-matters Jul 12 '13 at 18:31
  • well someone would prefer that if they have something code written already in c but to add some functionality. instead of writing from scratch using OOlanguage. – Dineshkumar Jul 12 '13 at 18:33
  • 1
    Would you consider a callback function a dynamic method dispatch? – jxh Jul 12 '13 at 18:37
  • 1
    Sounds like you you would benefit from reading about [«Object Oriented Programming with ANSI C»](http://lazarenko.me/2013/03/20/object-oriented-programming-with-ansi-c/). –  Jul 12 '13 at 19:45
  • object orientation is just a paradigm. – Ryan Haining Jul 13 '13 at 01:34
  • 2
    Two points: Objective-C is a strict superset of C, and was implemented as a specialized pre processor; second some libraries do a great job of oop using naked c with stucts of function pointers and data members... serf http library comes to mind. – Grady Player Jul 13 '13 at 07:11
  • You should read the documentation for [GObject](https://developer.gnome.org/gobject/stable/). – ceving Jul 07 '21 at 11:36

4 Answers4

49

As others have noted, it is certainly possible to implement this in C. Not only is it possible, it is a fairly common mechanism. The most commonly used example is probably the file descriptor interface in UNIX. A read() call on a file descriptor will dispatch to a read function specific to the device or service that provided that file descriptor (was it a file? was it a socket? was it some other kind of device?).

The only trick is to recover the pointer to the concrete type from the abstract type. For file descriptors, UNIX uses a lookup table that contains information specific to that descriptor. If you are using a pointer to an object, the pointer held by the user of the interface is the "base" type, not the "derived" type. C doesn't have inheritance per se, but it does guarantee that the pointer to the first element of a struct is equal to the pointer of the containing struct. So you can use this to recover the "derived" type by making the instance of the "base" be the first member of the "derived".

Here's a simple example with a stack:

struct Stack {
    const struct StackInterface * const vtable;
};

struct StackInterface {
    int (*top)(struct Stack *);
    void (*pop)(struct Stack *);
    void (*push)(struct Stack *, int);
    int (*empty)(struct Stack *);
    int (*full)(struct Stack *);
    void (*destroy)(struct Stack *);
};

inline int stack_top (struct Stack *s) { return s->vtable->top(s); }
inline void stack_pop (struct Stack *s) { s->vtable->pop(s); }
inline void stack_push (struct Stack *s, int x) { s->vtable->push(s, x); }
inline int stack_empty (struct Stack *s) { return s->vtable->empty(s); }
inline int stack_full (struct Stack *s) { return s->vtable->full(s); }
inline void stack_destroy (struct Stack *s) { s->vtable->destroy(s); }

Now, if I wanted to implement a stack using a fixed sized array, I could do something like this:

struct StackArray {
    struct Stack base;
    int idx;
    int array[STACK_ARRAY_MAX];
};
static int stack_array_top (struct Stack *s) { /* ... */ }
static void stack_array_pop (struct Stack *s) { /* ... */ }
static void stack_array_push (struct Stack *s, int x) { /* ... */ }
static int stack_array_empty (struct Stack *s) { /* ... */ }
static int stack_array_full (struct Stack *s) { /* ... */ }
static void stack_array_destroy (struct Stack *s) { /* ... */ }
struct Stack * stack_array_create () {
    static const struct StackInterface vtable = {
        stack_array_top, stack_array_pop, stack_array_push,
        stack_array_empty, stack_array_full, stack_array_destroy
    };
    static struct Stack base = { &vtable };
    struct StackArray *sa = malloc(sizeof(*sa));
    memcpy(&sa->base, &base, sizeof(base));
    sa->idx = 0;
    return &sa->base;
}

And if I wanted to implement a stack using a list instead:

struct StackList {
    struct Stack base;
    struct StackNode *head;
};
struct StackNode {
    struct StackNode *next;
    int data;
};
static int stack_list_top (struct Stack *s) { /* ... */ }
static void stack_list_pop (struct Stack *s) { /* ... */ }
static void stack_list_push (struct Stack *s, int x) { /* ... */ }
static int stack_list_empty (struct Stack *s) { /* ... */ }
static int stack_list_full (struct Stack *s) { /* ... */ }
static void stack_list_destroy (struct Stack *s) { /* ... */ }
struct Stack * stack_list_create () {
    static const struct StackInterface vtable = {
        stack_list_top, stack_list_pop, stack_list_push,
        stack_list_empty, stack_list_full, stack_list_destroy
    };
    static struct Stack base = { &vtable };
    struct StackList *sl = malloc(sizeof(*sl));
    memcpy(&sl->base, &base, sizeof(base));
    sl->head = 0;
    return &sl->base;
}

The implementations of the stack operations would simply cast the struct Stack * to what it knows it should be. For example:

static int stack_array_empty (struct Stack *s) {
    struct StackArray *sa = (void *)s;
    return sa->idx == 0;
}

static int stack_list_empty (struct Stack *s) {
    struct StackList *sl = (void *)s;
    return sl->head == 0;
}

When a user of a stack invokes a stack operation on the stack instance, the operation will dispatch to the corresponding operation in the vtable. This vtable is initialized by the creation function with the functions that correspond to its particular implementation. So:

Stack *s1 = stack_array_create();
Stack *s2 = stack_list_create();

stack_push(s1, 1);
stack_push(s2, 1);

stack_push() is called on both s1 and s2. But, for s1, it will dispatch to stack_array_push(), while for s2, it will dispatch to stack_list_push().

jxh
  • 69,070
  • 8
  • 110
  • 193
  • 2
    Also, `vtable` member is generally `const`, and points to a constant memory (if MMU/MPU is there and OS does the right thing, of course). –  Sep 27 '13 at 01:26
  • You wrote as: struct StackList *sl = malloc(sizeof(*sl)); But I think it should be as: struct StackList *sl = malloc(sizeof(*StackList)); – LORDTEK Oct 13 '21 at 19:55
  • @LORDTEK I assure you what I wrote is correct. – jxh Oct 18 '22 at 21:11
3

C++ was (initially) built on top of C. The first C++ compilers were actually generating C as intermediary step. Ergo, yes it's possible.

Here's how C++ does things like this.

There's is plenty of solid information available online, more than we can type together here in a few minutes. "Google and you will find."

You said in a comment above:

Well someone would prefer that if they have something code written already in c but to add some functionality. instead of writing from scratch using OOlanguage.

To have functionality like this in C, you'll basically need to reimplement OO language features. Getting people to use this new OO method is the biggest factor against usability. In other words, by creating yet another method for reuse, you'll actually making things less reusable.

meaning-matters
  • 21,929
  • 10
  • 82
  • 142
3

Yes. It can be easily achieved. You would use an array of function pointers, then use those function pointers to make the call. If you want to "override" a function, you just set the corresponding slot to point to the new function. This is exactly how C++ implements virtual functions.

user2555312
  • 174
  • 2
0

I'm a little surprised that no one added glib and/or the whole gtk stuff as an example. So please check: http://www.gtk.org/features.php

Working Link as of 2021-07-07: https://developer.gnome.org/glib/2.26/

I'm aware it's quite some boilerplate code you have to have for using gtk and it's not that "easy" to get right the first time. But if one has used it it's remarkable. The only thing you have to remember is to use a kind of "Object" as first parameter to your functions. But if you look through the API, you<'ll see it's used everywhere. IMHO that makes really a good example on merits and problems one may have with OOP-

Community
  • 1
  • 1
Friedrich
  • 5,916
  • 25
  • 45