Ever notice that some functions, like printf and scanf, can take any number of arguments? Ever want to learn how you can do the same? Well, now's your chance. Here I'll explain the rules of variadic functions and how to to roll your own.
Rules
1) You must have at least one fixed argument. For example:
2) You have to have some way of figuring out the types and sizes of the arguments that were passed to your function. Otherwise, who knows what could've been passed in?Code:/*this is ok*/ void good(int, ...); /*this is NOT ok*/ void bad(...);
Declaring
All right, you're going to need to include either stdarg.h or cstdarg, depending on whether you're coding in C or C++, respectively. Because I'm a low-level guy, I'll use C for my examples.
The way we declare our variadic functions is 1) put your arguments as you would a normal function, 2) as your final argument, put three periods (...) like so:
Accessing VariablesCode:void *varfunc(int numargs, ...); int dosomething(char *formatstr, FILE *file, size_t len, ...);
Unfortunately you have to access your variables in order, simply because of the way these things work. Anyway, you have to do two things to initialize:
1) Declare a variable of type va_list somewhere in your function, before you need to access your arguments.
2) Use the macro va_start and pass in your va_list variable, along with your last named argument. For example:
Now all you need to do is figure out the type of your first variant variable. printf and scanf do this by looking at the format string (the thing with all the %d and %s thingies in it) to determine what to pull off the stack next. Then save it off to a variable, and retrieve the next one!Code:void *varfunc(int numargs, ...) { va_list ap; va_start(ap,numargs); /*do some stuff*/ } int dosomething(char *formatstr, FILE *file, size_t len, ...) { va_list ap; va_start(ap,len); /*do some stuff*/ }
Make sure you call va_end on your argument list before you exit your function.Code:void foo(int firstarg, size_t lastarg, ...) { va_list ap; va_start(ap,lastarg); /*let's say I know the next argument is a char */ char nextarg = va_arg(ap,char); /*next one is an unsigned long.*/ unsigned long a = va_arg(ap,unsigned long); /*free the variable list*/ va_end(ap); }
Putting It Together
Simple and rather useless, I know, but I'm hungry right now and I need to eat dinner. Plus I think it gets the point across.Code:#include <stdio.h> #include <stdarg.h> int print_ints(size_t count, ...) { va_list ap; int tmp; va_start(ap,count); for(size_t i = 0; i < count; ++i) { tmp = va_arg(ap,int); printf("%d ",tmp); } va_end(ap); }
If you have any questions, feel free to post! I'd love to help.
Last edited by dargueta; 10-05-2009 at 04:29 PM.
sudo rm -rf /
Very well done. Personally, I prefer to avoid this type of function (I prefer overloading and default values in C++), but it's nice to have a clear explanation of how it works.
Good little tutorial. God bless variadic functions. You're going to need to re-write this for C++0x when it comes out, because func(...) is now valid, and finally, finally, varadic templates exist (Tuples, anyone?)
Excellent tutorial! I also prefer function overloading.
+rep!
Good work, +rep of courseDid you get your dinner?
Hey! Check out my new Toyota keyboaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Yes, and it was very good, too. Though I avoid variadic functions, there are some cases where it'd be impractical to do so. Take the *printf and *scanf functions, for example. It'd be really annoying to do separate calls for each variable you want to print out / read in.
sudo rm -rf /
There are currently 1 users browsing this thread. (0 members and 1 guests)
Bookmarks