David Callanan
David Callanan

Reputation: 5958

How to build up a va_list in c++

I have an argument struct with different types and values (this is not that important to my question):

enum class ArgType {...}

struct Argument
{
    ArgType type;
    void* value;
}

I have a custom dynamic List implementation that I use to create a list of Arguments. I then have the following function which can take in this list and my aim is to loop through this list and build a va_list:

void foo(List<Argument>* args)
{
    for (int i = 0; i < args->length; i++)
    {
        Argument* arg = args->get(i);
        char* value = (char*) value;
        // build a `va_list` from value
    }
}

I then have a function called bar which will build a va_list using the foo function, and will print a formatted string.

void bar(char* format_str, List<Argument>* args)
{
    // get va_list from: foo(args);
    vfprintf(stdout, format_str, argptr);
}

How would I build a va_list, or make something like a va_list that I can still pass to vfprintf?

Thanks in advance.

Upvotes: 3

Views: 2581

Answers (2)

You should prefer, in C++11 or better, using variadic templates when possible. Read about parameter packs. Consider using std::stringstream-s, std::ostringstream-s etc.

If you really want to build a variadic call at runtime (but that is generally a bad idea) you might consider using the libffi (which knows about your particular ABI & calling conventions, and has some bits of assembler).

Today, va_list requires compiler support (thru __builtin_va_start etc...) and your ABI knows about it (since many calling conventions involve processor registers). See stdarg(3) (and notice its va_copy) and this.

Upvotes: 1

Remy Lebeau
Remy Lebeau

Reputation: 595712

va_list is just a pointer to arguments that have been pushed onto the call stack for use with a function CALL instruction. Your linked list exists elsewhere in memory. The only way to do what you are asking for is to write assembly code that enumerates the list and pushs the values into the call stack manually. Don't do it, it is highly compiler-dependant and even platform-dependant.

You shouldn't even be using printf-style functions in C++ to begin with. Use I/O streams instead, like std::cout, std::ostringstream, etc. Write an operator<< for Argument and then use a normal loop to enumerate the list and write its values to your desired std::ostream object.

In the case of your bar() function, you could simply split the format_str into a list of tokens, and then write each token to a std::ostream, substituting Argument values for "special" tokens as needed.

Or, you could just get rid of bar() altogether and let the caller directly format its Arguments list using whatever std::ostream it wants.

Upvotes: 1

Related Questions