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);