Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best Way to Store a va_list for Later Use in C/C++

I am using a va_list to construct a string that is rendered.

void Text2D::SetText(const char *szText, ...)

This is all fine and good, but now the user has the ability to change the language while the application is running. I need to regenerate all the text strings and re-cache the text bitmaps after initialization. I would like to store the va_list and use it whenever the text needs to be generated.

To give you some more background, this needs to happen in the case where the key string that I'm translating has a dynamic piece of data in it.

"Player Score:%d"

That is the key string I need to translate. I would like to hold the number(s) provided in the va_list for later use (outside the scope of the function that initializes the text) in the case that it needs to be re-translated after initialization. Preferably I would like to hold a copy of the va_list for use with vsnprintf.

I've done some research into doing this and have found a few ways. Some of which I question whether it is a appropriate method (in terms of being stable and portable).

like image 974
resolveaswontfix Avatar asked Oct 13 '09 21:10

resolveaswontfix


3 Answers

Storing the va_list itself is not a great idea; the standard only requires that the va_list argument work with va_start(), va_arg() and va_end(). As far as I can tell, the va_list is not guaranteed to be copy constructable.

But you don't need to store the va_list. Copy the supplied arguments into another data structure, such as a vector (of void*, probably), and retrieve them later in the usual way. You'll need to be careful about the types, but that's always the case for printf-style functions in C++.

like image 142
David Seiler Avatar answered Nov 10 '22 01:11

David Seiler


What you describe about "holding the number(s) provided in the va_list" is the way to approach this.

The va_list maintains pointers to temporary memory on the stack (so-called "automatic storage" in the C standard). After the function with variable args has returned, this automatic storage is gone and the contents are no longer usable. Because of this, you cannot simply keep a copy of the va_list itself -- the memory it references will contain unpredictable content.

In the example you give, you will need to store two integers that are re-used when re-creating that message. Depending on how many different format strings you have to deal with, your approach might vary.

For a completely general type of approach, you will need to:

  • Write a "cache_arguments()" function which creates a dynamic-memory buffer of the values found in variable arguments.
  • This cache_arguments() would use the printf()-style format string, along with the va_start, va_arg, va_end macros. You will need to retrieve the types according to the printf() type-specifiers, because sizeof(double) != sizeof(int).
  • Store the arguments in your memory cache with the same alignment and padding expected by va_arg() on your platform. (Read your varargs.h file.)
  • Get your calls to vsnprintf() working with this cached memory buffer instead of a pointer created by va_start().

The above items are all possible on most platforms, including Linux and Windows.

An item you may wish to consider about translation is the word-order concern. What is written in English as:

Player Sam has scored 20 points.

Could in some (human) languages only be written fluently with a word order analagous to:

20 points have been scored by Player Sam.

For this reason, the Win32 FormatMessage() API uses a printf()-like format string, with the functional difference that parameters are numbered, as in:

Player %1 has scored %2!d! points.
%2!d! points have been scored by player %1.

(The string type is assumed for each argument, so %1 is equivalent to %1!s!)

Of course you may not be using the Win32 API, but the functionality of altering the word order of the formatted arguments is what I am attempting to introduce as a concept. You may want to implement this in your software, also.

like image 35
Heath Hunnicutt Avatar answered Nov 10 '22 00:11

Heath Hunnicutt


You can use va_copy(), here is an example:

va_list ap;
va_list tmp;
va_copy(tmp, ap);
//do something with tmp
va_end(tmp);
like image 3
PawMarc Avatar answered Nov 10 '22 02:11

PawMarc