mirror of
https://github.com/fmtlib/fmt.git
synced 2025-01-26 12:35:32 +00:00
Implement an escape mechanism.
This commit is contained in:
parent
6caa750a00
commit
31a507034e
23
format.cc
23
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 '{'
|
// FormatError reporting unmatched '{'. The idea is that unmatched '{'
|
||||||
// should override other errors.
|
// should override other errors.
|
||||||
void ReportError(const char *s, const std::string &message) {
|
void ReportError(const char *s, const std::string &message) {
|
||||||
while (*s && *s != '}')
|
for (int num_open_braces = 1; *s; ++s) {
|
||||||
++s;
|
if (*s == '{') {
|
||||||
throw fmt::FormatError(*s ? message : std::string("unmatched '{' in format"));
|
++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) {
|
void ReportUnknownType(char code, const char *type) {
|
||||||
@ -225,8 +231,15 @@ void fmt::Formatter::Format() {
|
|||||||
const char *start = format_;
|
const char *start = format_;
|
||||||
const char *s = start;
|
const char *s = start;
|
||||||
while (*s) {
|
while (*s) {
|
||||||
if (*s++ != '{') continue;
|
char c = *s++;
|
||||||
// TODO: handle escape sequence
|
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);
|
buffer_.insert(buffer_.end(), start, s - 1);
|
||||||
|
|
||||||
// Parse argument index.
|
// Parse argument index.
|
||||||
|
25
format.h
25
format.h
@ -78,7 +78,7 @@ class Formatter {
|
|||||||
: type(CUSTOM), custom_value(value), format(f) {}
|
: type(CUSTOM), custom_value(value), format(f) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<Arg> args_;
|
std::vector<Arg> args_; // Format arguments.
|
||||||
|
|
||||||
const char *format_; // Format string.
|
const char *format_; // Format string.
|
||||||
|
|
||||||
@ -94,7 +94,7 @@ class Formatter {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
void FormatInt(T value, unsigned flags, int width, char type);
|
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 <typename T>
|
template <typename T>
|
||||||
void FormatDouble(
|
void FormatDouble(
|
||||||
T value, unsigned flags, int width, int precision, char type);
|
T value, unsigned flags, int width, int precision, char type);
|
||||||
@ -127,6 +127,16 @@ class ArgFormatter {
|
|||||||
private:
|
private:
|
||||||
friend class Formatter;
|
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 <typename T>
|
||||||
|
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:
|
protected:
|
||||||
mutable Formatter *formatter_;
|
mutable Formatter *formatter_;
|
||||||
|
|
||||||
@ -211,17 +221,6 @@ class ArgFormatter {
|
|||||||
return *this;
|
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 <typename T>
|
|
||||||
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 <typename T>
|
template <typename T>
|
||||||
ArgFormatter &operator<<(T *value) {
|
ArgFormatter &operator<<(T *value) {
|
||||||
const T *const_value = value;
|
const T *const_value = value;
|
||||||
|
@ -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) {
|
TEST(FormatterTest, NoArgs) {
|
||||||
EXPECT_EQ("abracadabra", str(Format("{0}{1}{0}") << "abra" << "cad"));
|
|
||||||
EXPECT_EQ("test", str(Format("test")));
|
EXPECT_EQ("test", str(Format("test")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +97,8 @@ TEST(FormatterTest, ArgsInDifferentPositions) {
|
|||||||
EXPECT_EQ("42 after", str(Format("{0} after") << 42));
|
EXPECT_EQ("42 after", str(Format("{0} after") << 42));
|
||||||
EXPECT_EQ("before 42 after", str(Format("before {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("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"));
|
EXPECT_EQ("abracadabra", str(Format("{0}{1}{0}") << "abra" << "cad"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -424,5 +445,3 @@ TEST(FormatterTest, FormatStringFromSpeedTest) {
|
|||||||
<< 1.234 << 42 << 3.13 << "str"
|
<< 1.234 << 42 << 3.13 << "str"
|
||||||
<< reinterpret_cast<void*>(1000) << 'X'));
|
<< reinterpret_cast<void*>(1000) << 'X'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: more tests
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user