1

I want to do some object-oriented style programming in C using polymorphism, where my interface class contains a pointer to a table of functions. Example something like:

/* Implement polymorphism in C, Linux kernel-style */
struct statement {
    const struct statement_ops *ops;
    struct list_head list;      /* when on master input list */
    void *private;          /* pointer to type-specific data */
};

struct statement_ops {
    int (*analyse)(void *private, int pc);
    int (*get_binary_size)(void *private);
};

void user(void)
{
    struct statement *s = make_a_statement();
    if (s->ops->analyse(s->private, foo))
        blah blah;
}

I'd like to be able to write something without explicitly passing s->private into every "method". Any ideas? Some macro tricks maybe?

blueshift
  • 6,742
  • 2
  • 39
  • 63

3 Answers3

6

If this is part of the public interface, you can add accessor functions. A hidden benefit is that you can do sanity checks and other work in the accessor. (Note I called the "this" pointer "o", as in "object". I prefer it that way for consistency.)

int statement_analyse (struct statement *o, int pc)
{
    assert(pc >= 0);

    int ret = o->ops->analyse(o->private, pc);
    assert(ret >= 0);

    return ret;
}

You can now call this without the explicit passing of "private".

void user(void)
{
    struct statement *s = make_a_statement();

    if (statement_analyse(s, foo))
        blah blah;
}

While it may seem that this provides no benefit, because you still have to implement the accessors, assuming that you want a well defined and robust interface, the accessor functions are the only sane place to put the assertions and the interface documentation. In fact, if you write good assertions, the assertions themselves help document the interface. And once you add sanity checks in the accessors, you don't have to add them in the actual methods they call.

Of course, this approach only makes sense when the function called via the function pointer will be something provided by the user, or in some other way can be different things. If there's a single analyse() method that will always do the same thing, you can simply implement a statement_analyse() that directly does what it needs to do.

Small note: when doing OOP, I prefer to typedef the structs and give them CamelCase names. I use this convention as a way of telling that the struct is opaque and should only be accessed via its public interface. It also looks nicer, though that is subjective. I also prefer having the user allocate the memory for the struct itself, as opposed to the constructor malloc'ing it. That avoids having to handle malloc failure, and makes the program a little bit more efficient.

typedef struct {
    ...
} Statement;

void Statement_Init (Statement *o);
int Statement_Analyse (Statement *o, int pc);
Ambroz Bizjak
  • 7,809
  • 1
  • 38
  • 49
  • Fair enough, I suppose that's about as good as it's going to get, with possible macro wrapping. I have a personal aversion to CamelCase :) – blueshift Apr 23 '12 at 03:47
3

Unfortunately, writing your methods to allow the passing of a self or this object is the only way to achieve this in C.

You can use macro tricks to hide part of it, but at that point it's not really C any more.

Williham Totland
  • 28,471
  • 6
  • 52
  • 68
2

As the other answers say, there is no way to do this without calling the function with the appropriate pointer, but (as Williham Totland suggests) you could use macros to streamline the calls (requires a compiler with variadic macro support):

// macro_call.c

#define C_ARGS(stmnt, func, ...) (stmnt)->ops->func((stmnt)->private, ...)
#define C_NOARGS(stmnt, func) (stmnt)->ops->func((stmnt)->private)

C_ARGS(s, analyse, 1);

C_ARGS(s, lots_of_args, 1, 2, 3, 4);
C_NOARGS(s, no_args);

(The C is for "call".)

Doing the preprocessing on that (via gcc -E macro_call.c) gives:

(s)->ops->analyse((s)->private, 1);

(s)->ops->lots_of_args((s)->private, 1, 2, 3, 4);
(s)->ops->no_args((s)->private);

This is similar to the accessor function version: the macro version is slightly more flexible in some ways, but it is also less safe and could lead to subtle errors and mistakes.

There are two macros because passing no extra arguments to C_ARGS would result in s->ops->func(s->private, ), I think it is possible to fix this, but it is awkward and would require significantly more code (empty __VA_ARGS__ are notoriously hard to deal with).

huon
  • 94,605
  • 21
  • 231
  • 225
  • I had wondered something like this. I often use macros like `#define DBG(fmt, args...) printf(fmt, ##args)` that work fine with no supplied args. – blueshift Apr 23 '12 at 03:46