问题
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 offind
. 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 writeint 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?
回答1:
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.
回答2:
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?
来源:https://stackoverflow.com/questions/12444385/how-to-write-c-function-accepting-one-argument-of-any-type