Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I am implementing a generic singly linked list where list nodes store a pointer to their data.

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

To implement a generic find subroutine that works with any kind of data, I wrote it so that it takes as an argument a function pointer to the comparison function 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 appropriate function pointer that works with the data type at hand.. So if you store strings in the list nodes, you can pass strcmp as the eq() function, and so on. It works but I'm still not satisfied..

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

I tried this at first:

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

I expected it to work. But no (edit: it compiles with a warning but I have -Werror on!), I had to write a wrapper function around strcmp to make it conform to the eq prototype.

I then 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 wouldn't compile since: "a parameter list without types is only allowed in a function definition"

like image 655
Osama Arafa Avatar asked Feb 12 '15 23:02

Osama Arafa


1 Answers

To use strcmp without a wrapper or a cast, the declaration needs to be

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

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

Method 1: direct cast, messy but effective

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

Method 2: typedef the comparison function, and then use it to cast

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

Edit: After reading this post that @WilburVandrsmith linked, I've decided to leave this answer as is. I leave it up to the reader to decide whether the proposed cast violates the following paragraph from the specification:

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

Compatible or not compatible, that is the question, you decide.

like image 52
user3386109 Avatar answered Oct 19 '22 22:10

user3386109