How to write C function accepting (one) argument of any type

Bartek Chaber picture Bartek Chaber · Sep 16, 2012 · Viewed 10.2k times · Source

I am implementing simple library for lists in C, and I have a problem with writing find function.

I would like my function to accept any type of argument to find, both: find(my_list, 3) and find(my_list, my_int_var_to_find). I already have information what is type of list's elements.

For now I've found couple of ways dealing with this:

  • different function with suffix for different types: int findi(void* list, int i), int findd(void* list, double d) - but I don't like this approach, it seems like redundancy for me and an API is confusing.

  • using union:

    typedef union {
       int i;
       double d;
       char c;
       ...
    } any_type;
    

    but this way I force user to both know about any_type union, and to create it before invocation of find. I would like to avoid that.

  • using variadic function: int find(void* list, ...). I like this approach. However, I am concerned about no restrictions on number of arguments. User is free to write int x = find(list, 1, 2.0, 'c') although I don't know what it should mean.

I have seen also answer to this question: C : send different structures for one function argument but it's irrelevant, because I want to accept non-pointer arguments.

What is the proper way of handling this function?

Answer

AusCBloke picture AusCBloke · Sep 16, 2012

You could instead try implementing your function similar to a generic function like bsearch, which can perform a binary search on an array of any data type:

void *bsearch(const void *key, const void *base, size_t nmemb, size_t size,
              int (*compar)(const void *, const void *))

Rather than hard-coding the different implementations for different data types inside your function, you instead pass a pointer to a function which will do the type-dependent operation, and only it knows the underlying implementation. In your case, that could be some sort of traversal/iteration function.

The other thing bsearch needs to know (apart from the obvious - search key and array length) is the size of each element in the array, so that it can calculate the address of each element in the array and pass it to the comparison function.


If you had a finite list of types that were to be operated on, there's nothing wrong with having a family of findX() functions. The above method requires a function for each data type to be passed to the bsearch function, however one of the main differences is that common functionality doesn't need to be repeated and the generic function can be used for any data type.

I wouldn't really say there's any proper way to do this, it's up to you and really depends on the problem you're trying to solve.