views:

384

answers:

3

I'm the lead dev for Bitfighter, and am having problems porting the game to 64-bit Linux. This should be a relatively easy and common problem, but it has stumped a number of people and I have been able to find no good information about it.

[[ The code compiles in 32-bit with gcc version 4.1.2, and others, and fails with several variants of 64-bit Linux, but I am relying on reports from others, and do not have the exact version of gcc that is failing. But it is failing for several people, on a variety of Linux flavors. I am 99% sure this is not a compiler version issue. ]]

I have the following:

 void UserInterface::drawCenteredString(int y, int size, const char *format, ...)
   {
    va_list args; 
    va_start(args, format); 
    char buffer[2048]; 
    dVsprintf(buffer, sizeof(buffer), format, args); 
    va_end(args);

    drawCenteredString2(y, size, buffer);
 }   

 // Elsewhere, in platform.cpp... (this is where the error occurs)

 S32 dVsprintf(char *buffer, int bufferSize, const char *format, void *arglist)
 {
    return vsnprintf(buffer, bufferSize, format, (char *) arglist);
 }

This works great on 32-bit platforms. However, when I compile it on 64-bit Linux, it fails:

 platform.cpp:457: error: cannot convert 'char*' to '__va_list_tag*' for argument '4' to 'int TNL::vsnprintf(char*, size_t, const char*, __va_list_tag*)'

I've tried many variants, including:

return vsnprintf(buffer, bufferSize, format, (va_list) arglist);

without success.

Does anyone have any ideas on how to make this construct portable, or achieve the same ends with a more 64-bit friendly mechanism?

And, for bonus points :-) can anyone tell me where the va_list_tag thingy comes from?

Thanks!

============================================

Here is the solution we settled on, using a different example:

logprintf("Hello %s", name);

calls

void logprintf(const char *format, ...)
{
   va_list s;    
   va_start( s, format );

   logger(LogConsumer::GeneralFilter, format, s);
   va_end(s);
}

calls

void logger(LogConsumer::FilterType filtertype, const char *format, va_list args)
{
   char buffer[4096];

   vsnprintf(buffer, sizeof(buffer), format, args);

   Platform::outputDebugString(buffer);
}
+2  A: 

This should work:

S32 dVsprintf(char *buffer, int bufferSize, const char *format, ...)
{
    va_list va_args;
    va_start( va_args, format );
    S32 result = vsnprintf(buffer, bufferSize, format, va_args); 
    va_end( va_args );
    return result;
}

__va_list_tag* is the hidden implementation type of ..., that's why it didn't like the char* typecast -- suddenly the pointer wasn't 32 bit anymore...

Considering this is C++, have you considered a more C++ approach than va_args? Several ideas come to mind:

Kornel Kisielewicz
+3  A: 

Firstly, the prototype for your dVsprintf is wrong.

S32 dVsprintf(char *buffer, int bufferSize, const char *format, void *arglist)

arglist clearly has the type va_list from context.

Secondly, why not just use vsnprintf instead of calling dVsprintf?

Thirdly, your function drawCenteredString clearly infinitely recurses, which is not good:

void UserInterface::drawCenteredString(int y, int size, const char *format, ...)
{
    ///...
    drawCenteredString(y, size, buffer);
}   

__va_list_tag* must be the underlying type of va_list. This is hard to verify, since va_list is gcc implementation dependent, and is not defined in the system header files, as far as I can see.

Alex Brown
Yes, it apparently does! That's a result of my attempts to simplify the problem a bit, and remove extraneous code. It doesn't really recurse forever! :-)
Watusimoto
I don't call vsnprintf directly because I'm using a slightly different implementation for Windows, and in my production code, the dVsprintf function has an #ifdef that uses a different function for each platform. That said, I might be able to make dVsprintf into a macro and avoid the extra function call.
Watusimoto
+4  A: 

Change

S32 dVsprintf(char *buffer, int bufferSize, const char *format, void *arglist)

to

S32 dVsprintf(char *buffer, size_t bufferSize, const char *format, va_list arglist)

and it should work without the cast.

Alok