From: Adam Simpkins Date: Sun, 28 Sep 2014 01:08:34 +0000 (-0700) Subject: add stringVPrintf() and stringVAppendf() X-Git-Tag: v0.22.0~306 X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=commitdiff_plain;h=0606460a10e0e41f9be09552d771ea1ee327f8ee add stringVPrintf() and stringVAppendf() Summary: This adds versions of stringPrintf() and stringAppendf() that accept va_list arguments. This also fixes the existing stringPrintf() functions to properly call va_end() even when an exception is thrown in stringPrintfImpl(). Test Plan: Included new unit tests for stringVPrintf() and stringVAppendf(). Reviewed By: davejwatson@fb.com Subscribers: trunkagent, doug, net-systems@, exa, njormrod FB internal diff: D1583675 --- diff --git a/folly/String.cpp b/folly/String.cpp index 4314d960..9ccd0928 100644 --- a/folly/String.cpp +++ b/folly/String.cpp @@ -15,7 +15,9 @@ */ #include + #include +#include #include #include @@ -73,6 +75,15 @@ inline void stringPrintfImpl(std::string& output, const char* format, } // anon namespace std::string stringPrintf(const char* format, ...) { + va_list ap; + va_start(ap, format); + SCOPE_EXIT { + va_end(ap); + }; + return stringVPrintf(format, ap); +} + +std::string stringVPrintf(const char* format, va_list ap) { // snprintf will tell us how large the output buffer should be, but // we then have to call it a second time, which is costly. By // guestimating the final size, we avoid the double snprintf in many @@ -85,10 +96,7 @@ std::string stringPrintf(const char* format, ...) { std::string ret(std::max(32UL, strlen(format) * 2), '\0'); ret.resize(0); - va_list ap; - va_start(ap, format); stringPrintfImpl(ret, format, ap); - va_end(ap); return ret; } @@ -97,17 +105,31 @@ std::string stringPrintf(const char* format, ...) { std::string& stringAppendf(std::string* output, const char* format, ...) { va_list ap; va_start(ap, format); + SCOPE_EXIT { + va_end(ap); + }; + return stringVAppendf(output, format, ap); +} + +std::string& stringVAppendf(std::string* output, + const char* format, + va_list ap) { stringPrintfImpl(*output, format, ap); - va_end(ap); return *output; } void stringPrintf(std::string* output, const char* format, ...) { - output->clear(); va_list ap; va_start(ap, format); + SCOPE_EXIT { + va_end(ap); + }; + return stringVPrintf(output, format, ap); +} + +void stringVPrintf(std::string* output, const char* format, va_list ap) { + output->clear(); stringPrintfImpl(*output, format, ap); - va_end(ap); }; namespace { diff --git a/folly/String.h b/folly/String.h index 14fda430..efbda2f1 100644 --- a/folly/String.h +++ b/folly/String.h @@ -186,6 +186,16 @@ std::string& stringAppendf(std::string* output, FOLLY_PRINTF_FORMAT const char* format, ...) FOLLY_PRINTF_FORMAT_ATTR(2, 3); +/** + * Similar to stringPrintf, but accepts a va_list argument. + * + * As with vsnprintf() itself, the value of ap is undefined after the call. + * These functions do not call va_end() on ap. + */ +std::string stringVPrintf(const char* format, va_list ap); +void stringVPrintf(std::string* out, const char* format, va_list ap); +std::string& stringVAppendf(std::string* out, const char* format, va_list ap); + /** * Backslashify a string, that is, replace non-printable characters * with C-style (but NOT C compliant) "\xHH" encoding. If hex_style diff --git a/folly/test/StringTest.cpp b/folly/test/StringTest.cpp index f2162567..e24f3028 100644 --- a/folly/test/StringTest.cpp +++ b/folly/test/StringTest.cpp @@ -16,6 +16,7 @@ #include +#include #include #include #include @@ -60,6 +61,56 @@ TEST(StringPrintf, Appending) { EXPECT_EQ(s, "abc 123"); } +void vprintfCheck(const char* expected, const char* fmt, ...) { + va_list apOrig; + va_start(apOrig, fmt); + SCOPE_EXIT { + va_end(apOrig); + }; + va_list ap; + va_copy(ap, apOrig); + SCOPE_EXIT { + va_end(ap); + }; + + // Check both APIs for calling stringVPrintf() + EXPECT_EQ(expected, stringVPrintf(fmt, ap)); + va_end(ap); + va_copy(ap, apOrig); + + std::string out; + stringVPrintf(&out, fmt, ap); + va_end(ap); + va_copy(ap, apOrig); + EXPECT_EQ(expected, out); + + // Check stringVAppendf() as well + std::string prefix = "foobar"; + out = prefix; + EXPECT_EQ(prefix + expected, stringVAppendf(&out, fmt, ap)); + va_end(ap); + va_copy(ap, apOrig); +} + +void vprintfError(const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + SCOPE_EXIT { + va_end(ap); + }; + + EXPECT_THROW({stringVPrintf(fmt, ap);}, + std::runtime_error); +} + +TEST(StringPrintf, VPrintf) { + vprintfCheck("foo", "%s", "foo"); + vprintfCheck("long string requiring reallocation 1 2 3 0x12345678", + "%s %s %d %d %d %#x", + "long string", "requiring reallocation", 1, 2, 3, 0x12345678); + vprintfError("bogus%", "foo"); +} + TEST(StringPrintf, VariousSizes) { // Test a wide variety of output sizes for (int i = 0; i < 100; ++i) {