Commit 0606460a authored by Adam Simpkins's avatar Adam Simpkins Committed by Andrii Grynenko

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
parent edbdc3d4
...@@ -15,7 +15,9 @@ ...@@ -15,7 +15,9 @@
*/ */
#include <folly/String.h> #include <folly/String.h>
#include <folly/Format.h> #include <folly/Format.h>
#include <folly/ScopeGuard.h>
#include <cerrno> #include <cerrno>
#include <cstdarg> #include <cstdarg>
...@@ -73,6 +75,15 @@ inline void stringPrintfImpl(std::string& output, const char* format, ...@@ -73,6 +75,15 @@ inline void stringPrintfImpl(std::string& output, const char* format,
} // anon namespace } // anon namespace
std::string stringPrintf(const char* format, ...) { 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 // 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 // we then have to call it a second time, which is costly. By
// guestimating the final size, we avoid the double snprintf in many // guestimating the final size, we avoid the double snprintf in many
...@@ -85,10 +96,7 @@ std::string stringPrintf(const char* format, ...) { ...@@ -85,10 +96,7 @@ std::string stringPrintf(const char* format, ...) {
std::string ret(std::max(32UL, strlen(format) * 2), '\0'); std::string ret(std::max(32UL, strlen(format) * 2), '\0');
ret.resize(0); ret.resize(0);
va_list ap;
va_start(ap, format);
stringPrintfImpl(ret, format, ap); stringPrintfImpl(ret, format, ap);
va_end(ap);
return ret; return ret;
} }
...@@ -97,17 +105,31 @@ std::string stringPrintf(const char* format, ...) { ...@@ -97,17 +105,31 @@ std::string stringPrintf(const char* format, ...) {
std::string& stringAppendf(std::string* output, const char* format, ...) { std::string& stringAppendf(std::string* output, const char* format, ...) {
va_list ap; va_list ap;
va_start(ap, format); 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); stringPrintfImpl(*output, format, ap);
va_end(ap);
return *output; return *output;
} }
void stringPrintf(std::string* output, const char* format, ...) { void stringPrintf(std::string* output, const char* format, ...) {
output->clear();
va_list ap; va_list ap;
va_start(ap, format); 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); stringPrintfImpl(*output, format, ap);
va_end(ap);
}; };
namespace { namespace {
......
...@@ -186,6 +186,16 @@ std::string& stringAppendf(std::string* output, ...@@ -186,6 +186,16 @@ std::string& stringAppendf(std::string* output,
FOLLY_PRINTF_FORMAT const char* format, ...) FOLLY_PRINTF_FORMAT const char* format, ...)
FOLLY_PRINTF_FORMAT_ATTR(2, 3); 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 * Backslashify a string, that is, replace non-printable characters
* with C-style (but NOT C compliant) "\xHH" encoding. If hex_style * with C-style (but NOT C compliant) "\xHH" encoding. If hex_style
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <folly/String.h> #include <folly/String.h>
#include <cstdarg>
#include <random> #include <random>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <gtest/gtest.h> #include <gtest/gtest.h>
...@@ -60,6 +61,56 @@ TEST(StringPrintf, Appending) { ...@@ -60,6 +61,56 @@ TEST(StringPrintf, Appending) {
EXPECT_EQ(s, "abc 123"); 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(StringPrintf, VariousSizes) {
// Test a wide variety of output sizes // Test a wide variety of output sizes
for (int i = 0; i < 100; ++i) { for (int i = 0; i < 100; ++i) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment