2

Main use of function pointers (from what I gather) is to pass them along with some variables to a function which will then call the function the pointer points to. To achieve similar results one can pass an arbitrary integer instead of a function pointer and let a switch case call the appropriate function; which will also bypass the restriction that is innate to function pointers, that a function you are passing the pointer to, needs to know exactly what kind of function is coming to it (what it returns and what variables it expects.)

Is there an advantage to using function pointers over the switch case method proposed above? I'm interested in technical capabilities of function pointers that I might be missing and preferably some examples too.

MLquest
  • 19
  • 6
  • 2
    You mean other than the obvious one of having created a maintenance nightmare with the `switch` statement? Calling any function requires that you adhere to the function's contract; function pointers aren't any different in that regard. – Robert Harvey Mar 25 '21 at 14:07
  • What about third-party API's which doesn't know anything about your "arbitrary integer"? – Some programmer dude Mar 25 '21 at 14:08
  • 1
    Traditionally, function-pointer tables were faster than switch. Nowadays it shouldn't matter. In fact function pointers might slow things down a tiny bit since they block inlining. – Lundin Mar 25 '21 at 14:16
  • @Someprogrammerdude I obviously didn't mean it in relationship with third party APIs etc... I meant on a small scale – MLquest Mar 25 '21 at 14:21
  • 1
    See my recent answers: https://stackoverflow.com/questions/65621027/writing-a-generic-struct-print-method-in-c/65621483#65621483 and https://stackoverflow.com/questions/66176690/static-lookup-table-in-c-for-enum-with-assigned-values/66178756#66178756 – Craig Estey Mar 25 '21 at 14:59
  • 1
    `qsort(..., bydate)`; `qsort(..., bycountry)`; `qsort(..., byheight);`; ... the C library writers had no idea what function you would chose, they simply couldn't have done that with `switch` – pmg Mar 25 '21 at 15:21
  • 1
    Traditionally function pointer table (or table of labels) is the mechanism how compilers implement a (fast) switch case. – Aki Suihkonen Mar 25 '21 at 17:35

3 Answers3

1

If the functions each have different signatures, you'll need to have a separate case for each one in order to call the right parameters and get the right return type.

If the signatures are the same, you could instead create an array of function pointers and just index the array. This avoids branches and is simpler to maintain. For example:

int add(int, int);
int sub(int, int);

typedef int (*math_func)(int, int);

math_func arr[] = { add, sub };

int call_math_func(int idx, int a, int b)
{
    return math_func[idx](a, b);
}
dbush
  • 205,898
  • 23
  • 218
  • 273
0

I prefer using function pointers when possible because imho they make intents more explicit at the calling site, e.g.:

#include <stdio.h>

typedef void (*HelloWorld)(void);

void english(void) { printf("Hello, World!\n"); }
void italian(void) { printf("Ciao, Mondo!\n"); }

void greet_world_in(HelloWorld hello_world) { hello_world(); }

int main(void)
{
    greet_world_in(english);
    greet_world_in(italian);

    return 0;
}
Jack Lilhammers
  • 1,207
  • 7
  • 19
0

Assuming that the function pointers have exactly the same kind of prototype, a switch case statement is often implemented as a jump table:

void conceptual_dispatcher_emulating_switch_case(int n) {
   static const jmp_ptr_t jmp_table[5]={label_0,label_1,label_2,label_3,label_4};
  
   if (n < 5) {
      goto jmp_table[n];
   }
   return;

   label_0 :  return myfunc_1();
   label_1 :  return myfunc_2();
   label_2 :  return myfunc_3();
   label_3 :  return myfunc_4();
   label_4 :  return myfunc_5();
}

From this it's pretty easy to make an optimization:

void conceptual_dispatcher_better(int n) {
   static const function_ptr_t jmp_table[5]={myfunc_1, myfunc_2, myfunc_3, myfunc_4, myfunc_5};
  
   if (n < 5) {
      goto jmp_table[n];
   }
   return;
}

And from this the next logical step is to

void conceptual_dispatcher_even_better(function_ptr_t *ptr) {
   ptr();
}

And from this the next step is to make that inline or just call the ptr without the dispatcher.

Aki Suihkonen
  • 19,144
  • 1
  • 36
  • 57