3

sprintf_sis a Microsoft implementation of the function sprintf where they patched a flaw, adding an argument to take a boundary value where the function is limited to write.

An equivalent was introduced in C++11: snprintf. But here, we are talking of C++03 syntax.

Signatures:

count_char_written sprintf(char* string_out, const char* output_template, VARIADIC_ARGS);
// and
count_char_written sprintf_s(char* string_out, size_t buffer_max_size, const char* output_template, VARIADIC_ARGS);

Functionnaly, sprintf_s is more advanced than sprintf, because it avoids overflows. But sprintf_s is Microsoft only!

What to do if you want to port back a C++03 code written with sprintf_s to POSIX compatible syntax?

Sandburg
  • 757
  • 15
  • 28
  • 1
    `snprintf` is also POSIX. – Matthieu Brucher Jan 17 '19 at 15:06
  • @MatthieuBrucher I never said it is not. My constraint is not POSIX but C++03 max (it's more not only Microsoft), and `snprintf`was introduced in C++11. – Sandburg Jan 17 '19 at 15:13
  • @Sandburg So you need to implement this for Windows while using the system's `snprintf` on POSIX? –  Jan 17 '19 at 15:17
  • @ivan That's it. I have to produce a code that compiles on both windows and posix systems and compatible with C++03 compiler. – Sandburg Jan 17 '19 at 15:18
  • The good answer may be "don't use it, but do prefer streams!" I don't know. – Sandburg Jan 17 '19 at 15:19
  • @ivan Asked like that, I guess not. I'm asking for proposals. If you think the convenient way is to implement `snprintf` for systems that haven't it in their `std`, OK. It's more a "what would you do?". – Sandburg Jan 17 '19 at 15:27
  • Just write it yourself, use vsnprintf() to do the heavy lifting so it takes only a single line of code. [This Q+A](https://stackoverflow.com/questions/37788305/how-do-you-call-vsnprintf-safely) talks about vsnprintf. – Hans Passant Jan 17 '19 at 15:29
  • 1
    @HansPassant MSVC12 (and older) do not provide `vsnprintf` as it was added in C99 –  Jan 17 '19 at 15:31

1 Answers1

4

Today both snprintf and vsnprintf should be available everywhere with the exception of Windows with MSVC12 and older. The simplest way for you is to provide snprintf/vsnprintf on Windows where it is not available.

Windows provides function _vsnprintf_s which is already similar to vsnprintf, but has following important differences with regards to what happens when provided buffer is too small:

  • Buffer content depends on the additional count argument which does not exist in vsnprintf. To get vsnprintf behavior you can pass _TRUNCATE here.
  • -1 is returned instead of number of characters required. This can be fixed by using _vscprintf function which only needs to be called if previous call to _vsnprintf_s has failed.

Additionally those functions do not support format specifiers added in C99 such as %zd. This cannot be easily resolved, you will have to avoid using them.

Code below:

int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
{
    int r = -1;

    if (size != 0)
    {
        va_list args_copy;
        va_copy(args_copy, args);
        r = _vsnprintf_s(buf, size, _TRUNCATE, fmt, args_copy);
        va_end(args_copy);
    }

    if (r == -1)
    {
        r = _vscprintf(fmt, args);
    }

    return r;
}

int snprintf(char *buf, size_t size, const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    int r = vsnprintf(buf, size, fmt, args);
    va_end(args);
    return r;
}

Note: Windows also provides _vsnprintf which looks better suited for this implementation, but it does not terminate the resulting string. If you want to use it, you should be careful.

  • Sorry, I was not very confident, I had to verify the existance of `snprintf`on my `ixlc`compiler (iSeries / AS400... limited to C++03). Thank you, this approch is great. It filles the smaller gap left by Microsoft "Linux, I don't promise them a great future!" – Sandburg Jan 18 '19 at 09:55