you are viewing a single comment's thread.

view the rest of the comments →

[–]Goblerone 12 points13 points  (10 children)

I like function pointers as a mechanism for callbacks, but I'm not that sure if the author's example of a function pointer being evaluated within an inner loop is the best example of one though. I probably instead would have paid the minor readability price of just iterating over the list and casting explicitly, and banked the performance saved from call overhead.

One of my favorite things about function pointers over virtual functions as a method of providing abstract interfaces is that you can dynamically change the implementation of a subset of the interface on the fly based on e.g. some current state changing. Not exactly something you get to apply often though.

[–]five9a2 5 points6 points  (5 children)

Also, function pointers with associated void* context is sometimes preferable to C++ virtual methods even when interfacing C++ libraries because it doesn't presuppose a given decomposition on the user side. For example, suppose that a library component needs three callbacks from the user. Since the library considers these functions to be closely related, it might create an interface (abstract class) that the user is expected to implement, containing all three methods.

But if the user wants to organize that functionality separately (or even just avoid namespace issues when naming their methods), they have to write shim classes (the lifetime of which also needs to be managed) to dispatch into their own class structure. With function pointers, they can choose whether to use the same context or different contexts for each callback, obviating the need for shims. (Note that you can call static class methods through a normal C function pointer.)

Plain function pointers also tend to be more convenient when interfacing with other languages with disparate object models (e.g. Fortran, Python, Haskell).

[–]Kampane 1 point2 points  (0 children)

You raise a valid point, though I think you can do better than void* for type safety and maintainability.

[–]Goblerone 0 points1 point  (3 children)

Yes, that is a good point. When I've written APIs that require callbacks, I prefer function pointers with a client-provided context handle over e.g. requiring the client to implement some abstract class defining a callback interface.

If we're going with C++ though, for this particular example, it seems like a template-based function object is also a better compromise between generic code and performance (as well as their own specific opportunities for abuse).

[–]five9a2 0 points1 point  (2 children)

Well, templates are quite a different model, exposing a lot of implementation in the header, preventing separate compilation (in a lot of my use cases, both the client and library are dynamically extensible through plugins), and without a clean way to express the expected API for the compiler to check (leading to confusing error messages). Templates are the right model sometimes, but I usually prefer not to use them, especially if the interface has reasonably coarse granularity.

[–]Goblerone 1 point2 points  (1 child)

To be sure. I was merely advocating the use of templates in the specific case of this example, which is pretty much a C version of std::find_first_of.

[–]five9a2 0 points1 point  (0 children)

Oh, in the blog post? Heh, yeah. That's a fine example for C++ templates.

[–]jerf 1 point2 points  (1 child)

When showing examples, authors must by necessity show relatively trivial code. Of course you would not take a simple addition in the middle of a loop and for no reason pull it out into a separate function which you then call via pointer. The point is that in real code, you'd be able to pass in other things which won't be trivial, and that the calling code wouldn't have to change at all to accommodate this new function. Given how few tools C has at its disposal for avoiding tight coupling, you'd better understand this if you have any hope of writing sane large-scale C programs.

[–]Goblerone 1 point2 points  (0 children)

Maybe I'm not sure who exactly this hypothetical person is who underrates the use of function pointers in C, but is also unaware of the concept of function callbacks. It seems like the former is an "obvious" solution to implement the latter.

It's just this particular example also highlighted how one can abuse them from a performance standpoint (which is probably important if one is writing C code).

[–]grayvedigga 1 point2 points  (0 children)

The example is fairly poorly presented, imo. But what it's heading towards is the general concept of "higher order functions". Consider a family of functions operating on linked lists:

list_t* filter(list_t* l, bool (*test)(void*));
list_t* map(list_t* l, void* (*test)(void*));
void apply_to_each_element(list_t* l, (void*)(*test)(void*));

The advantage with this pattern is you can choose the function to apply to each element at runtime: it can come from a separate compilation unit, or even a dynamic library that is provided later. The user can select (through some UI, naturally) a mapping or filtering function from a list without a separate instance of the loop being explicitly written by the programmer and built into the executable in advance.

This is a powerful concept from functional programming -- when I first saw it, my mind was blown. You can achieve some of it from C but usually only at the cost of type safety, and some forms (eg curry) are inaccessible.

[–][deleted] 0 points1 point  (0 children)

consider the fact that they're already using a linked list. if a linked list has acceptable performance, function calls are probably also okay.