Hacker News new | past | comments | ask | show | jobs | submit login
A Simply Neat Example of Function Pointers in C (stoneship.org)
31 points by ilovecomputers on Jan 16, 2010 | hide | past | favorite | 22 comments



Example of function pointers in the linux kernel: http://lxr.linux.no/linux+v2.6.32/include/net/sock.h#L297

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...


Function pointers are a good means of allowing "flexible" finite state patterns, by making each function a state, directing all program flow to a "next" function pointer, and using conditionals within each state to begin transitions:

  /* 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.


What do you compile your haXe to?


Flash 10, mostly. Sometimes Neko.


What is neko used for mostly?


That's a pretty design, but a rather typical function pointer demonstration. It's at least more unusual if you change main to this:

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


I modified the program so that the main function looks like:

  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?


Maybe because people know by now that C doesn't hold your hand :). The compiler did exactly what you told it to do. And, in fact, since C is frequently used for writing operating system code, what you wrote may actually be a valid operation if the OS maps critical functions into page 0 (which I believe some older systems did).


what you wrote may actually be a valid operation if the OS maps critical functions into page 0

Then you'd better not call it NULL. "0 and NULL are the same" was how I first got root on my phone ;)


In C++ 0 and NULL are the same by definition. Just look at Stroustrup's C++ Style and Technique FAQ.


Assigning the value 0 or NULL to a pointer does not necessarily set it to address 0.


A common use of function pointers is in a dispatch table. For example, in a text editor, every ASCII character value, including control-characters and printable characters, is used as an index into an array of function pointers, each one of whom represents a function. For example the ^N character retrieves a function pointer for the moveToNextLine() function. The printable characters all are insertSelf() function.

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


More importantly, function pointers can be used (especially in type definitions) as a way for polymorphism and object oriented code in C.

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.


I think it's safe to say that most uses of function pointers in C fall into one of two categories: 1. Objects; 2. Continuations; 3. Callbacks from generic algorithms.

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.


isn't your event-driven loop an example of a callback function? i'm no expert on continuations, but i thought they involved somehow packaging up the 'rest of the computation' in some structure and then running it. your example seems to be an ordinary callback.


He did say there were only two categories. They're just numbered 1 through 3 for some reason ;-)


A continuation is really just a type of callback -- "go to point X in the code, with state Y".


Another favorite is simple embedded languages where every statement or function has an entry in a table with a pointer to the function for execution.

That makes for a very simple parser.


Real world usage: an event-based architecture like the one employed in Keyspace [1]. We have an abstract class Callable, which has a member virtual void Execute() and a global convenience function Call(Callable) which just calls ->Execute(). So a Callable is something that can be called, ie. a function pointer. We then have two concrete implementations, one for global (C) function pointers and one for class member (C++) function pointers. The whole thing is in Callable.h and is <100 lines of code, hence very lightweight.

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


The same site has an interesting example of reference counting:

http://stoneship.org/journal/2007/c-reference-counting-and-y...


welcome to 1988


I'd like to congratulate you on your C function pointer hello world.

Not how he avoided using & or deref in the first snippet.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: