tags:

views:

39

answers:

2

I have a 3rd party function with signature:

int secretfoo(int numargs, ...);

I can call it directly, but what I really want is wrap it with my function that adds some extra arguments to it.

Assume simple case of integers: I want calls secretfoo(2, 10, 20) to be translated as this: when I see argument 10 to duplicate it and make the call: secretfoo(3, 10, 10, 20). I want to do it in wrapper:

int foowrapper(int numargs, ...);

This wrapper analyze argumetns and call secretfoo as described above.

Can this be done in portably with va_list / va_arg etc.? Any other way?

+1  A: 

I'm afraid it can't be done portably. stdarg.h "defines four macros" (latest C standard draft): va_start, va_end, va_arg and va_copy. None of these can be used to convert a va_list back to a variable number of values, other than one-by-one.

Your third party library should have supplied a function vsecretfoo(int, va_list), like the standard library does for these cases (vprintf, etc.).

larsmans
how this would help if there was `va_list` version? can I somehow build `va_list` dynamically?
zaharpopov
You can't build them dynamically, only shrink them by calling `va_arg` a few times. But that wouldn't solve your problem of growing it, I guess.
larsmans
+2  A: 

There is no portable way to manipulate the arguments in a variable argument list directly, because it is highly platform dependent how such arguments are passed into the function. And on most hardware architectures, there is absolutely no way to insert additional arguments in the middle or the end of the list.

If there is a practical upper limit to the number of arguments, then it could be done by extracting all the arguments to foowrapper and 'manually' building the new argument list for the call to secretfoo.
The code would look something like this:

int foowrapper(int numarg, ...)
{
  va_list args
  int newargs[numarg*2]; /* worst case allocation */
  int numnewargs = 0;

  /* Extract the arguments */
  va_start(numarg, args);
  for (int i=0; i<numarg; i++)
  {
    newargs[numnewargs++] = va_arg(args, int);

    /* duplicate value 10 as you encounter it */
    if (newargs[numnewargs-1] == 10)
    {
      newargs[numnewargs++] = 10;
    }
  }

  /* Forward to the secretfoo function */
  switch (numnewargs)
  {
  case 0: return secretfoo(0);
  case 1: return secretfoo(1, newargs[0]);
  case 2: return secretfoo(2, newargs[0], newargs[1]);
  /* etc... */
  }
}
Bart van Ingen Schenau
and if `secretfoo` has version with `va_list` instead of `...` - is this possible without switch statement?
zaharpopov
@zaharpopov: Only if you want to pass the `va_list` on unchanged. `va_list` only provides read-only access to the variable argument list.
Bart van Ingen Schenau
@Bart: no but I want to modify (expand) it like i explained in original question. do you mean it's not possible (except your switch method)? can I only pass `va_list` around without ever change it?
zaharpopov
@Bart, a `va_list` can also be shrunk by the caller from the front. `vprintf`, for example, has "the variable argument list replaced by [`va_list`] `arg`, which shall have been initialized by the `va_start` macro (and possibly subsequent `va_arg` calls)."
larsmans
@zaharpopov: There are exactly three things you can do with a `va_list`: Copy it (with `va_copy`), read arguments from it (with `va_arg`) and pass it on to a function (which expects a `va_list` argument). Extending the list in not portably possible and for some very common architectures (such as i386) not possible at all, because function arguments and local variables are placed intermixed on a hardware stack. Dynamically adding an element to a variable argument list would mean adding an element to the middle of the stack and the hardware just doesn't support that.
Bart van Ingen Schenau
@Bart: thanks I realize can't expand them. But can't I build a *new one* `va_list` from scratch somehow?
zaharpopov
@zaharpopov: No, because in the end, a `va_list` is nothing more than a way of telling the code "look *here* for getting your next argument" and the *here* might be a single location, but it can also be a set of multiple locations for different types. If you are willing to limit yourself to one (or a few) platforms, you could set up the arguments for `secretfoo` using assembly according to the relevant calling convention, but that will obviously not be widely portable.
Bart van Ingen Schenau