You can have lots of fun with the above, assuming I'm interpreting your post correctly. I did this recently while writing a task scheduler for a project that will probably never get done, which uses the same concept:
Like in C++, you could use singletons, but I don't think there are too many benefits to attemting to mimic this bahavior in C. An example of using global data, as I said above, is as follows.
Basicly, the header file for the above only shows how to schedule a task,
static TaskList *TaskHandler( TaskInstance **t, Task_h command )
static TaskList *tasks[ TaskB_MaxSlices ]; /* The tasks. */
switch( command )
/* These are purposefully hidden functions. */
static int TaskEnqueue( TaskList *x, TaskInstance *t );
TaskEnqueue( tasks, *t );
/* Comment for board readers: This is in effect the 'interface'. */
int TaskSchedule( TaskInstance *t )
TaskHandler( &t, TaskH_Enqueue );
/* Comment for board readers: This would be considered 'implementation'. */
static int TaskEnqueue( TaskList *tasks, TaskInstance *t )
TaskList **slice = NULL;
complete a task, and cleanup. The rest isn't listed, because the user doesn't
need to know about them. They're purposefully not prototyped, except for in
the handler function, and they're defined after said handler. The actual data
for the table is static inside the hander, and the only way to do anything with
that data, is by using the interface provided to you.
Everything is contained in the handler function. There is no other way to get
at it. At least that's the idea behind it. The reason for the arguments to the
handler, and the return value from it, are so I can:
a) update a pointer if I need to.
b) return a task if I need to.
c) use the return value for error state.
Anyway, that's more than what you're interested in, and is probably off topic
now, but it's rather fun. I'm actually probably going to end up modifying the
handler so it can handle additional task sets, but that's neither here nor there.