Commit 4460753f authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Anton Likhtarov

Add shorthand functions to Format.h.

Summary:
[Folly] Add shorthand functions to Format.h.

This makes calling code simpler when it does not depend on the raw performance of writing to a stream.

Test Plan:
$ fbconfig -r folly/test
$ fbmake runtests

Reviewed By: tudorb@fb.com

Subscribers: folly@lists, dougw

FB internal diff: D1347353

Tasks: 4391110
parent d82763a6
...@@ -207,6 +207,15 @@ Formatter<false, Args...> format(StringPiece fmt, Args&&... args) { ...@@ -207,6 +207,15 @@ Formatter<false, Args...> format(StringPiece fmt, Args&&... args) {
fmt, std::forward<Args>(args)...); fmt, std::forward<Args>(args)...);
} }
/**
* Like format(), but immediately returns the formatted string instead of an
* intermediate format object.
*/
template <class... Args>
inline std::string sformat(StringPiece fmt, Args&&... args) {
return format(fmt, std::forward<Args>(args)...).str();
}
/** /**
* Create a formatter object from a dynamic format string. * Create a formatter object from a dynamic format string.
* *
...@@ -222,6 +231,15 @@ Formatter<false, Args...> formatChecked(StringPiece fmt, Args&&... args) { ...@@ -222,6 +231,15 @@ Formatter<false, Args...> formatChecked(StringPiece fmt, Args&&... args) {
return f; return f;
} }
/**
* Like formatChecked(), but immediately returns the formatted string instead of
* an intermediate format object.
*/
template <class... Args>
inline std::string sformatChecked(StringPiece fmt, Args&&... args) {
return formatChecked(fmt, std::forward<Args>(args)...).str();
}
/** /**
* Create a formatter object that takes one argument (of container type) * Create a formatter object that takes one argument (of container type)
* and uses that container to get argument values from. * and uses that container to get argument values from.
...@@ -241,6 +259,15 @@ Formatter<true, Container> vformat(StringPiece fmt, Container&& container) { ...@@ -241,6 +259,15 @@ Formatter<true, Container> vformat(StringPiece fmt, Container&& container) {
fmt, std::forward<Container>(container)); fmt, std::forward<Container>(container));
} }
/**
* Like vformat(), but immediately returns the formatted string instead of an
* intermediate format object.
*/
template <class Container>
inline std::string svformat(StringPiece fmt, Container&& container) {
return vformat(fmt, std::forward<Container>(container)).str();
}
/** /**
* Create a formatter object from a dynamic format string. * Create a formatter object from a dynamic format string.
* *
...@@ -257,6 +284,15 @@ Formatter<true, Container> vformatChecked(StringPiece fmt, ...@@ -257,6 +284,15 @@ Formatter<true, Container> vformatChecked(StringPiece fmt,
return f; return f;
} }
/**
* Like vformatChecked(), but immediately returns the formatted string instead
* of an intermediate format object.
*/
template <class Container>
inline std::string svformatChecked(StringPiece fmt, Container&& container) {
return vformatChecked(fmt, std::forward<Container>(container)).str();
}
/** /**
* Wrap a sequence or associative container so that out-of-range lookups * Wrap a sequence or associative container so that out-of-range lookups
* return a default value rather than throwing an exception. * return a default value rather than throwing an exception.
......
...@@ -19,7 +19,9 @@ Here are some code samples to get started: ...@@ -19,7 +19,9 @@ Here are some code samples to get started:
``` Cpp ``` Cpp
using folly::format; using folly::format;
using folly::sformat;
using folly::vformat; using folly::vformat;
using folly::svformat;
// Objects produced by format() can be streamed without creating // Objects produced by format() can be streamed without creating
// an intermediary string; {} yields the next argument using default // an intermediary string; {} yields the next argument using default
...@@ -27,6 +29,10 @@ using folly::vformat; ...@@ -27,6 +29,10 @@ using folly::vformat;
std::cout << format("The answers are {} and {}", 23, 42); std::cout << format("The answers are {} and {}", 23, 42);
// => "The answers are 23 and 42" // => "The answers are 23 and 42"
// If you just want the string, though, you're covered.
std::string result = sformat("The answers are {} and {}", 23, 42);
// => "The answers are 23 and 42"
// To insert a literal '{' or '}', just double it. // To insert a literal '{' or '}', just double it.
std::cout << format("{} {{}} {{{}}}", 23, 42); std::cout << format("{} {{}} {{{}}}", 23, 42);
// => "23 {} {42}" // => "23 {} {42}"
...@@ -54,6 +60,11 @@ std::cout << vformat("The only {what} is {value}", m); ...@@ -54,6 +60,11 @@ std::cout << vformat("The only {what} is {value}", m);
// same as // same as
std::cout << format("The only {0[what]} is {0[value]}", m); std::cout << format("The only {0[what]} is {0[value]}", m);
// => "The only answer is 42" // => "The only answer is 42"
// And if you just want the string,
std::string result = svformat("The only {what} is {value}", m);
// => "The only answer is 42"
std::string result = svformat("The only {0[what]} is {0[value]}", m);
// => "The only answer is 42"
// {} works for vformat too // {} works for vformat too
std::vector<int> v {42, 23}; std::vector<int> v {42, 23};
......
...@@ -27,26 +27,6 @@ ...@@ -27,26 +27,6 @@
using namespace folly; using namespace folly;
template <class... Args>
std::string fstr(StringPiece fmt, Args&&... args) {
return format(fmt, std::forward<Args>(args)...).str();
}
template <class C>
std::string vstr(StringPiece fmt, const C& c) {
return vformat(fmt, c).str();
}
template <class... Args>
std::string fstrChecked(StringPiece fmt, Args&&... args) {
return formatChecked(fmt, std::forward<Args>(args)...).str();
}
template <class C>
std::string vstrChecked(StringPiece fmt, const C& c) {
return vformatChecked(fmt, c).str();
}
template <class Uint> template <class Uint>
void compareOctal(Uint u) { void compareOctal(Uint u) {
char buf1[detail::kMaxOctalLength + 1]; char buf1[detail::kMaxOctalLength + 1];
...@@ -110,97 +90,97 @@ TEST(Format, uintToBinary) { ...@@ -110,97 +90,97 @@ TEST(Format, uintToBinary) {
} }
TEST(Format, Simple) { TEST(Format, Simple) {
EXPECT_EQ("hello", fstr("hello")); EXPECT_EQ("hello", sformat("hello"));
EXPECT_EQ("42", fstr("{}", 42)); EXPECT_EQ("42", sformat("{}", 42));
EXPECT_EQ("42 42", fstr("{0} {0}", 42)); EXPECT_EQ("42 42", sformat("{0} {0}", 42));
EXPECT_EQ("00042 23 42", fstr("{0:05} {1:3} {0:4}", 42, 23)); EXPECT_EQ("00042 23 42", sformat("{0:05} {1:3} {0:4}", 42, 23));
EXPECT_EQ("hello world hello 42", EXPECT_EQ("hello world hello 42",
fstr("{0} {1} {0} {2}", "hello", "world", 42)); sformat("{0} {1} {0} {2}", "hello", "world", 42));
EXPECT_EQ("XXhelloXX", fstr("{:X^9}", "hello")); EXPECT_EQ("XXhelloXX", sformat("{:X^9}", "hello"));
EXPECT_EQ("XXX42XXXX", fstr("{:X^9}", 42)); EXPECT_EQ("XXX42XXXX", sformat("{:X^9}", 42));
EXPECT_EQ("-0xYYYY2a", fstr("{:Y=#9x}", -42)); EXPECT_EQ("-0xYYYY2a", sformat("{:Y=#9x}", -42));
EXPECT_EQ("*", fstr("{}", '*')); EXPECT_EQ("*", sformat("{}", '*'));
EXPECT_EQ("42", fstr("{}", 42)); EXPECT_EQ("42", sformat("{}", 42));
EXPECT_EQ("0042", fstr("{:04}", 42)); EXPECT_EQ("0042", sformat("{:04}", 42));
EXPECT_EQ("hello ", fstr("{:7}", "hello")); EXPECT_EQ("hello ", sformat("{:7}", "hello"));
EXPECT_EQ("hello ", fstr("{:<7}", "hello")); EXPECT_EQ("hello ", sformat("{:<7}", "hello"));
EXPECT_EQ(" hello", fstr("{:>7}", "hello")); EXPECT_EQ(" hello", sformat("{:>7}", "hello"));
std::vector<int> v1 {10, 20, 30}; std::vector<int> v1 {10, 20, 30};
EXPECT_EQ("0020", fstr("{0[1]:04}", v1)); EXPECT_EQ("0020", sformat("{0[1]:04}", v1));
EXPECT_EQ("0020", vstr("{1:04}", v1)); EXPECT_EQ("0020", svformat("{1:04}", v1));
EXPECT_EQ("10 20", vstr("{} {}", v1)); EXPECT_EQ("10 20", svformat("{} {}", v1));
const std::vector<int> v2 = v1; const std::vector<int> v2 = v1;
EXPECT_EQ("0020", fstr("{0[1]:04}", v2)); EXPECT_EQ("0020", sformat("{0[1]:04}", v2));
EXPECT_EQ("0020", vstr("{1:04}", v2)); EXPECT_EQ("0020", svformat("{1:04}", v2));
EXPECT_THROW(fstr("{0[3]:04}", v2), std::out_of_range); EXPECT_THROW(sformat("{0[3]:04}", v2), std::out_of_range);
EXPECT_THROW(vstr("{3:04}", v2), std::out_of_range); EXPECT_THROW(svformat("{3:04}", v2), std::out_of_range);
EXPECT_EQ("0020", fstr("{0[1]:04}", defaulted(v2, 42))); EXPECT_EQ("0020", sformat("{0[1]:04}", defaulted(v2, 42)));
EXPECT_EQ("0020", vstr("{1:04}", defaulted(v2, 42))); EXPECT_EQ("0020", svformat("{1:04}", defaulted(v2, 42)));
EXPECT_EQ("0042", fstr("{0[3]:04}", defaulted(v2, 42))); EXPECT_EQ("0042", sformat("{0[3]:04}", defaulted(v2, 42)));
EXPECT_EQ("0042", vstr("{3:04}", defaulted(v2, 42))); EXPECT_EQ("0042", svformat("{3:04}", defaulted(v2, 42)));
const int p[] = {10, 20, 30}; const int p[] = {10, 20, 30};
const int* q = p; const int* q = p;
EXPECT_EQ("0020", fstr("{0[1]:04}", p)); EXPECT_EQ("0020", sformat("{0[1]:04}", p));
EXPECT_EQ("0020", vstr("{1:04}", p)); EXPECT_EQ("0020", svformat("{1:04}", p));
EXPECT_EQ("0020", fstr("{0[1]:04}", q)); EXPECT_EQ("0020", sformat("{0[1]:04}", q));
EXPECT_EQ("0020", vstr("{1:04}", q)); EXPECT_EQ("0020", svformat("{1:04}", q));
EXPECT_NE("", fstr("{}", q)); EXPECT_NE("", sformat("{}", q));
EXPECT_EQ("0x", fstr("{}", p).substr(0, 2)); EXPECT_EQ("0x", sformat("{}", p).substr(0, 2));
EXPECT_EQ("10", vstr("{}", p)); EXPECT_EQ("10", svformat("{}", p));
EXPECT_EQ("0x", fstr("{}", q).substr(0, 2)); EXPECT_EQ("0x", sformat("{}", q).substr(0, 2));
EXPECT_EQ("10", vstr("{}", q)); EXPECT_EQ("10", svformat("{}", q));
q = nullptr; q = nullptr;
EXPECT_EQ("(null)", fstr("{}", q)); EXPECT_EQ("(null)", sformat("{}", q));
std::map<int, std::string> m { {10, "hello"}, {20, "world"} }; std::map<int, std::string> m { {10, "hello"}, {20, "world"} };
EXPECT_EQ("worldXX", fstr("{[20]:X<7}", m)); EXPECT_EQ("worldXX", sformat("{[20]:X<7}", m));
EXPECT_EQ("worldXX", vstr("{20:X<7}", m)); EXPECT_EQ("worldXX", svformat("{20:X<7}", m));
EXPECT_THROW(fstr("{[42]:X<7}", m), std::out_of_range); EXPECT_THROW(sformat("{[42]:X<7}", m), std::out_of_range);
EXPECT_THROW(vstr("{42:X<7}", m), std::out_of_range); EXPECT_THROW(svformat("{42:X<7}", m), std::out_of_range);
EXPECT_EQ("worldXX", fstr("{[20]:X<7}", defaulted(m, "meow"))); EXPECT_EQ("worldXX", sformat("{[20]:X<7}", defaulted(m, "meow")));
EXPECT_EQ("worldXX", vstr("{20:X<7}", defaulted(m, "meow"))); EXPECT_EQ("worldXX", svformat("{20:X<7}", defaulted(m, "meow")));
EXPECT_EQ("meowXXX", fstr("{[42]:X<7}", defaulted(m, "meow"))); EXPECT_EQ("meowXXX", sformat("{[42]:X<7}", defaulted(m, "meow")));
EXPECT_EQ("meowXXX", vstr("{42:X<7}", defaulted(m, "meow"))); EXPECT_EQ("meowXXX", svformat("{42:X<7}", defaulted(m, "meow")));
std::map<std::string, std::string> m2 { {"hello", "world"} }; std::map<std::string, std::string> m2 { {"hello", "world"} };
EXPECT_EQ("worldXX", fstr("{[hello]:X<7}", m2)); EXPECT_EQ("worldXX", sformat("{[hello]:X<7}", m2));
EXPECT_EQ("worldXX", vstr("{hello:X<7}", m2)); EXPECT_EQ("worldXX", svformat("{hello:X<7}", m2));
EXPECT_THROW(fstr("{[none]:X<7}", m2), std::out_of_range); EXPECT_THROW(sformat("{[none]:X<7}", m2), std::out_of_range);
EXPECT_THROW(vstr("{none:X<7}", m2), std::out_of_range); EXPECT_THROW(svformat("{none:X<7}", m2), std::out_of_range);
EXPECT_EQ("worldXX", fstr("{[hello]:X<7}", defaulted(m2, "meow"))); EXPECT_EQ("worldXX", sformat("{[hello]:X<7}", defaulted(m2, "meow")));
EXPECT_EQ("worldXX", vstr("{hello:X<7}", defaulted(m2, "meow"))); EXPECT_EQ("worldXX", svformat("{hello:X<7}", defaulted(m2, "meow")));
EXPECT_EQ("meowXXX", fstr("{[none]:X<7}", defaulted(m2, "meow"))); EXPECT_EQ("meowXXX", sformat("{[none]:X<7}", defaulted(m2, "meow")));
EXPECT_EQ("meowXXX", vstr("{none:X<7}", defaulted(m2, "meow"))); EXPECT_EQ("meowXXX", svformat("{none:X<7}", defaulted(m2, "meow")));
// Test indexing in strings // Test indexing in strings
EXPECT_EQ("61 62", fstr("{0[0]:x} {0[1]:x}", "abcde")); EXPECT_EQ("61 62", sformat("{0[0]:x} {0[1]:x}", "abcde"));
EXPECT_EQ("61 62", vstr("{0:x} {1:x}", "abcde")); EXPECT_EQ("61 62", svformat("{0:x} {1:x}", "abcde"));
EXPECT_EQ("61 62", fstr("{0[0]:x} {0[1]:x}", std::string("abcde"))); EXPECT_EQ("61 62", sformat("{0[0]:x} {0[1]:x}", std::string("abcde")));
EXPECT_EQ("61 62", vstr("{0:x} {1:x}", std::string("abcde"))); EXPECT_EQ("61 62", svformat("{0:x} {1:x}", std::string("abcde")));
// Test booleans // Test booleans
EXPECT_EQ("true", fstr("{}", true)); EXPECT_EQ("true", sformat("{}", true));
EXPECT_EQ("1", fstr("{:d}", true)); EXPECT_EQ("1", sformat("{:d}", true));
EXPECT_EQ("false", fstr("{}", false)); EXPECT_EQ("false", sformat("{}", false));
EXPECT_EQ("0", fstr("{:d}", false)); EXPECT_EQ("0", sformat("{:d}", false));
// Test pairs // Test pairs
{ {
std::pair<int, std::string> p {42, "hello"}; std::pair<int, std::string> p {42, "hello"};
EXPECT_EQ(" 42 hello ", fstr("{0[0]:6} {0[1]:6}", p)); EXPECT_EQ(" 42 hello ", sformat("{0[0]:6} {0[1]:6}", p));
EXPECT_EQ(" 42 hello ", vstr("{:6} {:6}", p)); EXPECT_EQ(" 42 hello ", svformat("{:6} {:6}", p));
} }
// Test tuples // Test tuples
{ {
std::tuple<int, std::string, int> t { 42, "hello", 23 }; std::tuple<int, std::string, int> t { 42, "hello", 23 };
EXPECT_EQ(" 42 hello 23", fstr("{0[0]:6} {0[1]:6} {0[2]:6}", t)); EXPECT_EQ(" 42 hello 23", sformat("{0[0]:6} {0[1]:6} {0[2]:6}", t));
EXPECT_EQ(" 42 hello 23", vstr("{:6} {:6} {:6}", t)); EXPECT_EQ(" 42 hello 23", svformat("{:6} {:6} {:6}", t));
} }
// Test writing to stream // Test writing to stream
...@@ -237,26 +217,26 @@ TEST(Format, Simple) { ...@@ -237,26 +217,26 @@ TEST(Format, Simple) {
TEST(Format, Float) { TEST(Format, Float) {
double d = 1; double d = 1;
EXPECT_EQ("1", fstr("{}", 1.0)); EXPECT_EQ("1", sformat("{}", 1.0));
EXPECT_EQ("0.1", fstr("{}", 0.1)); EXPECT_EQ("0.1", sformat("{}", 0.1));
EXPECT_EQ("0.01", fstr("{}", 0.01)); EXPECT_EQ("0.01", sformat("{}", 0.01));
EXPECT_EQ("0.001", fstr("{}", 0.001)); EXPECT_EQ("0.001", sformat("{}", 0.001));
EXPECT_EQ("0.0001", fstr("{}", 0.0001)); EXPECT_EQ("0.0001", sformat("{}", 0.0001));
EXPECT_EQ("1e-5", fstr("{}", 0.00001)); EXPECT_EQ("1e-5", sformat("{}", 0.00001));
EXPECT_EQ("1e-6", fstr("{}", 0.000001)); EXPECT_EQ("1e-6", sformat("{}", 0.000001));
EXPECT_EQ("10", fstr("{}", 10.0)); EXPECT_EQ("10", sformat("{}", 10.0));
EXPECT_EQ("100", fstr("{}", 100.0)); EXPECT_EQ("100", sformat("{}", 100.0));
EXPECT_EQ("1000", fstr("{}", 1000.0)); EXPECT_EQ("1000", sformat("{}", 1000.0));
EXPECT_EQ("10000", fstr("{}", 10000.0)); EXPECT_EQ("10000", sformat("{}", 10000.0));
EXPECT_EQ("100000", fstr("{}", 100000.0)); EXPECT_EQ("100000", sformat("{}", 100000.0));
EXPECT_EQ("1e+6", fstr("{}", 1000000.0)); EXPECT_EQ("1e+6", sformat("{}", 1000000.0));
EXPECT_EQ("1e+7", fstr("{}", 10000000.0)); EXPECT_EQ("1e+7", sformat("{}", 10000000.0));
EXPECT_EQ("1.00", fstr("{:.2f}", 1.0)); EXPECT_EQ("1.00", sformat("{:.2f}", 1.0));
EXPECT_EQ("0.10", fstr("{:.2f}", 0.1)); EXPECT_EQ("0.10", sformat("{:.2f}", 0.1));
EXPECT_EQ("0.01", fstr("{:.2f}", 0.01)); EXPECT_EQ("0.01", sformat("{:.2f}", 0.01));
EXPECT_EQ("0.00", fstr("{:.2f}", 0.001)); EXPECT_EQ("0.00", sformat("{:.2f}", 0.001));
} }
TEST(Format, MultiLevel) { TEST(Format, MultiLevel) {
...@@ -266,7 +246,7 @@ TEST(Format, MultiLevel) { ...@@ -266,7 +246,7 @@ TEST(Format, MultiLevel) {
}, },
}; };
EXPECT_EQ("world", fstr("{[0.hello]}", v)); EXPECT_EQ("world", sformat("{[0.hello]}", v));
} }
TEST(Format, dynamic) { TEST(Format, dynamic) {
...@@ -277,24 +257,24 @@ TEST(Format, dynamic) { ...@@ -277,24 +257,24 @@ TEST(Format, dynamic) {
" \"y\": {\"a\" : 42}\n" " \"y\": {\"a\" : 42}\n"
"}"); "}");
EXPECT_EQ("world", fstr("{0[hello]}", dyn)); EXPECT_EQ("world", sformat("{0[hello]}", dyn));
EXPECT_THROW(fstr("{0[none]}", dyn), std::out_of_range); EXPECT_THROW(sformat("{0[none]}", dyn), std::out_of_range);
EXPECT_EQ("world", fstr("{0[hello]}", defaulted(dyn, "meow"))); EXPECT_EQ("world", sformat("{0[hello]}", defaulted(dyn, "meow")));
EXPECT_EQ("meow", fstr("{0[none]}", defaulted(dyn, "meow"))); EXPECT_EQ("meow", sformat("{0[none]}", defaulted(dyn, "meow")));
EXPECT_EQ("20", fstr("{0[x.0]}", dyn)); EXPECT_EQ("20", sformat("{0[x.0]}", dyn));
EXPECT_THROW(fstr("{0[x.2]}", dyn), std::out_of_range); EXPECT_THROW(sformat("{0[x.2]}", dyn), std::out_of_range);
// No support for "deep" defaulting (dyn["x"] is not defaulted) // No support for "deep" defaulting (dyn["x"] is not defaulted)
auto v = dyn.at("x"); auto v = dyn.at("x");
EXPECT_EQ("20", fstr("{0[0]}", v)); EXPECT_EQ("20", sformat("{0[0]}", v));
EXPECT_THROW(fstr("{0[2]}", v), std::out_of_range); EXPECT_THROW(sformat("{0[2]}", v), std::out_of_range);
EXPECT_EQ("20", fstr("{0[0]}", defaulted(v, 42))); EXPECT_EQ("20", sformat("{0[0]}", defaulted(v, 42)));
EXPECT_EQ("42", fstr("{0[2]}", defaulted(v, 42))); EXPECT_EQ("42", sformat("{0[2]}", defaulted(v, 42)));
EXPECT_EQ("42", fstr("{0[y.a]}", dyn)); EXPECT_EQ("42", sformat("{0[y.a]}", dyn));
EXPECT_EQ("(null)", fstr("{}", dynamic(nullptr))); EXPECT_EQ("(null)", sformat("{}", dynamic(nullptr)));
} }
namespace { namespace {
...@@ -328,13 +308,13 @@ template <> class FormatValue<KeyValue> { ...@@ -328,13 +308,13 @@ template <> class FormatValue<KeyValue> {
TEST(Format, Custom) { TEST(Format, Custom) {
KeyValue kv { "hello", 42 }; KeyValue kv { "hello", 42 };
EXPECT_EQ("<key=hello, value=42>", fstr("{}", kv)); EXPECT_EQ("<key=hello, value=42>", sformat("{}", kv));
EXPECT_EQ("<key=hello, value=42>", fstr("{:10}", kv)); EXPECT_EQ("<key=hello, value=42>", sformat("{:10}", kv));
EXPECT_EQ("<key=hello", fstr("{:.10}", kv)); EXPECT_EQ("<key=hello", sformat("{:.10}", kv));
EXPECT_EQ("<key=hello, value=42>XX", fstr("{:X<23}", kv)); EXPECT_EQ("<key=hello, value=42>XX", sformat("{:X<23}", kv));
EXPECT_EQ("XX<key=hello, value=42>", fstr("{:X>23}", kv)); EXPECT_EQ("XX<key=hello, value=42>", sformat("{:X>23}", kv));
EXPECT_EQ("<key=hello, value=42>", fstr("{0[0]}", &kv)); EXPECT_EQ("<key=hello, value=42>", sformat("{0[0]}", &kv));
EXPECT_NE("", fstr("{}", &kv)); EXPECT_NE("", sformat("{}", &kv));
} }
namespace { namespace {
...@@ -347,56 +327,56 @@ struct Opaque { ...@@ -347,56 +327,56 @@ struct Opaque {
TEST(Format, Unformatted) { TEST(Format, Unformatted) {
Opaque o; Opaque o;
EXPECT_NE("", fstr("{}", &o)); EXPECT_NE("", sformat("{}", &o));
EXPECT_DEATH(fstr("{0[0]}", &o), "No formatter available for this type"); EXPECT_DEATH(sformat("{0[0]}", &o), "No formatter available for this type");
EXPECT_THROW(fstrChecked("{0[0]}", &o), std::invalid_argument); EXPECT_THROW(sformatChecked("{0[0]}", &o), std::invalid_argument);
} }
TEST(Format, Nested) { TEST(Format, Nested) {
EXPECT_EQ("1 2 3 4", fstr("{} {} {}", 1, 2, format("{} {}", 3, 4))); EXPECT_EQ("1 2 3 4", sformat("{} {} {}", 1, 2, format("{} {}", 3, 4)));
// //
// not copyable, must hold temporary in scope instead. // not copyable, must hold temporary in scope instead.
auto&& saved = format("{} {}", 3, 4); auto&& saved = format("{} {}", 3, 4);
EXPECT_EQ("1 2 3 4", fstr("{} {} {}", 1, 2, saved)); EXPECT_EQ("1 2 3 4", sformat("{} {} {}", 1, 2, saved));
} }
TEST(Format, OutOfBounds) { TEST(Format, OutOfBounds) {
std::vector<int> ints{1, 2, 3, 4, 5}; std::vector<int> ints{1, 2, 3, 4, 5};
EXPECT_EQ("1 3 5", fstr("{0[0]} {0[2]} {0[4]}", ints)); EXPECT_EQ("1 3 5", sformat("{0[0]} {0[2]} {0[4]}", ints));
EXPECT_THROW(fstr("{[5]}", ints), std::out_of_range); EXPECT_THROW(sformat("{[5]}", ints), std::out_of_range);
EXPECT_THROW(fstrChecked("{[5]}", ints), std::out_of_range); EXPECT_THROW(sformatChecked("{[5]}", ints), std::out_of_range);
std::map<std::string, int> map{{"hello", 0}, {"world", 1}}; std::map<std::string, int> map{{"hello", 0}, {"world", 1}};
EXPECT_EQ("hello = 0", fstr("hello = {[hello]}", map)); EXPECT_EQ("hello = 0", sformat("hello = {[hello]}", map));
EXPECT_THROW(fstr("{[nope]}", map), std::out_of_range); EXPECT_THROW(sformat("{[nope]}", map), std::out_of_range);
EXPECT_THROW(vstr("{nope}", map), std::out_of_range); EXPECT_THROW(svformat("{nope}", map), std::out_of_range);
EXPECT_THROW(vstrChecked("{nope}", map), std::out_of_range); EXPECT_THROW(svformatChecked("{nope}", map), std::out_of_range);
} }
TEST(Format, BogusFormatString) { TEST(Format, BogusFormatString) {
// format() will crash the program if the format string is invalid. // format() will crash the program if the format string is invalid.
EXPECT_DEATH(fstr("}"), "single '}' in format string"); EXPECT_DEATH(sformat("}"), "single '}' in format string");
EXPECT_DEATH(fstr("foo}bar"), "single '}' in format string"); EXPECT_DEATH(sformat("foo}bar"), "single '}' in format string");
EXPECT_DEATH(fstr("foo{bar"), "missing ending '}'"); EXPECT_DEATH(sformat("foo{bar"), "missing ending '}'");
EXPECT_DEATH(fstr("{[test]"), "missing ending '}'"); EXPECT_DEATH(sformat("{[test]"), "missing ending '}'");
EXPECT_DEATH(fstr("{-1.3}"), "argument index must be non-negative"); EXPECT_DEATH(sformat("{-1.3}"), "argument index must be non-negative");
EXPECT_DEATH(fstr("{1.3}", 0, 1, 2), "index not allowed"); EXPECT_DEATH(sformat("{1.3}", 0, 1, 2), "index not allowed");
EXPECT_DEATH(fstr("{0} {} {1}", 0, 1, 2), EXPECT_DEATH(sformat("{0} {} {1}", 0, 1, 2),
"may not have both default and explicit arg indexes"); "may not have both default and explicit arg indexes");
// formatChecked() should throw exceptions rather than crashing the program // formatChecked() should throw exceptions rather than crashing the program
EXPECT_THROW(fstrChecked("}"), std::invalid_argument); EXPECT_THROW(sformatChecked("}"), std::invalid_argument);
EXPECT_THROW(fstrChecked("foo}bar"), std::invalid_argument); EXPECT_THROW(sformatChecked("foo}bar"), std::invalid_argument);
EXPECT_THROW(fstrChecked("foo{bar"), std::invalid_argument); EXPECT_THROW(sformatChecked("foo{bar"), std::invalid_argument);
EXPECT_THROW(fstrChecked("{[test]"), std::invalid_argument); EXPECT_THROW(sformatChecked("{[test]"), std::invalid_argument);
EXPECT_THROW(fstrChecked("{-1.3}"), std::invalid_argument); EXPECT_THROW(sformatChecked("{-1.3}"), std::invalid_argument);
EXPECT_THROW(fstrChecked("{1.3}", 0, 1, 2), std::invalid_argument); EXPECT_THROW(sformatChecked("{1.3}", 0, 1, 2), std::invalid_argument);
EXPECT_THROW(fstrChecked("{0} {} {1}", 0, 1, 2), std::invalid_argument); EXPECT_THROW(sformatChecked("{0} {} {1}", 0, 1, 2), std::invalid_argument);
// This one fails in detail::enforceWhitespace(), which throws // This one fails in detail::enforceWhitespace(), which throws
// std::range_error // std::range_error
EXPECT_DEATH(fstr("{0[test}"), "Non-whitespace: \\["); EXPECT_DEATH(sformat("{0[test}"), "Non-whitespace: \\[");
EXPECT_THROW(fstrChecked("{0[test}"), std::exception); EXPECT_THROW(sformatChecked("{0[test}"), std::exception);
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
......
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