How to create a va_list on GCC?
Asked Answered
M

3

7

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]'
Maharajah answered 26/12, 2010 at 19:10 Comment(3)
consider using libffi ( sourceware.org/libffi )Glorygloryofthesnow
I reckon this problem is insoluble. It's just not possible to dynamically construct a va_list in portable C/C++. Add to that the problem with __builtin_va_list not being defined in any header file, and the thing becomes doubly impossible.Tod
Given more complete code for the function, it maybe possible to help you. However, as TonyK said, there isn't a portable way to create a va_list.Sunglasses
I
4

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.)

Isochronal answered 26/12, 2010 at 19:37 Comment(2)
I think the OP wants to delete some of the arguments from an existing va_list before passing it to vsprintf. This looks difficult to me. gcc uses the built-in type __builtin_va_list for this, which is automatically pre-defined by the compiler -- it's not defined in any header files. So you would need to go to the source code of the compiler to see what it looks like, or look at some assembly language output. Neither of these alternatives is very inviting!Tod
Thanks Tony. I misunderstood. I thought he was just popping items off the front, not from the middle. Responded in-message as it got long...Isochronal
G
1

There's not enough context to figure out what you're trying to do here, but if you need to COPY a va_list, you may be able to use the C99 standard function va_copy, which gcc supports (but I have no idea if MS supports it).

Gully answered 27/12, 2010 at 4:56 Comment(0)
S
0

There is a way to do this, it isn't pretty:

union {
      char *pa;
      va_list al;
      } au;

....
au.pa = new_args;
vsprintf(buffer, sFormat.c_str(), au.al);

Using a union instead of a cast is ugly, but you can't cast if va_list is an array type.

Sidewheel answered 22/4, 2014 at 9:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.