printf() to a std::string

Just in case you weren’t already aware, a part of the upcoming C++20 Standard is a text formatting library (based upon the popular, free, third-party {fmt} library). It looks like being a great addition to the Standard Library, providing ease-of-use, performance and type-safety.

Before we put the <iomanip> and <cstdio> C++ headers into the museum however, lets look at something which is also possible in Modern C++, using a printf()-style format string to construct a std::string. The prototype of this function is therefore:

std::string string_printf(const char *fmt,...);

To use the (optional) variadic arguments pack indicated by the ellipsis we need to use the functionality from the <cstdarg> header, in the form of some clever macros: va_list, va_start and va_end. Actually we need two va_lists, so we use va_copy as well.

The printf() family functions which take a format string and a va_list all begin with “v”. The code below uses vsnprintf() (with nullptr and zero length as arguments) for just one purpose, its return code (being the length of the output string, not including the null terminator byte). This is used on the std::string‘s resize() member function. We then call vsprintf() with the writable (since C++17) data() member of the string as the pointer value. Finally, a call to pop_back() (a C++11 addition) removes the null terminator byte from the std::string, and this is passed as the function’s return value. The complete function is shown below:

#include <cstdio>
#include <cstdarg>
#include <string>

inline std::string string_printf(const char *fmt,...) {
    std::string s{};
    va_list args, args2;
    va_start(args, fmt);
    va_copy(args2, args);

    s.resize(vsnprintf(nullptr, 0, fmt, args2) + 1);
    va_end(args2);
    vsprintf(s.data(), fmt, args);
    va_end(args);
    s.pop_back();
    return s;
}

The main performance hit with this approach is the need to call two printf()-family functions in order to reserve exactly the right amount of buffer space. It is likely to be quicker than using C++ streams with manipulators, but maybe std::format will be faster still, as well as being type-safe.

Resources

Download or browse the source code

1 thought on “printf() to a std::string”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s