diff --git a/format.cc b/format.cc index 38ef44bb..7eef716c 100644 --- a/format.cc +++ b/format.cc @@ -23,9 +23,15 @@ enum { PLUS_FLAG = 1, ZERO_FLAG = 2, HEX_PREFIX_FLAG = 4 }; // FormatError reporting unmatched '{'. The idea is that unmatched '{' // should override other errors. void ReportError(const char *s, const std::string &message) { - while (*s && *s != '}') - ++s; - throw fmt::FormatError(*s ? message : std::string("unmatched '{' in format")); + for (int num_open_braces = 1; *s; ++s) { + if (*s == '{') { + ++num_open_braces; + } else if (*s == '}') { + if (--num_open_braces == 0) + throw fmt::FormatError(message); + } + } + throw fmt::FormatError("unmatched '{' in format"); } void ReportUnknownType(char code, const char *type) { @@ -225,8 +231,15 @@ void fmt::Formatter::Format() { const char *start = format_; const char *s = start; while (*s) { - if (*s++ != '{') continue; - // TODO: handle escape sequence + char c = *s++; + if (c != '{' && c != '}') continue; + if (*s == c) { + buffer_.insert(buffer_.end(), start, s); + start = ++s; + continue; + } + if (c == '}') + throw FormatError("unmatched '}' in format"); buffer_.insert(buffer_.end(), start, s - 1); // Parse argument index. diff --git a/format.h b/format.h index d085231a..e6aebf46 100644 --- a/format.h +++ b/format.h @@ -78,7 +78,7 @@ class Formatter { : type(CUSTOM), custom_value(value), format(f) {} }; - std::vector args_; + std::vector args_; // Format arguments. const char *format_; // Format string. @@ -94,7 +94,7 @@ class Formatter { template void FormatInt(T value, unsigned flags, int width, char type); - // Formats a floating point number. + // Formats a floating point number (double or long double). template void FormatDouble( T value, unsigned flags, int width, int precision, char type); @@ -127,6 +127,16 @@ class ArgFormatter { private: friend class Formatter; + // This method is private to disallow formatting of arbitrary pointers. + // If you want to output a pointer cast it to void*. Do not implement! + template + ArgFormatter &operator<<(const T *value); + + // This method is private to disallow formatting of wide characters. + // If you want to output a wide character cast it to integer type. + // Do not implement! + ArgFormatter &operator<<(wchar_t value); + protected: mutable Formatter *formatter_; @@ -211,17 +221,6 @@ class ArgFormatter { return *this; } - // This method contains a deliberate error to disallow formatting - // arbitrary pointers. If you want to output a pointer cast it to void*. - template - ArgFormatter &operator<<(const T *value) { - "Formatting arbitrary pointers is not allowed" = value; - } - - // This method is not implemented intentionally to disallow formatting - // wide characters. - ArgFormatter &operator<<(wchar_t value); - template ArgFormatter &operator<<(T *value) { const T *const_value = value; diff --git a/format_test.cc b/format_test.cc index f173c776..e6c7c611 100644 --- a/format_test.cc +++ b/format_test.cc @@ -66,8 +66,28 @@ class TestString { } }; +TEST(FormatterTest, Escape) { + EXPECT_EQ("{", str(Format("{{"))); + EXPECT_EQ("before {", str(Format("before {{"))); + EXPECT_EQ("{ after", str(Format("{{ after"))); + EXPECT_EQ("before { after", str(Format("before {{ after"))); + + EXPECT_EQ("}", str(Format("}}"))); + EXPECT_EQ("before }", str(Format("before }}"))); + EXPECT_EQ("} after", str(Format("}} after"))); + EXPECT_EQ("before } after", str(Format("before }} after"))); + + EXPECT_EQ("{}", str(Format("{{}}"))); + EXPECT_EQ("{42}", str(Format("{{{0}}}") << 42)); +} + +TEST(FormatterTest, UnmatchedBraces) { + EXPECT_THROW_MSG(Format("{"), FormatError, "unmatched '{' in format"); + EXPECT_THROW_MSG(Format("}"), FormatError, "unmatched '}' in format"); + EXPECT_THROW_MSG(Format("{0{}"), FormatError, "unmatched '{' in format"); +} + TEST(FormatterTest, NoArgs) { - EXPECT_EQ("abracadabra", str(Format("{0}{1}{0}") << "abra" << "cad")); EXPECT_EQ("test", str(Format("test"))); } @@ -77,7 +97,8 @@ TEST(FormatterTest, ArgsInDifferentPositions) { EXPECT_EQ("42 after", str(Format("{0} after") << 42)); EXPECT_EQ("before 42 after", str(Format("before {0} after") << 42)); EXPECT_EQ("answer = 42", str(Format("{0} = {1}") << "answer" << 42)); - EXPECT_EQ("42 is the answer", str(Format("{1} is the {0}") << "answer" << 42)); + EXPECT_EQ("42 is the answer", + str(Format("{1} is the {0}") << "answer" << 42)); EXPECT_EQ("abracadabra", str(Format("{0}{1}{0}") << "abra" << "cad")); } @@ -424,5 +445,3 @@ TEST(FormatterTest, FormatStringFromSpeedTest) { << 1.234 << 42 << 3.13 << "str" << reinterpret_cast(1000) << 'X')); } - -// TODO: more tests