开发者

va_args and 64 bits

开发者 https://www.devze.com 2022-12-17 22:33 出处:网络
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 abl

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);
开发者_Python百科

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);
}


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.


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.


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:

  • use streams and << operator to do formating
  • use a vector of boost::variant or boost::any
  • use a recursive tuple printer
  • use boost::format


In my case renaming the stdarg.h in out...osprey/obj/include, to fix_starg.h, soved to issue,and of course the header that requires stdarg.h also needs to be edited to: #include . Apparently stdarg.h sitting in out...kernel/obj/include was creating a conflict in bionic. I've noticed this issue with a few header files (particuarily types.h and similar variations). The headers seem to be fine in kernel/include and in bionic but as soon as the are in kernel/obj/incudes or kernel/obj/includes/linux, it seems to create an issue. I guess this folder is processed differently, at least it seemed to create havoc in bionic. Renaming the header files seemed to solve the issue.

0

精彩评论

暂无评论...
验证码 换一张
取 消