Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Understanding function pointers in C unlocks the ability to write clean, object-oriented code with inheritance (kinda, sorta, shhhh).

With great power, etc. etc.



I committed what I now realize were true crimes against software engineering when I first learned of function pointers.


They can't be any worse than abusing the C preprocessor to implement templates for C code (a sin I committed in college).


Sometimes it is a useful sin, many data structure libraries for C use this for:

1) genericity 2) (extreme) speed

It's (unfortunately) impossible for C to equal C++ templates in this: write once, use for multiple datatypes (without macros).

For people who like copy pasting, generic macros is on the horizon for C11, which is rapidly being implemented (and mostly works) for Clang and GCC, the most important compilers. Read more about it here: http://www.robertgamble.net/2012/01/c11-generic-selections.h...

Simply put, it allows to create a small "shim"-macro that detects the type of the input parameters and redirect to an actual function based on type.


What's wrong with that? It allows you to write type-safe sort-of-generic data structures. It's certainly better than 'generic' data structures that work on void*'s.


> Understanding function pointers in C unlocks the ability to write clean, object-oriented code with inheritance (kinda, sorta, shhhh).

People often say this in regards to pointers, or something similar like in the article too, "When understood, function pointers become a powerful tool in the C toolbox.", but often don't explain how/why. In the article the author says that at some indefinite point of time in future they may write about that.

Do you, or anyone, have a link to somewhere not simply explaining the technical side of pointers, but their usage in idealistic and primarily real world examples? Bonus points if it includes indirection, function pointers and other things.

Update: User derefr gave a seemingly great answer below to a similar question like mine.


>> Do you, or anyone, have a link to somewhere not simply explaining the technical side of pointers, but their usage in idealistic and primarily real world examples?

I often use them for parsing json on the fly in an embedded environment with limited ram. Suppose you have a runtime with 4K RAM of which 2K is available, but in comes a string that requires much more space. You can't store the string in RAM, validate it and then use the variables. What I do is parse on the fly and store the variables along with function pointers that need to process them. Once the string has ended and you're sure it's valid and checksummed you process the variables with their function pointers. This technique stretches the length of the message you're able to process, especially for stuff like 32-bit floats that have a larger string than binary representation.


>> "When understood, function pointers become a powerful tool in the C toolbox."

The callback pattern.

I have no idea if I just made that up or if it's a term in use, but it's where I most often find them useful. For instance the interface to libpcap. You can pass libpcap a function to call when it sees network traffic. Without this you would need some sort of polling in your own code. I've seen this in use in a variety of event-driven frameworks written in C.

The other major one I've implemented in the past is thread-pooling with job queues. You can make a queue of jobs that way. Each job is a struct containing a function pointer and a pointer to a struct of arguments. When a thread becomes idle it pulls the job off the queue and calls the function with the given args.

There are probably more. Yes you can kind-of mangle OO out of them, but I prefer the other uses.


Elaborating on the OO bit, as derefr did an excellent job of talking about the usefulness of function pointers in a VM.

So, let's say I have a structure which I will use to represent objects in my game:

  typedef void (*thinkfunc_t)(void* self, unsigned int dt);

  typedef struct BaseFoo {
    float x,y,z;
    thinkfunc_t doThink;
  } BaseFoo;

  void null_think(void* self, unsigned int dt) { return 0; } /* empty think function */

  BaseFoo* Foo_new() {
    BaseFoo* ret;
    ret = calloc(1,sizeof(BaseFoo));
    ret->doThink = null_think;
    return ret;
  }

  BaseFoo* Foo_clone( BaseFoo* foo) {
    BaseFoo* ret;
    ret = Foo_new();
    memmove( ret, foo, sizeof(BaseFoo) );
    return ret;
  }
So, if I want to create a new BaseFoo, I just call Foo_new() and I'm off to the races; I get back a Foo object that, during my update loop, I can fiddle with:

  /* update loop snipped for brevity */
  BaseFoo* currentFoo;
  currentFoo = get_my_foo_from_my_big_foo_list();
  currentFoo->doThink(currentFoo, getFrameTimeInMilliseconds());
So, that's kind of cool, but what if I want to create a new, better foo, that actually moves? I do this:

  void move_think(void* self, unsigned int dt) {
    BaseFoo* foo = (BaseFoo*) self;
    foo->x += dt;
    foo->y += dt;
    foo->z += dt;
    return 0;
  }

  typedef BaseFoo MovingFoo;

  MovingFoo* MovingFoo_new() {
    MovingFoo* ret;
    ret = Foo_new();
    ret->doThink = move_think;
    return ret;
  }
So, this is very bland, but it gets across the idea that function pointers let me easily override member methods on class instances. You can imagine that, with a little cleverness, you can store arbitrary data into the struct, along with a table of function pointers to instance methods (a virtual function table).

This is a very fun, very deep rabbit hole.

In a lot of ways, you basically fake the type of prototypical inheritance you'd expect from, say, JavaScript.

EDIT:

Elaborating on another point--it's very common to use function pointers in a struct that then gets filled out by a dll or shared library.

For example, let's say that I've got a renderer that draws 3D stuff and conforms to an API. At runtime, I check what driver the user wants to use (software renderer, Direct3D, or OpenGL), and then I fill out the function pointers of that structure to point at the architecture and driver-specific methods I'd like to use.

The rest of my code doesn't change, because it only ever calls the interface exposed by the structure--the actual addresses pointed to by the function pointers are irrelevant.


Inheritance can be implemented as well:

    typedef struct AdvancedFoo {
      BaseFoo super;
      int a, b, c;
      thinkfunc_t thinkHarder;
    } AdvancedFoo;
Since `super` is the first member in the struct, a pointer to an AdvancedFoo can be used as a pointer to BaseFoo as well:

    AdvancedFoo *advFoo = AdvancedFoo_new();
    advFoo->thinkHarder(advFoo, advFoo->a);

    ((BaseFoo*) advFoo)->doThink(advFoo, some_int_method());
Of course, this inheritance pattern can continue in multiple steps.


I don't see the need to do the "ret->doThink = null_think;" since you always pass ret to the function. Just call null_think( ret... )


Ah, but what if the object is handed over to some other deep end of the code? We want to bake the null behavior into it so that any Foo knows how to think itself--calling code just needs to be aware that any Foo object will always have a valid think callback defined.

This is basically the null-object pattern.


You mean as in

    func(foot);
    /* Should check func != gun.shoot */




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

Search: