How to properly replace sprintf_s by sprintf in C++03?

2.7k views Asked by At

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?

1

There are 1 answers

1
AudioBubble On BEST ANSWER

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.