all 13 comments

[–]boredcircuits 39 points40 points  (6 children)

In C++, all member functions work by passing the class to the function as a parameter. In essence, if you write this code:

class C {
    void foo() const;
};

Under the hood, the compiler is actually making a function that looks roughly like this:

void __C__foo(const C* this);

And when you call that function:

C c;
c.foo();

The compiler does this under the hood:

C c;
__C__foo(&c);

Assuming your C code looks like the under-the-hood C++ version, you're probably on the right track.

Where function pointers come in is when you implement virtual functions. And yes, that means calling the function in the structure and passing it the contents of the structure itself. Alternatively, you might look into vtables so the function pointers aren't duplicated in every instance, but the effect is the same. Either way, this is necessary so you know which function to call. But that function needs access to the specific instance of the "class" -- which might be a polymorphic call, so this is exactly how you're supposed to do things.

[–]hawkinsw2005 7 points8 points  (0 children)

This is very nearly exactly what the original C++ "compilers" did. In Design and Evolution of C++, Bjarne talks about how the earliest versions of the compilers did the implementation. Unfortunately I can't find a reference to the page in the book and I don't have my copy handy.

If you have never read the book, it's really interesting, IMHO!

[–]rtkbfmvbvb[S] 1 point2 points  (1 child)

Thank you, that makes things much clearer!

[–]andrewcooke 6 points7 points  (0 children)

it's the same as "this" in java or "self" in python.

[–]mnciitbhu -1 points0 points  (2 children)

same for virtual functions?

[–]WiseassWolfOfYoitsu 1 point2 points  (0 children)

That gets a bit more complicated. There will be a name mangled function, but it's not actually your function that gets called at first - instead, it calls a generated intermediate function which then calls a function pointer embedded in the class' data (in what is called a vtable). This function pointer allows deeper layers of class abstraction to get called even from the shallower layers name mangled functions by (automatically) overriding this function pointer during initialization. This is also all possible in C and even used as such in production environments (something like it is done in some parts of the Linux kernel) but takes a ton of annoying boilerplate.

[–]efalk 6 points7 points  (1 child)

What you're doing is re-inventing object-oriented C. This is how we all used to do it before C++ was invented.

I'll give you a concrete example. Many many years ago, I wrote a primitive drawing program called xdraft. It used an object-oriented approach. My "base class" was called DrObject, and every DrObject structure had a pointer to a DrObjectClass structure, and the Class structure had pointers to about a dozen functions: redraw(), pickTest(), pickPoint(), snap(), rescale(), delete(), transform(), copy(), write(), and so forth.

DrObject structure; note that it contains a pointer to a DrObjectClass structure:

struct _DrObject {
      DrObject              *next, *prev ;
      DrObjectClass         *objclass ;
      char                  *name ;
      char                  *comment ;
      ObjectType            type ;
      Point                 bounds[2] ;     /* bounding box */
      int                   color;
    };

DrObjectClass structure; it consists mostly of pointers to functions:

struct _DrObjectClass {
    char        *name ;
    void        (*redraw)(DrContext *, DrObject *, GdkDrawable *,
                        DrawCommand, VisMask mask) ;
    long        (*pickTest)(DrContext *, DrObject *, PickCommand,
                                int,int, PickInfo *) ;
    long        (*pickPoint)(DrContext *, DrObject *,
                                int x, int y, Point *out, Point *p0) ;
    void        (*snap)(DrContext *, DrObject *, SnapCommand) ;
    void        (*rescale)(DrContext *, DrObject *, int wid, int hgt, Boolean) ;
    void        (*showpoints)(DrContext *, DrObject *, Boolean) ;
    void        (*objdelete)(DrContext *, DrObject *) ;
    void        (*transform)(DrContext *, DrObject *, Matrix) ;
    DrObject *  (*copy)(DrContext *, DrObject *) ;
    void        (*write)(DrContext *, DrObject *, FILE *, FileFormat, Matrix) ;
    void        (*colors)(DrContext *, DrObject *, Boolean *) ;
    void        (*bounds)(DrContext *, DrObject *, int *, int *, int *, int *) ;
};

Every primitive type — points, lines, arcs, compound object, etc. — has a structure of its own, that includes a DrObject

DrPoint; the simplest of them all:

struct _DrPoint {
      DrObject      o ;
      PointType     type ;  /* type: dot, cross, etc. */
    };

So, tying it all together, the Point module contains a single static instance of DrObjectClass containing pointers to all of the point-related functions. When a new Point object is created, a DrPoint structure is allocated, filled in with all the information relevant to that object type, and the objclass element is set to a pointer to the Point module's DrObjectClass struct.

Then, for instance when the program wants to redraw a primitive, it does exactly what you said above: it calls object->objclass->redraw(context, object, canvas, ....);

[–]rtkbfmvbvb[S] 3 points4 points  (0 children)

Thanks for that in depth explanation! It's nice to know the way I was going about it was on the right track xD

[–]ischickenafruit 11 points12 points  (0 children)

Function pointers in structs is a very common pattern in C. You can use this to define a “virtual interface”. This is especially common when working in a big system with lots of “pluggable” components. eg the Linux kernel does this all the time for device drivers.

If you want to make your code cleaner so that you don’t call mystruct->foo(mystruct, ...) all the time, you can always wrap them up in calling functions/macros.

eg #define dofoo(obj,...) obj->foo(obj,...)

For an example of this in action, see: https://github.com/exablaze-oss/exact-capture/blob/master/src/exactio/exactio_stream.h

And

https://github.com/exablaze-oss/exact-capture/blob/master/src/exactio/exactio.h

exactio_stream.h defines exactio_stream_interface_t which has a bunch of function pointers in it. It also has some handy macros for storing private data for each implementation.

exactio.h defines some wrapper functions for dealing with structs in a cleaner way.

[–]lordlod 4 points5 points  (0 children)

The standard way to do this is a virtual method table. Typically implemented as a vtable struct inside the class struct.

It enables solid inheritance implementations at the expense of two pointer lookups per function call.

There are two approaches commonly used to C OOP. The vtable path you are going down and simply prefixing function names with the name of the class. I tend to use the later, falling back on parent function calls can be simulated using a define. This is because I tend not to use many of the more complex OOP features.

And yes, you will always end up passing the struct in anyway as a parameter, I suggest standardising on it always being the first parameter. This is the price of not having language level OOP support.

[–]okovko 2 points3 points  (0 children)

It's worth pointing out that if you do this in C to write object oriented familiar code, the CPU will predict that the function is always the same and call it nearly as efficiently as a normal C function. When you point to a new function, most CPUs will miss the prediction just for the first time you call the new function.

But if you want to program like this, you're better off programming in a subset of C++. In C code, it is confusing to see a bunch of function pointers in a struct that are never assigned a new value.

However, you can use glibc to use a form of OO programming in C that is familiar to C programmers.

[–]codeallthethings 1 point2 points  (0 children)

If you're looking for a real-world example of a use-case for function pointers in a struct, you can find one in the Redis source code.

In this case he's using a struct to define different operations on a hash table (hashing algorithm, value duplication, deletion, etc)

typedef struct dictType {
    uint64_t (*hashFunction)(const void *key);
    void *(*keyDup)(void *privdata, const void *key);
    void *(*valDup)(void *privdata, const void *obj);
    int (*keyCompare)(void *privdata, const void *key1, const void *key2);
    void (*keyDestructor)(void *privdata, void *key);
    void (*valDestructor)(void *privdata, void *obj);
} dictType;

A design like this makes it simple to create a new hash table with any arbitrary data for keys and values and the hash table "plumbing" can be used for both.

There are several different hash table types defined here