28

Is there a way to create a va_list from scratch? I'm trying to call a function that takes a va_list as a parameter:

func(void **entry, int num_args, va_list args, char *key); 

...from a function that doesn't take a variable number of arguments. The only way I can think of is to create an intermediary function that takes varargs and then passing along its va_list, which is pretty stupid:

void stupid_func(void **entry, char *key, int num_args, ...) {
    va_list args;
    va_start(args, num_args);

    func(entry, num_args, args, key);

    va_end(args);
}

Is there a better way? I can't change func's signature.

kris
  • 23,024
  • 10
  • 70
  • 79
  • 7
    The comment saying not to pass va_list is old, but just flat out wrong; va_list is there in part so that you can do just that. Passing va_list is how you write printf wrappers: you make a version that takes `...` and then pass the resulting va_list to `vfprintf` or whatever – EvanED Jun 18 '15 at 14:50
  • It's worth stating the obvious: it depends on which direction you pass it. Passing it "down", from a caller to callee is of course the right way to do it, and what everyone does all the time. Any attempt to pass it "up", from a callee to caller (e.g. by returning va_list, storing it to a global/static var) will lead to kaboom. Again, that should be very obvious to any intermediate C programmer. – pfalcon Dec 10 '18 at 19:12
  • There is a useful blog post over on [cocoawithlove](http://cocoawithlove.com/2009/05/variable-argument-lists-in-cocoa.html) that suggests a way to fake a va_list - it does lock you down to compiler and platform specific behaviour, as noted by the other good answers here. – petert Jan 20 '11 at 10:33

4 Answers4

14

This is a bad idea because the va_list abstraction is there to hide some grim compiler/architecture specific details regarding stack-pointers and what not. And it is pretty much bound to the function's scope once initialized. If you wind the stack and reference a previous frames va_args out of scope, things can go bad. You can pass them around but ...

expect bugs

See: http://lists.freebsd.org/pipermail/freebsd-amd64/2004-August/001946.html

Also checkout man(3) va_copy and friends for safer handling of va_args and passing them around.

IMHO the va_args stuff is not very neat. In the past I have dealt with this by initializing structures/opaque pointers on the heap then using pointer arithmetic to work the data. But this is a hack and depends on circumstances.

Aiden Bell
  • 28,212
  • 4
  • 75
  • 119
  • 1
    IMO this answer spans from plain wrong to misleading. The linked email talks about using a va_list *twice*, it does not say that using a va_list as pointed out by the OP is wrong (and in fact it is not). – ntd May 13 '17 at 17:50
  • @ntd suggest an edit or at least be constructive in your criticism. I wrote this from experience. I may be wrong, and I am happy to be. – Aiden Bell May 24 '17 at 10:43
  • From what I understand the wiki is for improving the answers, not for reverting the meaning... I cannot edit it. I should add I came here because I was looking for the same info: if you know a better way to highlight what I found I will be happy to delete my unconstructive comment. – ntd May 26 '17 at 08:07
6

I understand and agree with Aiden's warnings - va_list and friends are dangerous since they hide low-level calling conventions. But ... in this situation I think you have no other option. Put the static ... function into the .c file so nobody else can see it, sort of a proxy to the function you need to call, test the hell out of it, and be done. Just make sure you don't expose the variadic arguments up the call chain.

Nikolai Fetissov
  • 82,306
  • 11
  • 110
  • 171
  • @Nikolai - Good advice, although I find for portability/grace's sake, it is work abstracting the arguments of the targets into a binary block and overlaying some structural information onto it. Unless some function like vsprintf is ultimately getting the data and it needs to be va_list – Aiden Bell Jun 12 '09 at 19:37
  • @Aiden - that's what it looks like here - OP says "I can't change func's signature". In a general case of passing unknown number of things into a function - YES, by all means, use some explicit well-defined self-describing structure (by pointer, of course). – Nikolai Fetissov Jun 12 '09 at 19:53
5

Your stupid_func is perfectly valid C code, otherwise how would you supposedly call vprintf and similar functions?

The glib library make use of these wrappers extensively. The C99 specification itself has something similar in the examples. Excerpt from section 7.19.6.8:

The following shows the use of the vfprintf function in a general error-reporting routine.

#include <stdarg.h>
#include <stdio.h>
void error(char *function_name, char *format, ...)
{
    va_list args;
    va_start(args, format);
    // print out name of function causing error
    fprintf(stderr, "ERROR in %s: ", function_name);
    // print out remainder of message
    vfprintf(stderr, format, args);
    va_end(args)
}
ntd
  • 7,372
  • 1
  • 27
  • 44
  • Found your answer following on from your comment on mine. +1, especially for pointing out a valid example passing va_list. In my experience, albeit limited, doing much more than this leads to bugs where you have references to the args after va_end or the frame is popped. – Aiden Bell May 24 '17 at 10:45
5

Your idea of a proxy function that creates the va_list is the right way to do it. There is no need for that proxy to have public scope. However, if you might find that the proxy already exists. For instance, in many library implementations sprintf() is just a proxy for vsprintf().

Unless you are willing to tie your code to a specific compiler and target platform, there is no better way. The names defined in <stdarg.h> are there to provide a portable and consistent interface to support access to variadic argument lists. The only portable way to implement and use variadic functions is through that interface.

That said, it is possible that you could sacrifice portability by duplicating a call frame in an array and hand construct a va_list that correctly references it. The result will never be portable.

mcandre
  • 22,868
  • 20
  • 88
  • 147
RBerteig
  • 41,948
  • 7
  • 88
  • 128