Skip to main content
Code Review

Return to Question

Tweeted twitter.com/StackCodeReview/status/962155769016868864
added 120 characters in body
Source Link
Toby Speight
  • 87.7k
  • 14
  • 104
  • 325

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

Source Link
Toby Speight
  • 87.7k
  • 14
  • 104
  • 325

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.

lang-cpp

AltStyle によって変換されたページ (->オリジナル) /