7

I was wondering if there was any way to pass parameters dynamically to variadic functions. i.e. If I have a function

int some_function (int a, int b, ...){/*blah*/}

and I am accepting a bunch of values from the user, I want some way of passing those values into the function:

some_function (a,b, val1,val2,...,valn)

I don't want to write different versions of all these functions, but I suspect there is no other option?

4
  • 1
    if all values are of same type (as your question seems to imply - correct me if I'm wrong) I'd suggest to not use a variadic function at all and pass an array instead; see here for some macro magic to pretty it up for when you want to pass in a fixed number of arguments: stackoverflow.com/questions/1375474/variable-arity-in-c/… Commented Nov 12, 2009 at 11:59
  • No they are of different types. Commented Nov 12, 2009 at 12:01
  • @tommobh: the array approach can still work if you wrap your values in a union or pass in an array of void * to the values instead of the values themselves, but it'll be less elegant Commented Nov 12, 2009 at 12:04
  • @Christoph: Passing a array of void* sounds like an ideal solution actually, I didn't think this would work. Commented Nov 12, 2009 at 12:57

4 Answers 4

12

Variadic functions use a calling convention where the caller is responsible for popping the function parameters from the stack, so yes, it is possible to do this dynamically. It's not standardized in C, and normally would require some assembly to manually push the desired parameters, and invoke the variadic function correctly.

The cdecl calling convention requires that the arguments be pushed in the correct order, and after the call, the bytes pushed as arguments before the call are popped. In this way, the called function can receive an arbitrary number of parameters, as the caller will handle reverting the stack pointer to it's pre-call state. The space occupied by the arguments before the ... is the safe lower bound for number of bytes pushed. Additional variadic arguments are interpreted at runtime.

FFCALL is a library which provides wrappers for passing parameters dynamically to variadic functions. The group of functions you're interested in is avcall. Here's an example calling the functions you gave above:

#include <avcall.h>

av_alist argList;
int retVal;
av_start_int(argList, some_function, retval);
av_int(argList, a);
av_int(argList, b);
av_type(argList, val1);
...
av_type(argList, valn);
av_call(argList);

You might also find this link discussing generating wrappers around variadic functions in C, to be of interest in justifying why this isn't part of standard C.

Sign up to request clarification or add additional context in comments.

4 Comments

@Anacrolix What do you mean "and invoke the varargs call appropriately."? The function deals with the varargs.
I think it's important to mention that the library is GPL 3, which makes it no go for lots of cases.
FFCALL appears to be plagiarized from here.
That's a strong accusation when both packages have the same author in the copyright notices. Your link has a copyright notice from Bruno Haible 1995-2001, who has been doing GNU stuff for as long as I can remember. More likely the download on his personal website is an old release, and the GNU site has the more recent official releases.
2

A standard approach is to have each variadic function accompanied by a va_list-taking counterpart (as in printf and vprintf). The variadic version just converts ... to a va_list (using macros from stdarg.h) and calls its va_list-taking sister, which does actual work.

2 Comments

@atzz Same problem though. I still need to add parameters dynamically to the function whether it calls its va_list-taking sister or not. Do you mean I convert the values to a va_list and then just use the va_list-taking sister function instead?
@tommobh -- I guess i interpreted your question incorrectly. From reading the comments I take it that you have a means of obtaining values of different types (e.g. from gui) and need to pass them to a function; is it correct? In this case, va_list won't help much. If you don't want to rely on hacks, you'll have to change the functions...
1

It might be interesting to try just passing an array, and then use the vararg macros anyway. Depending on stack alignment, it might Just Work (tm).

This is probably not an optimal solution, I mainly posted it because I found the idea interesting. After trying it out, this approach worked on my linux x86, but not on x86-64 - it can probably be improved. This method will depend on stack alignment, struct alignment and probably more.

void varprint(int count, ...)
{
    va_list ap;
    int32_t i;

    va_start(ap, count);
    while(count-- ) {
        i = va_arg(ap, int32_t);
        printf("Argument: %d\n", i);
    }
    va_end(ap); 
}

struct intstack
{
    int32_t pos[99];
};

int main(int argc, char** argv)
{
    struct intstack *args = malloc(sizeof(struct intstack));
    args->pos[0] = 1;
    args->pos[1] = 2;
    args->pos[2] = 3;
    args->pos[3] = 4;
    args->pos[4] = 5;

    varprint(5, *args);
    return 0;
}

3 Comments

it won't work as arrays aren't passed by value, but as pointers
True - when I thought it out, I thought of somehow pushing the array on the stack. But C won't do that if you just pass the array to a function - you would have to do it yourself.
When using a struct to wrap the array, it has the possibility of working, at least :) See example
-1

Depending on what it is you're passing around, it could be a discriminated union you're after here (as hinted at in the comments). That would avoid the need for variadic functions or arrays of void*, and answers the question "how does some_function know what you actually passed it". You might have code something like this:

enum thing_code { INTEGER, DOUBLE, LONG };

struct thing
{
 enum thing_code code;
 union
 {
    int a;
    double b;
    long c;
 };
};

void some_function(size_t n_things, struct thing *things)
{
    /* ... for each thing ... */
    switch(things[i].code)
    {
      case INTEGER:
      /* ... */
    }
}

You can take this a step further and avoid the switch by replacing the code with one or more pointers to functions that do something useful with each thing. For example, if what you wanted to do was to simply print out each thing, you could have this:

struct thing
{
 void (*print)(struct thing*);
 union
 {
   ...
 };
}

void some_function(size_t n_things, struct thing *things)
{
  /* .. for each thing .. */
  things[i]->print(things[i]);
  /* ... */
}

3 Comments

@Ned: The variadic function I am using is in a library that I would ideally not like to change (i.e. i don't want to change 'some_function()'). The user can enter any amount of any type they like (within reason) so defining a strict union doesn't seem like it will work. An array of void* or Anacrolix's suggestion seems like my best bet. Either that or I write some inline assembly :S.
But if you can't change some_function, how can you pass it an array of void*? By user here, are we talking about a developer using your code, or an end user of the program?
@Ned: User=end user. Yeah I see what you mean actually. I do not want to change the function because the function itself invokes another variadic function and I'd have to change that too. It would just snowball. I feel like I'm back where I started.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.