Is there a way to pass a function pointer using generic arguments?

I implement a generic list, linked separately, in which list nodes store a pointer to their data.

typedef struct sll_node { void *data; struct sll_node *next; } sll_node; 

To implement a universal find routine that works with any data, I wrote it so that it uses a function pointer for the comparison function as an argument, as follows:

 /* eq() must take 2 arguments. ex: strcmp(char *, char *) */ sll_node *sll_find(void *data, int (*eq)(), sll_node *root); 

You can pass the corresponding function pointer that works with the data type at hand. Therefore, if you store strings in list nodes, you can pass strcmp as an eq () function, and so on. It works, but I'm still not satisfied.

Is there a way to explicitly indicate the number of parameters of the comparison function without giving up its generality?

I tried this first:

 sll_node *sll_find(void *data, int (*eq)(void *, void *), sll_node *root); 

I expected this to work. But no (editing: compiled with a warning, but I have -Werror on!), I had to write a wrapper function around strcmp to match the eq prototype.

Then I tried:

 sll_node *sll_find(void *data, int (*eq)(a, b), sll_node *root); 

or

 typedef int (*equality_fn)(a, b); sll_node *sll_find(void *data, equality_fn eq, sll_node *root); 

which both will not compile since: "the list of parameters without types is allowed only in the definition of the function"

+6
source share
3 answers

To use strcmp without a shell or cast, the declaration must be

 sll_node *findNode(void *data, int (*eq)(const char *, const char *), sll_node *root); 

On the other hand, if you declare args as const void * , you can avoid the wrapper by pointing strcmp to the appropriate type.

Method 1: direct cast, dirty, but effective

  result = findNode( "hello", (int(*)(const void *, const void *))strcmp, root ); 

Method 2: print a comparison function and then use it to create

 typedef int (*cmpfunc)(const void *, const void *); result = findNode( "world", (cmpfunc)strcmp, root ); 

Edit: After reading this post related to @WilburVandrsmith, I decided to leave this answer as it is. I leave this to the reader to decide if the proposed composition violates the following paragraph from the specification:

If the converted pointer is used to call a function whose type is not compatible with the specified type, the behavior is undefined.

Compatible or incompatible, this is a question that you decide.

+2
source

Your last decision was the closest to the right one. Parameters in your function pointer of a certain type must be declared with their data types, as well as with a regular function declaration, for example:

 typedef int (*equality_fn)(char *a, char *b); sll_node *sll_find(void *data, equality_fn eq, sll_node *root); 

UPDATE

To make it more universal, use void pointers, and then cast the passed-in void pointers to the desired data type in the definition of the matching function for equality_fn :

 typedef int (*equality_fn)(void *a, void *b); sll_node *sll_find(void *data, equality_fn eq, sll_node *root); 

Something else is important to remember, because a pointer - this is a pointer - is a pointer , regardless of what it points to or how it was originally defined. Thus, you can have a function pointer or a pointer to void or a pointer to a byte, char, int - whatever - if you correctly process it in your code and return it to the actual one before trying to use it.

Something else that most coders do not take great advantage of in C is that the function names themselves are simply addresses that are called at runtime, and therefore they are also pointers .;)

0
source

My solution to this puzzle would be (avoiding, for example, typedefs of a pointer):

 typedef int equality_fn(const void *a, const void *b); sll_node *sll_find(void *data, equality_fn *eq, sll_node *root); 

Then make all your comparators type equality_fn . If you really need to have a function, let it be so:

 equality_fn eq_strcmp; // a prototype // ... int eq_strcmp(const void *a, const void *b) { return strcmp(a, b); } 

Get a lot of type safety in exchange for potential picosopic execution time - which end of this deal you want to use depends on your application.

0
source

Source: https://habr.com/ru/post/982406/


All Articles