tags:

views:

292

answers:

8

How can I write a function which accepts a parameter of a generic type in C? (such as an int, a char...)

A: 

What you are looking for is called a variadic function. Linked below is a Wikipedia article on the topic:

http://en.wikipedia.org/wiki/Variadic_function#Variadic_functions_in_C.2C_Objective-C.2C_C.2B.2B.2C_and_D

John Scipione
+1, beat me by 5 seconds.
Stephan202
Variadic functions take an arbitrary number of arguments, while the original question was about taking a parameter of generic type. While variadic functions can do that you can solve the issue without variadic functions if the number of arguments is known.
bluebrother
Even with a variadic function, you need information about type when using the va_next() macro to advance across the variable arguments. As an example printf()-family embeds the type information into the format string.
Heath Hunnicutt
+5  A: 

I'd personally do it like this:

1) send a pointer to void * as the first parameter

2) send a second parameter which tells you what the void * is (an enum for the possibilities) and cast parameter 1 to that

This would make you write ugly code with lots of switches, but might work if done carefully and thoroughly tested.

Something like:

// the enum:
BYTE_VALUE = 1; INT_VALUE = 2, CHAR_VALUE = 3 etc

// the function
int parse(void *arg, enum_type arg_type)
{
    if (arg == NULL) return -1;

    switch(arg_type)
    {
    case BYTE_VALUE:
        byte value = (byte) *arg;
        // do work here
    case INT_VALUE:
     // etc
    }

    return something;
}

Edit: that is assuming you don't want variadic functions (which did not seem to me were what you wanted)

laura
Yes, i wanted a function like this. This is a nice implementation but i'd like to see one more concise (if there's anyone). Thank you.
En_t8
@pmg true, I'll fix it
laura
Don't forget to put a 'break;' at the end of each case.
eyalm
even better if you use an union instead of doing all the casting
fortran
And for the love of all things holy, write macros to call the function for you instead of including the type enum in every usage. This will insulate your code from changes in the enum and calling convention, and make it harder to shoot yourself in the foot by doing this: double d = 0.5; parse(
plinth
I think that you might find that `byte value = (byte) *arg;` needs to be `byte value = *((byte *) arg);`
Tim
laura, please comment on my answer since you were the one who came up with the brunt of the idea.
San Jacinto
+2  A: 
Secure
A: 

Using a void* is the usual way. Check for example the implementation of memcpy which does exactly this -- it accepts an argument of any type. You should be aware that you need to know the original type (i.e. typecast it to the type the data originally had) if you want to process the data passed "properly" (unless you don't do real processing like memcpy and similar functions) as passing a void* doesn't tell anything about the content -- it's just a pointer to some memory, without any information about its content, length or interpretation. Thus, your function needs to handle that by itself, probably with a different parameter that tells about the type. memcpy, for example, requires a parameter passing the length of the data in bytes.

bluebrother
A: 

Your best bet is to use templating. You can use void pointers but that can lead to other problems if done incorrectly. Best thing to do is go HERE read the tutorial and try it out for yourself. You shouldn't have any problem understanding the concept of templates.

ChadNC
Wrong language.
Chuck
Topic says C. Did he mean C#? If so, oops
ChadNC
Topic says C, your answer is C++.
Chuck
haha another minus one. I haven't been here long but I'm starting to find out that some people here are quite anal about things like misunderstanding a question.
ChadNC
The goal is for correct answers to be higher on the page than incorrect answers. This is an incorrect answer, therefore it should be voted down. You could delete it if you want to keep from being downvoted again.
Michael Myers
+2  A: 

You might consider the printf approach. It passes in an argument, that identifies the type for the called function.

printf("%d",  intvalue);
printf("%f",  floatvalue);
printf("%s",  stringvalue);

Here is a link that demonstrates how to implement a variable argument list.

EvilTeach
+1  A: 

If you don't want to pass a pointer, you could pass a union. Declare a union like a struct, but you can only use one member at a time. For example:

union foo {
char c;
int i;
}

struct foo f;
f.c = 'd';
function (f);
f.i = 23;
function(f);

int function(union foo f)
...

Remember that you can only use one field at a time, and if you set, say, f.c to something, the value of f.i is going to change in a potentially messy way. (I believe it's undefined behavior, but if a compiler ever does anything except byte-for-byte substitution it will fail to compile a whole lot of existing code.) Also, remember that the language provides no clue as to what field you last changed, so it's up to you to keep track of it somehow.

It isn't a very clean solution, but neither is the void * one. You're trying to subvert the type system, and you're not going to get a good solution.

David Thornley
+1 for pointing out that this can all be handled (including 'type selection', which was omitted) using a (struct+)union without the need for casting to void*. This approach is used extensively in the MAPI API (say that 3 times fast!) for instance.
pst
+1  A: 

it's been a while since i've done anything like this, so check the syntax.

To take off from Laura's answer, to make it more concise:

// the enum:
enum types{ int_val, char_val, float_val };
// the functions

int (*funcArr[3])(void*) = {NULL};


int to_do_if_int(void* input)
{
   return 0;
}

int to_do_if_char(void* input)
{
   return 0;
}

int to_do_if_float(void* input)
{
   return 0;
}

void initializer()
 {
   funcArr[int_val] = &to_do_if_int;
   funcArr[char_val] = &to_do_if_char;
   funcArr[float_val] = &to_do_if_float;
 }

int parse(void *arg, enum_type arg_type){    
   if (arg == NULL) return -1;

   (*funcArr[arg_type])(arg);

   return something;
}
San Jacinto
This has the advantage of keeping your code relatively clean. When you want to do something new for a new type, you just write the function, add the type to the enum, and then add it in the initializer. 3 steps, but your code is easy to maintain because each type has its own function, as opposed to being in a switch case.
San Jacinto
+1 This indeed will work better than the switch example when considering code maintenance.
laura