Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 787
Bartek Chaber Avatar asked Sep 16 '12 04:09

Bartek Chaber


People also ask

Which function has single arguments?

In mathematics, an argument of a function is a value provided to obtain the function's result. It is also called an independent variable. , is called a unary function. A function of two or more variables is considered to have a domain consisting of ordered pairs or tuples of argument values.

How can you get the type of arguments passed to a function?

There are two ways to pass arguments to a function: by reference or by value. Modifying an argument that's passed by reference is reflected globally, but modifying an argument that's passed by value is reflected only inside the function.

Can a function be an argument in C?

Till now, we have seen that in C programming, we can pass the variables as an argument to a function. We cannot pass the function as an argument to another function. But we can pass the reference of a function as a parameter by using a function pointer.

How do you use an argument in a function?

Arguments are Passed by Value The parameters, in a function call, are the function's arguments. JavaScript arguments are passed by value: The function only gets to know the values, not the argument's locations. If a function changes an argument's value, it does not change the parameter's original value.


2 Answers

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.

like image 198
AusCBloke Avatar answered Oct 03 '22 15:10

AusCBloke


I am not sure whether answering my own question is polite, but I want your opinion.

I tried to solve this problem using va_list. Why so? Because this way I can write only one function. Please, mind that I know what type the argument should be. This way I can do this:

    int find(void* list, ...) {
      any_type object = {0};
      int i = -1;
      va_list args;
      va_start(args, list);
      switch(type_of_elem(list)) {
        case INT: object.i = va_arg(args, int); break;
        ...
      }
      /* now &object is pointer to memory ready for comparision
       * f.eg. using memcmp */
      return i;
    }

The advantage of this solution is that I can wrap presented switch-case and reuse it with other functions.

After researching a little bit more on my concern regarding no limit on number of arguments I realized that printf lacks this limit either. You can write printf("%d", 1, 2, 3). But I tweaked my solution with additional macro:

    #define find_(list, object) find((list), (object))

Which produces error message at compile time, saying that find_ macro expects 2 arguments not 3.

What do you think about it? Do you think this is better solution than previously suggested?

like image 33
Bartek Chaber Avatar answered Oct 03 '22 16:10

Bartek Chaber