18

As a learning experience I'm porting some stuff from Windows to MacOS and came across something like this:

void SomeClass::someFunction(const char* format, va_list args)
{
    int size = _vscprintf(format, args); // length after formatting
    std::string s;
    s.resize(size);
    vsprintf(&s[0]);
    ...
}

Now, as _vscprintf is Microsoft specific and I haven't found anything similar on Linux I thought I'd ask here.

Let's also assume this code is in some critical path and shouldn't have some extra overhead of heap allocation or such.

What is the recommended replacement for _vscprintf on MacOS/Linux?

Thanks!

murrekatt
  • 5,961
  • 5
  • 39
  • 63
  • 1
    possible duplicate of [\_vscprintf equivalent on Android?](http://stackoverflow.com/questions/7825648/vscprintf-equivalent-on-android) – ismail Jan 28 '13 at 19:41
  • There may not be a direct replacement for the _vscprintf function; but if you use it together with `vsprintf` (as in your `someFunction` example), you could use the `asprintf` function instead (if it is available on your system). – oliver Dec 08 '15 at 12:42

2 Answers2

25

You can use vsnprintf instead;

  int _vscprintf (const char * format, va_list pargs) { 
      int retval; 
      va_list argcopy; 
      va_copy(argcopy, pargs); 
      retval = vsnprintf(NULL, 0, format, argcopy); 
      va_end(argcopy); 
      return retval; 
   }

Thanks to @dbasic for the more complete solution and @j-a for fixing the obvious errors.

ismail
  • 46,010
  • 9
  • 86
  • 95
  • Ditto. Not only that but the sensible expectation from this is a crash – jheriko Jan 17 '12 at 10:24
  • `vsnprintf(NULL, 0, f, a);` should work instead (which just so happens to be answer at the question that Daniel listed above). – dash-tom-bang Jan 28 '13 at 19:33
  • One more thing I would like to add is that copy of a must be made by using va_copy otherwise, it would not work in the later function calls. I faced such problem on Mac OS X. – doptimusprime Mar 08 '13 at 04:19
  • 3
    I did in the following way: int _vscprintf (const char * format, va_list pargs) { int retval; va_list argcopy; va_copy(argcopy); retval = vsnprintf(NULL, 0, format, pargs); va_end(argcopy); return retval; } – doptimusprime Mar 08 '13 at 11:19
  • Has anyone tested this? It seems like the best thing to do would be to create an initial buffer, pass it as the first argument of vsnprintf, then – allicoder Oct 16 '13 at 13:44
14

The previous solution is ok but has two bugs:

  1. The function va_copy has one parameter instead of two parameters.
  2. The call to function vsnprintf doesn't use the argcopy variable; it generates a corruption in the stack if you call vsnprintf again then.
int _vscprintf (const char * format, va_list pargs)
{ 
    int retval; 
    va_list argcopy;
    va_copy(argcopy, pargs); 
    retval = vsnprintf(NULL, 0, format, argcopy); 
    va_end(argcopy); 
    return retval;
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
MarianoVolker
  • 171
  • 1
  • 6
  • You should not put anything other than code or comments in indentation. – Daniel Larsson Oct 30 '13 at 19:59
  • 1
    Normally, when you pass a `va_list` to a function, it is assumed that it will be 'consumed' by the called function. The `va_copy()` code would normally be in the `SomeClass::someFunction()` in the question, rather than in the library function. The only safe way to call the library function is with a copy of the `va_list`. I see nothing on the MS manual pages (http://msdn.microsoft.com/en-us/library/w05tbk72%28v=vs.110%29.aspx and http://msdn.microsoft.com/en-us/library/t32cf9tb%28v=vs.110%29.aspx) to indicate otherwise. – Jonathan Leffler Feb 18 '16 at 05:03