va_args and 64 bits
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.
精彩评论