Function pointers are very useful if used properly, such as bypassing a ton of detection logic after/at first time initialization/use (ie, lazy/late initialization). A project I am currently working on uses them for pixel format handling routines, if the frame pixel format type suddenly changes, everything is reinitialized and the handler function pointer is changed to the one for the correct type avoiding complex conditionals every single frame of data.
In the instance of using them everywhere, I agree with most people here, use C++, you're likely to cause yourself bugs that are near impossible to track down, such as forgetting to initialize one of the pointers, or memory corruption messing with the pointers making debugging the corruption very hard.
For an atomic/singleton instance of an object the pattern I follow is this:
struct ExampleState
{
int x, y;
};
static struct ExampleState example;
int example_initialize()
{
example.x = 0;
example.y = 10;
return 0;
}
void example_deinit()
{
// do stuff...
}
When multiple instances are needed (ie, like objects) I use the following pattern:
struct ExampleState
{
int x, y;
};
struct ExampleState * example_initialize()
{
// obviously for embedded you may not want to malloc like this.
struct ExampleState * state = (struct ExampleState *)malloc(sizeof(struct ExampleState));
state->x = 0;
state->y = 10;
return state;
}
void example_deinit(struct ExampleState * state)
{
if (!state)
{
fprintf(stderr, "warning: null pointer passed to " __FUNC__);
return;
}
// do stuff
free(state);
}
by prefixing all methods related to the module with the module name it makes your code clear and easy to follow without the need for function pointers everywhere. Really what is cleaner "example->initialize()" or "example_initialize()"? The only thing here that C++ would afford additional is protection (public/private/protected) and extra features such as templates, which for most projects are not needed. Inheritance also, but that can also be done in C without too much trouble with function pointers, the Linux kernel is a perfect example of this, pick any kernel driver to see how they initialize.
Edit:
Actually private protection can be done also by simply not declaring the functions in the header.