Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a va_list on GCC?

I'm trying to convert some code so that it compiles on gcc too (right now, it compiles only on MSVC).

The code I'm stuck at is in a pseudo-formatting function that accepts as input a format string and zero or more arguments (const char *format, ...). It will then process some of the placeholders consuming some of the arguments, and pass the rest to vsprintf along with a new va_list dynamically generated.

This is the actual code for generating the new va_list:

char *new_args = (char *) malloc(sum);
char *n = new_args;

for(int i = 0; i < nArgs; i++)
{
    int j   = order[i];
    int len = _getlen(types[j]);

    memcpy(n, args + cumulOffsets[j], len);
    n += len;
}

vsprintf(buffer, sFormat.c_str(), new_args);

In my defense, I didn't and would never write this code. In fact, I think it's one of the most hackiest things I've seen in my whole life.

However, this function is very complex, very old, and very important. It's also hasn't been modified in years (well, except now) so while I'd like to rewrite it from scratch I can't justify the time it would take plus the bugs it would introduce.

So, I need a way to do this same thing on GCC.. But there a va_list is not a char * so I'm getting:

error: ISO C++ forbids casting to an array type '__va_list_tag [1]'
like image 450
frm Avatar asked Dec 26 '10 19:12

frm


1 Answers

I'm a bit lost. Why do you need a new dynamically-generated va_list? Why not just reuse the old one?

I believe vsnprintf() uses a current va_list object (if you can call it that). So you are free to va_start(), use the arguments you want via va_arg(), then pass the remaining arguments via the va_list to vsnprintf(), and then call va_end().

Am I missing something? Why the deep copy?

And if you do need a deep copy, why not va_start() fresh, remove the arguments you want via va_arg(), and then pass the resulting va_list object to vsnprintf().

(Each call to va_arg modifies the va_list object so that the next call returns the next argument.)

Alternatively, you could just use va_copy(). (Though be sure to follow it with a corresponding va_end().)

Addendum: Also note that these va_ macros are based on C89 & C99 standards. GNU g++ will support them. Microsoft is somewhat more limited.


Following up on TonyK's comment:

What I said above works if you are pulling the first N items off the va_list. If you are pulling items out of the middle, that's harder.

There is no portable way to construct a va_list.

However, you could pull apart the format string, use it to determine the object types (double,float,int,etc), and print each one out individually with it's own format string (a subsection of the original format string). The multiple snprintf() calls will cause some overhead. But if this routine isn't called too often, it should be viable.

You could also print out subsections of the original format string with a suitably crafted va_list. In other words, the first vsnprintf() call prints elements 1..3, the second elements 5..7, the third 10..13, etc. (As vsnprintf() will ignore extra elements on the va_list beyond what it needs. You just need a series of corresponding format-string-fragments, and popping items off the va_list with va_arg() as needed for each vsnprintf() call.)

like image 169
Mr.Ree Avatar answered Sep 28 '22 17:09

Mr.Ree