diff options
author | Yuri Wiitala <miu@chromium.org> | 2020-10-26 12:30:13 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-10-26 20:44:44 +0000 |
commit | 2837c9f0c416803cbab994f1fe10296d2a132971 (patch) | |
tree | dae3d9a0167f1bc6a56ee65be4f329ce086b73ed /util | |
parent | 83e50af3e0bd628e7307e67e0ae1c6a611b8d7bb (diff) | |
download | openscreen-2837c9f0c416803cbab994f1fe10296d2a132971.tar.gz |
Add StringPrintf() wrapper utility.
Adds an efficient wrapper around std::snprintf() that returns the
formatted result in a std::string.
Background: The issue of either 1) wanting to use absl::StrFormat()
without bloating our binary sizes, or 2) manually writing char[] buffer
code around std::snprintf() has come up several times over the past year
or so. So, this new utility is about reducing friction and improving
future developer velocity.
Change-Id: Ie898f91575852b06d59844dd564a0da03df1f7ed
Reviewed-on: https://chromium-review.googlesource.com/c/openscreen/+/2487768
Commit-Queue: Yuri Wiitala <miu@chromium.org>
Reviewed-by: Yuri Wiitala <miu@chromium.org>
Reviewed-by: Brandon Tolsch <btolsch@chromium.org>
Diffstat (limited to 'util')
-rw-r--r-- | util/stringprintf.cc | 23 | ||||
-rw-r--r-- | util/stringprintf.h | 13 | ||||
-rw-r--r-- | util/stringprintf_unittest.cc | 9 |
3 files changed, 45 insertions, 0 deletions
diff --git a/util/stringprintf.cc b/util/stringprintf.cc index 452c3282..2d9bba22 100644 --- a/util/stringprintf.cc +++ b/util/stringprintf.cc @@ -4,11 +4,34 @@ #include "util/stringprintf.h" +#include <cstdarg> +#include <cstdio> #include <iomanip> #include <sstream> +#include "util/osp_logging.h" + namespace openscreen { +std::string StringPrintf(const char* format, ...) { + va_list vlist; + va_start(vlist, format); + const int length = std::vsnprintf(nullptr, 0, format, vlist); + OSP_CHECK_GE(length, 0) << "Invalid format string: " << format; + va_end(vlist); + + std::string result(length, '\0'); + // Note: There's no need to add one for the extra terminating NUL char since + // the standard, since C++11, requires that "data() + size() points to [the + // NUL terminator]". Thus, std::vsnprintf() will write the NUL to a valid + // memory location. + va_start(vlist, format); + std::vsnprintf(&result[0], length + 1, format, vlist); + va_end(vlist); + + return result; +} + std::string HexEncode(absl::Span<const uint8_t> bytes) { std::ostringstream hex_dump; hex_dump << std::setfill('0') << std::hex; diff --git a/util/stringprintf.h b/util/stringprintf.h index df24ac04..0e7984e1 100644 --- a/util/stringprintf.h +++ b/util/stringprintf.h @@ -14,6 +14,19 @@ namespace openscreen { +// Enable compile-time checking of the printf format argument, if available. +#if defined(__GNUC__) || defined(__clang__) +#define OSP_CHECK_PRINTF_ARGS(format_param, dots_param) \ + __attribute__((format(printf, format_param, dots_param))) +#else +#define OSP_CHECK_PRINTF_ARGS(format_param, dots_param) +#endif + +// Returns a std::string containing the results of a std::sprintf() call. This +// is an efficient, zero-copy wrapper. +[[nodiscard]] std::string StringPrintf(const char* format, ...) + OSP_CHECK_PRINTF_ARGS(1, 2); + template <typename It> void PrettyPrintAsciiHex(std::ostream& os, It first, It last) { auto it = first; diff --git a/util/stringprintf_unittest.cc b/util/stringprintf_unittest.cc index df0d60f8..e37e7cb6 100644 --- a/util/stringprintf_unittest.cc +++ b/util/stringprintf_unittest.cc @@ -9,6 +9,15 @@ namespace openscreen { namespace { +TEST(StringPrintf, ProducesFormattedStrings) { + EXPECT_EQ("no args", StringPrintf("no args")); + EXPECT_EQ("", StringPrintf("%s", "")); + EXPECT_EQ("42", StringPrintf("%d", 42)); + EXPECT_EQ( + "The result of foo(1, 2) looks good!", + StringPrintf("The result of foo(%d, %d) looks %s%c", 1, 2, "good", '!')); +} + TEST(HexEncode, ProducesEmptyStringFromEmptyByteArray) { const uint8_t kSomeMemoryLocation = 0; EXPECT_EQ("", HexEncode(absl::Span<const uint8_t>(&kSomeMemoryLocation, 0))); |