I've provided a more memory-efficient version for C++17, which provides read/write access to the underlying array of a string. My copy of CPP Reference still says "Modifying the character array accessed through data()
has undefined behavior", but the [Web version][std::string::data] has been edited[edited] (May 2017) to indicate that it's only the const
version that has that constraint.
[std::string::data]: http://en.cppreference.com/w/cpp/string/basic_string/data#mw-content-text (std::string::data()) [edited]: http://en.cppreference.com/mwiki/index.php?title=cpp%2Fstring%2Fbasic_string%2Fdata&diff=93466&oldid=91659
I've provided a more memory-efficient version for C++17, which provides read/write access to the underlying array of a string. My copy of CPP Reference still says "Modifying the character array accessed through data()
has undefined behavior", but the [Web version][std::string::data] has been edited (May 2017) to indicate that it's only the const
version that has that constraint.
[std::string::data]: http://en.cppreference.com/w/cpp/string/basic_string/data#mw-content-text (std::string::data())
I've provided a more memory-efficient version for C++17, which provides read/write access to the underlying array of a string. My copy of CPP Reference still says "Modifying the character array accessed through data()
has undefined behavior", but the [Web version][std::string::data] has been [edited] (May 2017) to indicate that it's only the const
version that has that constraint.
[std::string::data]: http://en.cppreference.com/w/cpp/string/basic_string/data#mw-content-text (std::string::data()) [edited]: http://en.cppreference.com/mwiki/index.php?title=cpp%2Fstring%2Fbasic_string%2Fdata&diff=93466&oldid=91659
Create a C++ string using printf-style formatting
It's often convenient to use C-style printf
format strings when writing C++. I often find the modifiers much simpler to use than C++ I/O manipulators, and if I'm cribbing from existing C code, it helps to be able to re-use the existing format strings.
Here's my take on creating a new std::string
given a format and the corresponding arguments. I've given it an annotation so GCC can check the argument types agree when the format is a literal, but I don't know how to help other compilers.
I've provided a more memory-efficient version for C++17, which provides read/write access to the underlying array of a string. My copy of CPP Reference still says "Modifying the character array accessed through data()
has undefined behavior", but the [Web version][std::string::data] has been edited (May 2017) to indicate that it's only the const
version that has that constraint.
For earlier standards (I require minimum C++11), we may need to allocate a temporary array, as we can't write to a string's data. Unfortunately, this requires an extra allocation and copy.
[std::string::data]: http://en.cppreference.com/w/cpp/string/basic_string/data#mw-content-text (std::string::data())
#include <string>
std::string format(const char *fmt, ...)
#ifdef __GNUC__
__attribute__ ((format (printf, 1, 2)))
#endif
;
// Implementation
#include <cstdio>
#include <cstdarg>
#if __cplusplus < 201703L
#include <memory>
#endif
std::string format(const char *fmt, ...)
{
char buf[256];
va_list args;
va_start(args, fmt);
const auto r = std::vsnprintf(buf, sizeof buf, fmt, args);
va_end(args);
if (r < 0)
// conversion failed
return {};
const size_t len = r;
if (len < sizeof buf)
// we fit in the buffer
return { buf, len };
#if __cplusplus >= 201703L
// C++17: Create a string and write to its underlying array
std::string s(len, '0円');
va_start(args, fmt);
std::vsnprintf(s.data(), len+1, fmt, args);
va_end(args);
return s;
#else
// C++11 or C++14: We need to allocate scratch memory
auto vbuf = std::unique_ptr<char[]>(new char[len+1]);
va_start(args, fmt);
std::vsnprintf(vbuf.get(), len+1, fmt, args);
va_end(args);
return { vbuf.get(), len };
#endif
}
// Test program
#include <iostream>
int main()
{
std::clog << "'" << format("a")
<< "'" << std::endl;
std::clog << "'" << format("%#x", 1337)
<< "'" << std::endl;
std::clog << "'" << format("--%c--", 0) // an embedded NUL
<< "'" << std::endl;
std::clog << "'" << format("%300s++%6.2f", "**", 0.0).substr(300)
<< "'" << std::endl;
}
void provoke_warnings()
{
// warning: zero-length gnu_printf format string
// [-Wformat-zero-length]
std::clog << "'" << format("") << "'" << std::endl;
// warning: format ‘%c’ expects argument of type ‘int’, but
// argument 2 has type ‘const char*’ [-Wformat=]
std::clog << "'" << format("%c", "bar") << "'" << std::endl;
}
I've compiled the code with both C++17 and C++11 compilers, and verified them both under Valgrind using this test program.
I'd welcome any comments on the code itself or on my testing.