English 中文(简体)
Passing parameters dynamically to variadic functions
原标题:

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?

问题回答

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.

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.

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
", 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;
}

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]);
  /* ... */
}




相关问题
Fastest method for running a binary search on a file in C?

For example, let s say I want to find a particular word or number in a file. The contents are in sorted order (obviously). Since I want to run a binary search on the file, it seems like a real waste ...

Print possible strings created from a Number

Given a 10 digit Telephone Number, we have to print all possible strings created from that. The mapping of the numbers is the one as exactly on a phone s keypad. i.e. for 1,0-> No Letter for 2->...

Tips for debugging a made-for-linux application on windows?

I m trying to find the source of a bug I have found in an open-source application. I have managed to get a build up and running on my Windows machine, but I m having trouble finding the spot in the ...

Trying to split by two delimiters and it doesn t work - C

I wrote below code to readin line by line from stdin ex. city=Boston;city=New York;city=Chicago and then split each line by ; delimiter and print each record. Then in yet another loop I try to ...

Good, free, easy-to-use C graphics libraries? [closed]

I was wondering if there were any good free graphics libraries for C that are easy to use? It s for plotting 2d and 3d graphs and then saving to a file. It s on a Linux system and there s no gnuplot ...

Encoding, decoding an integer to a char array

Please note that this is not homework and i did search before starting this new thread. I got Store an int in a char array? I was looking for an answer but didn t get any satisfactory answer in the ...

热门标签