A Simply Neat Example of Function Pointers in C
stoneship.org
stoneship.org
The cool thing about this is that, if you want to make your kernel socket 'event-driven', you can actually use this method to overwrite them and 'hijack' the callback. Then, when you're doing doing what you wanted to do, you can punt to the original callback (which you, hopefully, saved)
Example of this taking place: http://lxr.linux.no/linux+v2.6.32/drivers/scsi/iscsi_tcp.c#L...
/* an "active" state where a timer decrements unless the user hits a key. */
if (timer<1) {prep_for_idle(); next = idle;}
else if (user_hit_key) {timer+=100; next = active;}
else {timer--; next = active;}
I use this pattern in haXe and Python, not C, but it applies equally well. int main(void) {
char *sayings[] = {
"I know a secret!", "WHAT", "WHAT again", NULL
};
int i = 0;
for (i = 0; sayings[i]; i++) {
(i ? say_loud : say_soft)(sayings[i]);
}
return 0;
} say = NULL;
say("OH NOES");
When compiled with all warnings on, there are none. When run, it immediately segfaults. (The same would happen with any assignment from void*, like 'say = malloc(42)'. Nice.)Edit: I knew this would get downmodded :) Why does any post implying that type safety is good get downmodded?
Then you'd better not call it NULL. "0 and NULL are the same" was how I first got root on my phone ;)
Use of typedefs make the code more readable.
Instead of: int (* var_pointer_to_function)(int)
one must first do: typedef int (* funcptr)(int)
which is initially equally messy, but then allows one to declare function pointer variables in a simple succinct manner: funcptr foo
If you have a struct, and it has type definition pointers in it, you can create a set of initialization functions that set the function pointers to the correct "class functions" of that type.
I use function pointers in C everyday and it makes coding more robust and extremely easy to get the performance out of OOP code in a procedural environment when C++ isn't an option.
A classic example of the first category is in the VFS layer of BSD UNIX: Filesystems provide a standard set of functions for open/read/write/ioctl/stat/close/etc, which all sit in a structure, and the upper layers of the kernel generally invoke those functions without knowing which underlying filesystem is handling them.
A classic example of the second category is event-driven loops: You register "when there's data on socket N, please call this function with this cookie", and the event loop doesn't need to know what the function does or what sort of data you have stored in the cookie. In this way, you can have several different things going on, passing control back and forth by registering "what happens next" and returning.
A classic example of the third category is qsort: There is no requirement for qsort to know what sort of data it is quicksorting, since it invokes the provided callback function whenever it needs to compare two values.
That makes for a very simple parser.
Then, in the framework layer, we register callbacks using Callables in the IOProcessor. So if you create a TCP server with a TCP listening socket, you have a TCPRead object and you set its onComplete member, which is a Callable, to &TCPServer::OnConnect and analogously for onClose. Then, once a connection is establised you post TCPRead or TCPWrite objects for the new socket to IOProcessor to receive read notifications or perform writes, again with members onComplete and onClose.
Then, in the application layer you have actual database code which uses the network abstractions in the framework layer (which abstract away I/O), but they also use Callables to have themselves get called back when, for example an entire message has been read (eg. command is then parsed and executed) from the TCP stream or a client connection is terminated (eg. commands are canceled).
The whole program is then one big infinite loop, each iteration IOProcessor::Poll() being waken up by the O/S because something has happened, and Callables being called in turn. (On BSD, we kqueue, on Linux epoll and on Win32 Completion Ports to receive readyness notifications.) We also have support timers, which are just a linked list of Callables, and when IOProcessor::Poll() blocks we pass in the amount of time until the next timer must be executed as the timeout.
Or you can use libev/libevent, but they're not lightweight and will force you to do things their way.
[1] AGPL source is at http://scalien.com
http://stoneship.org/journal/2007/c-reference-counting-and-y...
Not how he avoided using & or deref in the first snippet.