11

How do I declare a function pointer that can safely point to any function?

I need something like void pointers, but for functions. I thought about simply using that but, according to the answers to this question, function pointers cannot be reliably converted to data pointers, at least on non-POSIX systems.

I searched and found some possible examples of this:

  • Windows has FARPROC:

    int (FAR WINAPI * FARPROC) ()
    

    The documentation also mentions this:

    In C, the FARPROC declaration indicates a callback function that has an unspecified parameter list.

    Sounds fine, but what about the return value? It is clearly specified as int.

  • One of OpenGL's specifications has:

    typedef void (*GLfunction)();
    extern GLfunction glXGetProcAddressARB(const GLubyte *procName);
    

    The parameter list is also unspecified, but the return type is clearly specified as void.

I'm not sure how to interpret this. The function pointer will be cast to a function pointer type with the appropriate prototype before it is used. For example:

typedef void (*function_pointer_t)();
extern function_pointer_t get_function(const char * name);

/* Somewhere else... */

typedef double (*cosine_function_t)(double);
cosine_function_t cosine = (cosine_function_t) get_function("cos");

Is this code safe? Does it violate any C standard or result in any undefined behavior?

Community
  • 1
  • 1
Matheus Moreira
  • 17,106
  • 3
  • 68
  • 107
  • you still need FAR ???? its history... – perilbrain Oct 06 '12 at 19:25
  • 1
    @perilbrain, I've heard of near and far pointers, but I'm not sure what it means. I copied that declaration from the documentation. – Matheus Moreira Oct 06 '12 at 19:26
  • 1
    it was the time when processor cried for bus width,and the segmentation was introduced, which gave access to a code in its local segment using NEAR and in other segment using FAR – perilbrain Oct 06 '12 at 19:31
  • There is yet another wrinkle to this problem: a 'normal' C/C++ function pointer to a class/struct instance's function has a different signature than a function pointer to a non-instance function, even if the actual function signatures 'appear' equivalent (due largely to the fact that under the hood, the 'this' pointer is 'part' of the function pointer). Honestly, I'm not sure you can do what you want to do with 'normal' C/C++ function pointers; and it will take some jumping through hoops to roll-your-own standards compliant approach. Is using C++ boost (any,bind,etc) an option? – WeirdlyCheezy Oct 06 '12 at 19:33
  • @WeirdlyCheezy, I'm using C, so I believe boost is not an option. – Matheus Moreira Oct 06 '12 at 19:40
  • 4
    @WeirdlyCheezy 'C/C++' -- There is no such language. This is a question about C, which doesn't have classes or "instances", so your comment doesn't apply. – Jim Balter Oct 06 '12 at 19:43
  • 1
    @Jim Balter You're absolutely correct. For some reason I brain-farted and thought C structs supported instanced function calls; I've been away from strict C too long apparently. – WeirdlyCheezy Oct 06 '12 at 20:37

2 Answers2

13

Firstly, there's no way to declare a function pointer that would be able to accept any function pointer type without a cast. The problem, as you already noted yourself, is that any function pointer declaration immediately assumes a specific return type. So, there's no such thing as full analogue of void * in function pointer world.

Secondly, any function pointer can be used to store any other function pointer value as long as you force the conversion by using an explicit cast. Of course, in order to perform a proper call through a pointer forcefully converted to a different type you need to convert it back to the original type. I.e. this does not cause any undefined behavior as long as the proper function pointer type is restored at the moment of the call.

In your example if the pointer returned by get_function really points to a function of double (double) type, then it is perfectly safe to call that function through cosine pointer. The fact that the pointer value was stored intermediately in a void (*)() pointer does not destroy it.

Thirdly, in C language a () parameter declaration stands for unspecified number and types of parameters. This is a step in the direction of the "universal" function pointer type that can be used without a cast (as long as you supply proper parameters in the call and as long as the return type matches)

void foo(void);
void bar(int i);
void baz(long i, double x);

int main() {
  void (*a[3])() = { foo, bar, baz };
  a[0]();
  a[1](42);
  a[2](5L, 3.1415);
}

But, again, the problem of return type still remains.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 'This is a step in the direction' -- No, it's an obsolescent feature: "The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature." – Jim Balter Oct 06 '12 at 19:54
  • @Jim Balter: I would see non-prototype *function declarations* as an obsolescent feature, i.e. when it is used to *introduce* functions themselves (as opposed to function *pointer* declarations). But when used in function pointers declarations I'd see it as a formally useful feature, although I can't remember ever using it myself. – AnT stands with Russia Oct 06 '12 at 21:12
  • I quoted the standard; it refers to function *declarators*. "the direction" of C is toward stricter type conformity. – Jim Balter Oct 06 '12 at 21:20
  • @Jim Balter: I know. And I said "I would see...", implying that this is just my opinion. – AnT stands with Russia Oct 07 '12 at 08:13
  • The `void foo();` triggers the `-Wstrict-prototypes` warning in gcc: `void foo(); `. And explicit casts between function-types -- even when the return type is the same -- triggers the `-Wcast-function-type`: `cast between incompatible function types`. A function equivalent of `void *` remains elusive... – Mikhail T. Feb 22 '21 at 15:46
  • @Mikhail T.: I should've used `void foo(void);`. That was unintentional. I corrected the code. – AnT stands with Russia Feb 22 '21 at 18:27
  • The cast between function-types is still a problem. Tough clang does not mind, gcc-8.x flags it with `-Wcast-function-type`. – Mikhail T. Feb 22 '21 at 18:55
4

You can use a union. There are surely only a finite number of function types that you call via this pointer, and you can put them all in the union.

Jim Balter
  • 16,163
  • 3
  • 43
  • 66