I'm sure other people will tell you to use BOOST. So in the interest of providing a solution that is not dependent on BOOST:
I have run into the same problem so often that I gave up and wrote my own helper macros that feed into %s instead of any brand of %llu
or %lu
or whatever. I also found it helpful for helping maintain a sane format string design, and for providing better (and more consistent) hex and pointer printouts. There are two caveats:
You can't easily combine extra formatting parameters (left/right justification, padding, etc) -- but then you can't really do that with the LU
macro either.
this approach does add additional overhead to the task for formatting and printing strings. However, I write performance-critical apps and I haven't noticed it being an issue except in Microsoft's Visual C++ debug builds (which take about 200x longer to allocate and free heap memory than normal because of all the internal validation and corruption checks).
Here's a comparison:
printf( "Value1: " LU ", Value2: " LU, somevar1, somevar2 );
vs.
printf( "Value1: %s, Value2: %s", cStrDec(somevar1), cStrDec(somevar2) );
To make it work, I used a set of macros and templates like this:
#define cStrHex( value ) StrHex ( value ).c_str()
#define cStrDec( value ) StrDecimal( value ).c_str()
std::string StrDecimal( const uint64_t& src )
{
return StrFormat( "%u%u", uint32_t(src>>32), uint32_t(src) );
}
std::string StrDecimal( const int64_t& src )
{
return StrFormat( "%d%u", uint32_t(src>>32), uint32_t(src) );
}
std::string StrDecimal( const uint32_t& src )
{
return StrFormat( "%u", src );
}
std::string StrDecimal( const int32_t& src )
{
return StrFormat( "%d", src );
}
std::string StrHex( const uint64_t& src, const char* sep="_" )
{
return StrFormat( "0x%08x%s%08x", uint32_t(src>>32), sep, uint32_t(src) );
}
std::string StrHex( const int64_t& src, const char* sep="_" )
{
return StrFormat( "0x%08x%s%08x", uint32_t(src>>32), sep, uint32_t(src) );
}
// Repeat implementations for int32_t, int16_t, int8_t, etc.
// I also did versions for 128-bit and 256-bit SIMD types, since I use those.
// [...]
My string formatting functions are based on the now-preferred method of formatting directly into a std::string, which looks something like this:
std::string StrFormatV( const char* fmt, va_list list )
{
#ifdef _MSC_VER
int destSize = _vscprintf( fmt, list );
#else
va_list l2;
va_copy(l2, list);
int destSize = vsnprintf( nullptr, 0, fmt, l2 );
va_end(l2);
#endif
std::string result;
result.resize( destSize );
if (destSize!=0)
vsnprintf( &result[0], destSize+1, fmt, list );
return result;
}
std::string StrFormat( const char* fmt, ... )
{
va_list list;
va_start( list, fmt );
std::string result = StrFormatV( fmt, list );
va_end( list );
return *this;
}