Crowley Code! 
 (Take 12)

Functional programming in C 2008/03/03

I've been meaning to write this post for some time to document how I avoid the giant-if-statement-with-strcmp() problempattern when writing C programs.

The solution to every problem in C is to use pointers.  Keeping with that lemma, it's possible, beneficial and even fun to make pointers to functions do your bidding without the pile of if or switch statements typical of most student programming projects.  It's really close enough that I started calling it functional programming years ago even though this is still a far cry from Lisp/Python/JavaScript/whatever.  It doesn't matter because C is still awesome.

Use the STL, Luke

I'm cheating a bit when I say functional programming in C.  The use case I'm going to present is a C++ sample but there's no reason why you can't hash your own table in straight C99.  I just don't care to myself.  The example uses the STL map class to map strings to functions, making it trivially easy to build your own shell-like app that takes some argument and dispatches a function to make it so.  The map provides the nice dispatch-y interface but there's no reason you couldn't do something like this in plain C:

void foo(int i) {
    printf("You called foo(%d)!\n", i);
}

void bar(void(*)(int) f) {
    (*f)(2);
}

Anyone fluent in a scripting language that isn't PHP will likely understand exactly what's going on here.  foo is a simple function that does nothing interesting.  bar is a function that takes as its argument a pointer to a function.  That function can't return anything and must take a single integer argument.  Many will call foul here saying that the strong type restrictions here make this sort of "functional programming" rather useless.  I say nay and cite curvr.cc as my example.  In any language, a function lacking any expectations for its arguments will be rather tedious.  C doesn't give you this as a way to shoot yourself in the foot (there are so many other ways, however).

Function pointer declaration

The simplest way I know to explain the difficult syntax of function pointer declaration is an analogy to something easy like a character and a pointer to a character.

char c : char * p :: void foo(int i) : void (*)(int)

The first half declares a character, c and a pointer, p that can point to c.  Similarly, the second half declares a void function foo that takes an integer argument and a pointer to a void function that takes an integer argument.

Now for the useful part

Using this pattern for creating function pointers we can now define a few functions and map them in a useful way.  This is again taken from my curvr.cc file (more info about curvr).

// First a few function prototypes to map
int curve(Magick::Image & img);
int bigcurve(Magick::Image & img);
int anticurve(Magick::Image & img);

// Then the map part
int main(int argc, char * * argv) {
    // Magically figure out a string called process

    // Setup the map using the address operator (&)
    std::map<std::string, int (*)(Magick::Image &)> processes;
    processes["curve"] = &curve;
    processes["bigcurve"] = &bigcurve;
    processes["anticurve"] = &anticurve;

    // Call a function from the map
    (*processes[process])(img);

}

Your code will be shorter now.  Go forth and hack.

Comments (3)

  1. I needed to change

    void bar(void(*)(int) f)

    to

    void bar(void(*f)(int))

    to get your example to compile and work.

    Dotan Dimet — 2008/04/17 3:26 am

  2. Ah, I see it now -[ int (*)(Magick::Image &) ] is a type, [ int(* fp)(Magick::Image&)] is a variable. You need types for the C++ templates, but not for the C function declarations.

    Dotan Dimet — 2008/04/17 3:29 am

  3. You're right with the type vs. variable distinction. Glad you figured it out without me explaining myself more clearly.

    Richard Crowley — 2008/04/17 7:18 am

Richard Crowley?  Kentuckian engineer who cooks and eats in between bicycling and beering.

I blog mostly about programming and databases.  Browse by month or tag.

To blame for...


© 2009 Richard Crowley.  Managed by Bashpress.