From 344de54aa986653755f1696e4cad764adc9b8928 Mon Sep 17 00:00:00 2001 From: jamboree Date: Fri, 5 Jun 2015 18:06:29 +0800 Subject: [PATCH 01/11] Support named arguments (WIP) --- doc/syntax.rst | 12 +-- format.cc | 72 +++++++++++------ format.h | 184 +++++++++++++++++++++++++++++++++++++------- posix.h | 4 +- test/format-test.cc | 24 ++++-- 5 files changed, 234 insertions(+), 62 deletions(-) diff --git a/doc/syntax.rst b/doc/syntax.rst index 109b7175..c019945b 100644 --- a/doc/syntax.rst +++ b/doc/syntax.rst @@ -15,13 +15,15 @@ literal text, it can be escaped by doubling: ``{{`` and ``}}``. The grammar for a replacement field is as follows: .. productionlist:: sf - replacement_field: "{" [`arg_index`] [":" `format_spec`] "}" + replacement_field: "{" [`arg_field`] [":" `format_spec`] "}" + arg_field: `arg_index` | `arg_name` arg_index: `integer` + arg_name: \^[a-zA-Z_][a-zA-Z0-9_]*$\ -In less formal terms, the replacement field can start with an *arg_index* +In less formal terms, the replacement field can start with an *arg_field* that specifies the argument whose value is to be formatted and inserted into the output instead of the replacement field. -The *arg_index* is optionally followed by a *format_spec*, which is preceded +The *arg_field* is optionally followed by a *format_spec*, which is preceded by a colon ``':'``. These specify a non-default format for the replacement value. See also the :ref:`formatspec` section. @@ -73,8 +75,8 @@ The general form of a *standard format specifier* is: fill: align: "<" | ">" | "=" | "^" sign: "+" | "-" | " " - width: `integer` | "{" `arg_index` "}" - precision: `integer` | "{" `arg_index` "}" + width: `integer` | "{" `arg_field` "}" + precision: `integer` | "{" `arg_field` "}" type: `int_type` | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "p" | "s" int_type: "b" | "B" | "d" | "o" | "x" | "X" diff --git a/format.cc b/format.cc index d7c2ca90..398a2bbc 100644 --- a/format.cc +++ b/format.cc @@ -265,6 +265,12 @@ int parse_nonnegative_int(const Char *&s) { return value; } +template +inline bool is_name_start(Char c) +{ + return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '_' == c; +} + inline void require_numeric_argument(const Arg &arg, char spec) { if (arg.type > Arg::LAST_NUMERIC_TYPE) { std::string message = @@ -402,10 +408,10 @@ inline Arg::StringValue ignore_incompatible_str( } // namespace FMT_FUNC void fmt::SystemError::init( - int err_code, StringRef format_str, ArgList args) { + int err_code, StringRef format_str, ArgList args, const ArgMap &map) { error_code_ = err_code; MemoryWriter w; - internal::format_system_error(w, err_code, format(format_str, args)); + internal::format_system_error(w, err_code, format(format_str, args, map)); std::runtime_error &base = *this; base = std::runtime_error(w.str()); } @@ -518,10 +524,10 @@ FMT_FUNC int fmt::internal::UTF16ToUTF8::convert(fmt::WStringRef s) { } FMT_FUNC void fmt::WindowsError::init( - int err_code, StringRef format_str, ArgList args) { + int err_code, StringRef format_str, ArgList args, const ArgMap &map) { error_code_ = err_code; MemoryWriter w; - internal::format_windows_error(w, err_code, format(format_str, args)); + internal::format_windows_error(w, err_code, format(format_str, args, map)); std::runtime_error &base = *this; base = std::runtime_error(w.str()); } @@ -693,6 +699,24 @@ inline Arg fmt::BasicFormatter::parse_arg_index(const Char *&s) { return arg; } +template +inline Arg fmt::BasicFormatter::parse_arg_name(const Char *&s, const fmt::BasicArgMap &map) { + assert(is_name_start(*s)); + const Char *start = s; + Char c; + do { + c = *++s; + } while ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9'); + const int* index = map.find(fmt::BasicStringRef(start, s - start)); + if (!index) + FMT_THROW(fmt::FormatError("argument not found")); + const char *error = 0; + Arg arg = get_arg(*index, error); + if (error) + FMT_THROW(fmt::FormatError(error)); + return arg; +} + FMT_FUNC Arg fmt::internal::FormatterBase::do_get_arg( unsigned arg_index, const char *&error) { Arg arg = args_[arg_index]; @@ -960,7 +984,7 @@ void fmt::internal::PrintfFormatter::format( template const Char *fmt::BasicFormatter::format( - const Char *&format_str, const Arg &arg) { + const Char *&format_str, const Arg &arg, const BasicArgMap &map) { const Char *s = format_str; FormatSpec spec; if (*s == ':') { @@ -1038,7 +1062,7 @@ const Char *fmt::BasicFormatter::format( spec.width_ = parse_nonnegative_int(s); } else if (*s == '{') { ++s; - const Arg &width_arg = parse_arg_index(s); + const Arg &width_arg = is_name_start(*s) ? parse_arg_name(s, map) : parse_arg_index(s); if (*s++ != '}') FMT_THROW(FormatError("invalid format string")); ULongLong value = 0; @@ -1075,7 +1099,7 @@ const Char *fmt::BasicFormatter::format( spec.precision_ = parse_nonnegative_int(s); } else if (*s == '{') { ++s; - const Arg &precision_arg = parse_arg_index(s); + const Arg &precision_arg = is_name_start(*s) ? parse_arg_name(s, map) : parse_arg_index(s); if (*s++ != '}') FMT_THROW(FormatError("invalid format string")); ULongLong value = 0; @@ -1128,7 +1152,7 @@ const Char *fmt::BasicFormatter::format( template void fmt::BasicFormatter::format( - BasicStringRef format_str, const ArgList &args) { + BasicStringRef format_str, const ArgList &args, const BasicArgMap &map) { const Char *s = start_ = format_str.c_str(); set_args(args); while (*s) { @@ -1142,8 +1166,8 @@ void fmt::BasicFormatter::format( if (c == '}') FMT_THROW(FormatError("unmatched '}' in format string")); write(writer_, start_, s - 1); - Arg arg = parse_arg_index(s); - s = format(s, arg); + Arg arg = is_name_start(*s) ? parse_arg_name(s, map) : parse_arg_index(s); + s = format(s, arg, map); } write(writer_, start_, s); } @@ -1160,33 +1184,33 @@ FMT_FUNC void fmt::report_windows_error( } #endif -FMT_FUNC void fmt::print(std::FILE *f, StringRef format_str, ArgList args) { +FMT_FUNC void fmt::print(std::FILE *f, StringRef format_str, ArgList args, const ArgMap &map) { MemoryWriter w; - w.write(format_str, args); + w.write(format_str, args, map); std::fwrite(w.data(), 1, w.size(), f); } -FMT_FUNC void fmt::print(StringRef format_str, ArgList args) { - print(stdout, format_str, args); +FMT_FUNC void fmt::print(StringRef format_str, ArgList args, const ArgMap &map) { + print(stdout, format_str, args, map); } -FMT_FUNC void fmt::print(std::ostream &os, StringRef format_str, ArgList args) { +FMT_FUNC void fmt::print(std::ostream &os, StringRef format_str, ArgList args, const ArgMap &map) { MemoryWriter w; - w.write(format_str, args); + w.write(format_str, args, map); os.write(w.data(), w.size()); } -FMT_FUNC void fmt::print_colored(Color c, StringRef format, ArgList args) { +FMT_FUNC void fmt::print_colored(Color c, StringRef format, ArgList args, const ArgMap &map) { char escape[] = "\x1b[30m"; escape[3] = '0' + static_cast(c); std::fputs(escape, stdout); - print(format, args); + print(format, args, map); std::fputs(RESET_COLOR, stdout); } -FMT_FUNC int fmt::fprintf(std::FILE *f, StringRef format, ArgList args) { +FMT_FUNC int fmt::fprintf(std::FILE *f, StringRef format, ArgList args, const ArgMap &map) { MemoryWriter w; - printf(w, format, args); + printf(w, format, args, map); std::size_t size = w.size(); return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast(size); } @@ -1200,10 +1224,10 @@ template struct fmt::internal::BasicData; template void fmt::internal::FixedBuffer::grow(std::size_t); template const char *fmt::BasicFormatter::format( - const char *&format_str, const fmt::internal::Arg &arg); + const char *&format_str, const fmt::internal::Arg &arg, const BasicArgMap &map); template void fmt::BasicFormatter::format( - BasicStringRef format, const ArgList &args); + BasicStringRef format, const ArgList &args, const BasicArgMap &map); template void fmt::internal::PrintfFormatter::format( BasicWriter &writer, BasicStringRef format, const ArgList &args); @@ -1221,10 +1245,10 @@ template int fmt::internal::CharTraits::format_float( template void fmt::internal::FixedBuffer::grow(std::size_t); template const wchar_t *fmt::BasicFormatter::format( - const wchar_t *&format_str, const fmt::internal::Arg &arg); + const wchar_t *&format_str, const fmt::internal::Arg &arg, const BasicArgMap &map); template void fmt::BasicFormatter::format( - BasicStringRef format, const ArgList &args); + BasicStringRef format, const ArgList &args, const BasicArgMap &map); template void fmt::internal::PrintfFormatter::format( BasicWriter &writer, BasicStringRef format, diff --git a/format.h b/format.h index 2e558edc..3fd5ba4a 100644 --- a/format.h +++ b/format.h @@ -227,6 +227,11 @@ class BasicStringRef { std::size_t size_; public: + /** + Constructs an empty string reference object. + */ + BasicStringRef() : data_(), size_() {} + /** Constructs a string reference object from a C string and a size. */ @@ -274,6 +279,9 @@ class BasicStringRef { friend bool operator!=(BasicStringRef lhs, BasicStringRef rhs) { return lhs.data_ != rhs.data_; } + friend bool operator<(BasicStringRef lhs, BasicStringRef rhs) { + return std::lexicographical_compare(lhs.data_, lhs.data_ + lhs.size_, rhs.data_, rhs.data_ + rhs.size_); + } }; typedef BasicStringRef StringRef; @@ -759,6 +767,15 @@ struct Value { }; }; +template +struct NamedArg +{ + BasicStringRef name; + T const& arg; + + NamedArg(BasicStringRef name, T const& arg) : name(name), arg(arg) {} +}; + // A formatting argument. It is a POD type to allow storage in // internal::MemoryBuffer. struct Arg : Value { @@ -962,6 +979,50 @@ class MakeValue : public Arg { } }; +template +inline const T &strip_name(const T &arg) +{ + return arg; +} + +template +inline const T &strip_name(const NamedArg &namedArg) +{ + return namedArg.arg; +} + +template +struct NamedArgsCounter +{ + static const int value = 0; +}; + +template +struct NamedArgsCounter : NamedArgsCounter +{}; + +template +struct NamedArgsCounter, U...> +{ + static const int value = 1 + NamedArgsCounter::value; +}; + +template +inline void add_named_args(NameIndexPair*) {} + +template +inline void add_named_args(NameIndexPair* map, T const&, U const&... rest) +{ + add_named_args(map, rest...); +} + +template +inline void add_named_args(NameIndexPair* map, const NamedArg &namedArg, U const&... rest) +{ + *map = NameIndexPair(namedArg.name, N); + add_named_args(map + 1, rest...); +} + #define FMT_DISPATCH(call) static_cast(this)->call // An argument visitor. @@ -1146,6 +1207,48 @@ class ArgList { struct FormatSpec; +template +struct BasicArgMap +{ + typedef std::pair, int> value_type; + + BasicArgMap() : map_(), size_() {} + + BasicArgMap(value_type* map, int size) + : map_(map), size_(size) + { + std::sort(map, map + size, [](const value_type &lhs, const value_type &rhs) + { + return lhs.first < rhs.first; + }); + } + + const int* find(BasicStringRef name) const + { + value_type* first = map_; + value_type* last = map_ + size_; + while (first != last) + { + value_type* it(first + (last - first >> 1)); + if (name < it->first) + last = it; + else if (it->first < name) + first = ++it; + else + return &it->second; + } + return nullptr; + } + +private: + + value_type* map_; + int size_; +}; + +typedef BasicArgMap ArgMap; +typedef BasicArgMap WArgMap; + namespace internal { class FormatterBase { @@ -1208,14 +1311,17 @@ class BasicFormatter : private internal::FormatterBase { // Parses argument index and returns corresponding argument. internal::Arg parse_arg_index(const Char *&s); + // Parses argument name and returns corresponding argument. + internal::Arg parse_arg_name(const Char *&s, const BasicArgMap &map); + public: explicit BasicFormatter(BasicWriter &w) : writer_(w) {} BasicWriter &writer() { return writer_; } - void format(BasicStringRef format_str, const ArgList &args); + void format(BasicStringRef format_str, const ArgList &args, const BasicArgMap &map); - const Char *format(const Char *&format_str, const internal::Arg &arg); + const Char *format(const Char *&format_str, const internal::Arg &arg, const BasicArgMap &map); }; enum Alignment { @@ -1556,7 +1662,11 @@ inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) { template \ void func(arg_type arg0, const Args & ... args) { \ typename fmt::internal::ArgArray::Type array; \ - func(arg0, fmt::internal::make_arg_list(array, args...)); \ + const int count = fmt::internal::NamedArgsCounter::value; \ + fmt::BasicArgMap::value_type mapArray[count + 1]; \ + fmt::internal::add_named_args<0>(mapArray, args...); \ + fmt::BasicArgMap map(mapArray, count); \ + func(arg0, fmt::internal::make_arg_list(array, fmt::internal::strip_name(args)...), map); \ } // Defines a variadic constructor. @@ -1564,7 +1674,11 @@ inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) { template \ ctor(arg0_type arg0, arg1_type arg1, const Args & ... args) { \ typename fmt::internal::ArgArray::Type array; \ - func(arg0, arg1, fmt::internal::make_arg_list(array, args...)); \ + const int count = fmt::internal::NamedArgsCounter::value; \ + fmt::BasicArgMap::value_type mapArray[count + 1]; \ + fmt::internal::add_named_args<0>(mapArray, args...); \ + fmt::BasicArgMap map(mapArray, count); \ + func(arg0, arg1, fmt::internal::make_arg_list(array, fmt::internal::strip_name(args)...), map); \ } #else @@ -1641,7 +1755,7 @@ inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) { */ class SystemError : public internal::RuntimeError { private: - void init(int err_code, StringRef format_str, ArgList args); + void init(int err_code, StringRef format_str, ArgList args, const ArgMap &map); protected: int error_code_; @@ -1677,7 +1791,7 @@ class SystemError : public internal::RuntimeError { \endrst */ SystemError(int error_code, StringRef message) { - init(error_code, message, ArgList()); + init(error_code, message, ArgList(), ArgMap()); } FMT_VARIADIC_CTOR(SystemError, init, int, StringRef) @@ -1852,8 +1966,8 @@ class BasicWriter { See also :ref:`syntax`. \endrst */ - void write(BasicStringRef format, ArgList args) { - BasicFormatter(*this).format(format, args); + void write(BasicStringRef format, ArgList args, const BasicArgMap &map) { + BasicFormatter(*this).format(format, args, map); } FMT_VARIADIC_VOID(write, BasicStringRef) @@ -2401,7 +2515,7 @@ void format(BasicFormatter &f, const Char *&format_str, const T &value) { internal::Arg arg = internal::MakeValue(str); arg.type = static_cast( internal::MakeValue::type(str)); - format_str = f.format(format_str, arg); + format_str = f.format(format_str, arg, BasicArgMap()); } // Reports a system error without throwing an exception. @@ -2413,7 +2527,7 @@ void report_system_error(int error_code, StringRef message) FMT_NOEXCEPT; /** A Windows error. */ class WindowsError : public SystemError { private: - void init(int error_code, StringRef format_str, ArgList args); + void init(int error_code, StringRef format_str, ArgList args, const ArgMap &map); public: /** @@ -2445,7 +2559,7 @@ class WindowsError : public SystemError { \endrst */ WindowsError(int error_code, StringRef message) { - init(error_code, message, ArgList()); + init(error_code, message, ArgList(), ArgMap()); } FMT_VARIADIC_CTOR(WindowsError, init, int, StringRef) }; @@ -2464,7 +2578,7 @@ enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE }; Example: PrintColored(fmt::RED, "Elapsed time: {0:.2f} seconds") << 1.23; */ -void print_colored(Color c, StringRef format, ArgList args); +void print_colored(Color c, StringRef format, ArgList args, const ArgMap &map); /** \rst @@ -2475,15 +2589,15 @@ void print_colored(Color c, StringRef format, ArgList args); std::string message = format("The answer is {}", 42); \endrst */ -inline std::string format(StringRef format_str, ArgList args) { +inline std::string format(StringRef format_str, ArgList args, const ArgMap &map) { MemoryWriter w; - w.write(format_str, args); + w.write(format_str, args, map); return w.str(); } -inline std::wstring format(WStringRef format_str, ArgList args) { +inline std::wstring format(WStringRef format_str, ArgList args, const WArgMap &map) { WMemoryWriter w; - w.write(format_str, args); + w.write(format_str, args, map); return w.str(); } @@ -2496,7 +2610,7 @@ inline std::wstring format(WStringRef format_str, ArgList args) { print(stderr, "Don't {}!", "panic"); \endrst */ -void print(std::FILE *f, StringRef format_str, ArgList args); +void print(std::FILE *f, StringRef format_str, ArgList args, const ArgMap &map); /** \rst @@ -2507,7 +2621,7 @@ void print(std::FILE *f, StringRef format_str, ArgList args); print("Elapsed time: {0:.2f} seconds", 1.23); \endrst */ -void print(StringRef format_str, ArgList args); +void print(StringRef format_str, ArgList args, const ArgMap &map); /** \rst @@ -2518,10 +2632,10 @@ void print(StringRef format_str, ArgList args); print(cerr, "Don't {}!", "panic"); \endrst */ -void print(std::ostream &os, StringRef format_str, ArgList args); +void print(std::ostream &os, StringRef format_str, ArgList args, const ArgMap &map); template -void printf(BasicWriter &w, BasicStringRef format, ArgList args) { +void printf(BasicWriter &w, BasicStringRef format, ArgList args, const ArgMap &) { internal::PrintfFormatter().format(w, format, args); } @@ -2534,9 +2648,9 @@ void printf(BasicWriter &w, BasicStringRef format, ArgList args) { std::string message = fmt::sprintf("The answer is %d", 42); \endrst */ -inline std::string sprintf(StringRef format, ArgList args) { +inline std::string sprintf(StringRef format, ArgList args, const ArgMap &map) { MemoryWriter w; - printf(w, format, args); + printf(w, format, args, map); return w.str(); } @@ -2549,7 +2663,7 @@ inline std::string sprintf(StringRef format, ArgList args) { fmt::fprintf(stderr, "Don't %s!", "panic"); \endrst */ -int fprintf(std::FILE *f, StringRef format, ArgList args); +int fprintf(std::FILE *f, StringRef format, ArgList args, const ArgMap &); /** \rst @@ -2560,8 +2674,8 @@ int fprintf(std::FILE *f, StringRef format, ArgList args); fmt::printf("Elapsed time: %.2f seconds", 1.23); \endrst */ -inline int printf(StringRef format, ArgList args) { - return fprintf(stdout, format, args); +inline int printf(StringRef format, ArgList args, const ArgMap &map) { + return fprintf(stdout, format, args, map); } /** @@ -2667,6 +2781,16 @@ inline void format_decimal(char *&buffer, T value) { internal::format_decimal(buffer, abs_value, num_digits); buffer += num_digits; } + +template +inline internal::NamedArg arg(StringRef name, T const& arg) { + return internal::NamedArg(name, arg); +} + +template +inline internal::NamedArg arg(WStringRef name, T const& arg) { + return internal::NamedArg(name, arg); +} } #if FMT_GCC_VERSION @@ -2702,8 +2826,12 @@ inline void format_decimal(char *&buffer, T value) { ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ const Args & ... args) { \ typename fmt::internal::ArgArray::Type array; \ + const int count = fmt::internal::NamedArgsCounter::value; \ + fmt::BasicArgMap::value_type mapArray[count + 1]; \ + fmt::internal::add_named_args<0>(mapArray, args...); \ + fmt::BasicArgMap map(mapArray, count); \ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), \ - fmt::internal::make_arg_list(array, args...)); \ + fmt::internal::make_arg_list(array, fmt::internal::strip_name(args)...), map); \ } #else // Defines a wrapper for a function taking __VA_ARGS__ arguments @@ -2771,6 +2899,10 @@ inline void format_decimal(char *&buffer, T value) { #define FMT_VARIADIC_W(ReturnType, func, ...) \ FMT_VARIADIC_(wchar_t, ReturnType, func, return func, __VA_ARGS__) +#define FMT_CAPTURE_ARG_(id, index) ::fmt::arg(#id, id) + +#define FMT_CAPTURE(...) FMT_FOR_EACH(FMT_CAPTURE_ARG_, __VA_ARGS__) + namespace fmt { FMT_VARIADIC(std::string, format, StringRef) FMT_VARIADIC_W(std::wstring, format, WStringRef) diff --git a/posix.h b/posix.h index 70a0db1a..00928e11 100644 --- a/posix.h +++ b/posix.h @@ -197,8 +197,8 @@ public: // of MinGW that define fileno as a macro. int (fileno)() const; - void print(fmt::StringRef format_str, const ArgList &args) { - fmt::print(file_, format_str, args); + void print(fmt::StringRef format_str, const ArgList &args, const ArgMap &map) { + fmt::print(file_, format_str, args, map); } FMT_VARIADIC(void, print, fmt::StringRef) }; diff --git a/test/format-test.cc b/test/format-test.cc index 67ce0811..0c641d3c 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -561,7 +561,7 @@ TEST(FormatterTest, ArgsInDifferentPositions) { TEST(FormatterTest, ArgErrors) { EXPECT_THROW_MSG(format("{"), FormatError, "invalid format string"); - EXPECT_THROW_MSG(format("{x}"), FormatError, "invalid format string"); + EXPECT_THROW_MSG(format("{?}"), FormatError, "invalid format string"); EXPECT_THROW_MSG(format("{0"), FormatError, "invalid format string"); EXPECT_THROW_MSG(format("{0}"), FormatError, "argument index out of range"); @@ -606,6 +606,20 @@ TEST(FormatterTest, ManyArgs) { EXPECT_THROW_MSG(TestFormat::format(format_str), FormatError, "argument index out of range"); } + +TEST(FormatterTest, NamedArg) { + char a = 'A', b = 'B', c = 'C'; + EXPECT_EQ("BBAACC", format("{1}{b}{0}{a}{2}{c}", FMT_CAPTURE(a, b, c))); + EXPECT_EQ(" A", format("{a:>2}", FMT_CAPTURE(a))); + EXPECT_THROW_MSG(format("{a+}", FMT_CAPTURE(a)), FormatError, "missing '}' in format string"); + EXPECT_THROW_MSG(format("{d}", FMT_CAPTURE(a, b, c)), FormatError, "argument not found"); + EXPECT_THROW_MSG(format("{a}{}", FMT_CAPTURE(a)), + FormatError, "cannot switch from manual to automatic argument indexing"); + EXPECT_THROW_MSG(format("{}{a}", FMT_CAPTURE(a)), + FormatError, "cannot switch from automatic to manual argument indexing"); + EXPECT_EQ(" -42", format("{0:{width}}", -42, fmt::arg("width", 4))); + EXPECT_EQ("st", format("{0:.{precision}}", "str", fmt::arg("precision", 2))); +} #endif TEST(FormatterTest, AutoArgIndex) { @@ -919,7 +933,7 @@ TEST(FormatterTest, RuntimeWidth) { FormatError, "invalid format string"); EXPECT_THROW_MSG(format("{0:{}", 0), FormatError, "cannot switch from manual to automatic argument indexing"); - EXPECT_THROW_MSG(format("{0:{x}}", 0), + EXPECT_THROW_MSG(format("{0:{?}}", 0), FormatError, "invalid format string"); EXPECT_THROW_MSG(format("{0:{1}}", 0), FormatError, "argument index out of range"); @@ -1036,7 +1050,7 @@ TEST(FormatterTest, RuntimePrecision) { FormatError, "invalid format string"); EXPECT_THROW_MSG(format("{0:.{}", 0), FormatError, "cannot switch from manual to automatic argument indexing"); - EXPECT_THROW_MSG(format("{0:.{x}}", 0), + EXPECT_THROW_MSG(format("{0:.{?}}", 0), FormatError, "invalid format string"); EXPECT_THROW_MSG(format("{0:.{1}", 0, 0), FormatError, "precision not allowed in integer format specifier"); @@ -1550,10 +1564,10 @@ TEST(StrTest, Convert) { } std::string format_message(int id, const char *format, - const fmt::ArgList &args) { + const fmt::ArgList &args, const fmt::ArgMap &map) { MemoryWriter w; w.write("[{}] ", id); - w.write(format, args); + w.write(format, args, map); return w.str(); } From f2c4b6554919ef95a082b203e73e22f4e0a720ad Mon Sep 17 00:00:00 2001 From: Jamboree Date: Sat, 6 Jun 2015 01:59:11 +0800 Subject: [PATCH 02/11] Support named arguments (done) --- doc/api.rst | 4 + format.cc | 89 +++++++------ format.h | 318 +++++++++++++++++++++++++------------------- posix.h | 4 +- test/format-test.cc | 6 +- 5 files changed, 241 insertions(+), 180 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index 5464f08b..725f2ec1 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -72,6 +72,10 @@ Write API Utilities ========= +.. doxygenfunction:: fmt::arg(StringRef, const T&) + +.. doxygendefine:: FMT_CAPTURE + .. doxygendefine:: FMT_VARIADIC .. doxygenclass:: fmt::ArgList diff --git a/format.cc b/format.cc index 398a2bbc..1083f638 100644 --- a/format.cc +++ b/format.cc @@ -268,7 +268,7 @@ int parse_nonnegative_int(const Char *&s) { template inline bool is_name_start(Char c) { - return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '_' == c; + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; } inline void require_numeric_argument(const Arg &arg, char spec) { @@ -408,10 +408,10 @@ inline Arg::StringValue ignore_incompatible_str( } // namespace FMT_FUNC void fmt::SystemError::init( - int err_code, StringRef format_str, ArgList args, const ArgMap &map) { + int err_code, StringRef format_str, ArgList args) { error_code_ = err_code; MemoryWriter w; - internal::format_system_error(w, err_code, format(format_str, args, map)); + internal::format_system_error(w, err_code, format(format_str, args)); std::runtime_error &base = *this; base = std::runtime_error(w.str()); } @@ -524,10 +524,10 @@ FMT_FUNC int fmt::internal::UTF16ToUTF8::convert(fmt::WStringRef s) { } FMT_FUNC void fmt::WindowsError::init( - int err_code, StringRef format_str, ArgList args, const ArgMap &map) { + int err_code, StringRef format_str, ArgList args) { error_code_ = err_code; MemoryWriter w; - internal::format_windows_error(w, err_code, format(format_str, args, map)); + internal::format_windows_error(w, err_code, format(format_str, args)); std::runtime_error &base = *this; base = std::runtime_error(w.str()); } @@ -700,21 +700,18 @@ inline Arg fmt::BasicFormatter::parse_arg_index(const Char *&s) { } template -inline Arg fmt::BasicFormatter::parse_arg_name(const Char *&s, const fmt::BasicArgMap &map) { - assert(is_name_start(*s)); - const Char *start = s; - Char c; - do { - c = *++s; - } while ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9'); - const int* index = map.find(fmt::BasicStringRef(start, s - start)); - if (!index) - FMT_THROW(fmt::FormatError("argument not found")); - const char *error = 0; - Arg arg = get_arg(*index, error); - if (error) - FMT_THROW(fmt::FormatError(error)); - return arg; +inline Arg fmt::BasicFormatter::parse_arg_name(const Char *&s) { + assert(is_name_start(*s)); + const Char *start = s; + Char c; + do { + c = *++s; + } while (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9')); + const char *error = 0; + Arg arg = get_arg(fmt::BasicStringRef(start, s - start), error); + if (error) + FMT_THROW(fmt::FormatError(error)); + return arg; } FMT_FUNC Arg fmt::internal::FormatterBase::do_get_arg( @@ -742,6 +739,18 @@ inline Arg fmt::internal::FormatterBase::get_arg( return Arg(); } +template +inline Arg fmt::internal::FormatterBase::get_arg( + const BasicStringRef& arg_name, const char *&error) { + const BasicArgMap* map = args_.get_arg_map(); + assert(map); + const unsigned* index = map->find(arg_name); + if (index) + return get_arg(*index, error); + error = "argument not found"; + return Arg(); +} + template void fmt::internal::PrintfFormatter::parse_flags( FormatSpec &spec, const Char *&s) { @@ -984,7 +993,7 @@ void fmt::internal::PrintfFormatter::format( template const Char *fmt::BasicFormatter::format( - const Char *&format_str, const Arg &arg, const BasicArgMap &map) { + const Char *&format_str, const Arg &arg) { const Char *s = format_str; FormatSpec spec; if (*s == ':') { @@ -1062,7 +1071,7 @@ const Char *fmt::BasicFormatter::format( spec.width_ = parse_nonnegative_int(s); } else if (*s == '{') { ++s; - const Arg &width_arg = is_name_start(*s) ? parse_arg_name(s, map) : parse_arg_index(s); + const Arg &width_arg = is_name_start(*s) ? parse_arg_name(s) : parse_arg_index(s); if (*s++ != '}') FMT_THROW(FormatError("invalid format string")); ULongLong value = 0; @@ -1099,7 +1108,7 @@ const Char *fmt::BasicFormatter::format( spec.precision_ = parse_nonnegative_int(s); } else if (*s == '{') { ++s; - const Arg &precision_arg = is_name_start(*s) ? parse_arg_name(s, map) : parse_arg_index(s); + const Arg &precision_arg = is_name_start(*s) ? parse_arg_name(s) : parse_arg_index(s); if (*s++ != '}') FMT_THROW(FormatError("invalid format string")); ULongLong value = 0; @@ -1152,7 +1161,7 @@ const Char *fmt::BasicFormatter::format( template void fmt::BasicFormatter::format( - BasicStringRef format_str, const ArgList &args, const BasicArgMap &map) { + BasicStringRef format_str, const ArgList &args) { const Char *s = start_ = format_str.c_str(); set_args(args); while (*s) { @@ -1166,8 +1175,8 @@ void fmt::BasicFormatter::format( if (c == '}') FMT_THROW(FormatError("unmatched '}' in format string")); write(writer_, start_, s - 1); - Arg arg = is_name_start(*s) ? parse_arg_name(s, map) : parse_arg_index(s); - s = format(s, arg, map); + Arg arg = is_name_start(*s) ? parse_arg_name(s) : parse_arg_index(s); + s = format(s, arg); } write(writer_, start_, s); } @@ -1184,33 +1193,33 @@ FMT_FUNC void fmt::report_windows_error( } #endif -FMT_FUNC void fmt::print(std::FILE *f, StringRef format_str, ArgList args, const ArgMap &map) { +FMT_FUNC void fmt::print(std::FILE *f, StringRef format_str, ArgList args) { MemoryWriter w; - w.write(format_str, args, map); + w.write(format_str, args); std::fwrite(w.data(), 1, w.size(), f); } -FMT_FUNC void fmt::print(StringRef format_str, ArgList args, const ArgMap &map) { - print(stdout, format_str, args, map); +FMT_FUNC void fmt::print(StringRef format_str, ArgList args) { + print(stdout, format_str, args); } -FMT_FUNC void fmt::print(std::ostream &os, StringRef format_str, ArgList args, const ArgMap &map) { +FMT_FUNC void fmt::print(std::ostream &os, StringRef format_str, ArgList args) { MemoryWriter w; - w.write(format_str, args, map); + w.write(format_str, args); os.write(w.data(), w.size()); } -FMT_FUNC void fmt::print_colored(Color c, StringRef format, ArgList args, const ArgMap &map) { +FMT_FUNC void fmt::print_colored(Color c, StringRef format, ArgList args) { char escape[] = "\x1b[30m"; escape[3] = '0' + static_cast(c); std::fputs(escape, stdout); - print(format, args, map); + print(format, args); std::fputs(RESET_COLOR, stdout); } -FMT_FUNC int fmt::fprintf(std::FILE *f, StringRef format, ArgList args, const ArgMap &map) { +FMT_FUNC int fmt::fprintf(std::FILE *f, StringRef format, ArgList args) { MemoryWriter w; - printf(w, format, args, map); + printf(w, format, args); std::size_t size = w.size(); return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast(size); } @@ -1224,10 +1233,10 @@ template struct fmt::internal::BasicData; template void fmt::internal::FixedBuffer::grow(std::size_t); template const char *fmt::BasicFormatter::format( - const char *&format_str, const fmt::internal::Arg &arg, const BasicArgMap &map); + const char *&format_str, const fmt::internal::Arg &arg); template void fmt::BasicFormatter::format( - BasicStringRef format, const ArgList &args, const BasicArgMap &map); + BasicStringRef format, const ArgList &args); template void fmt::internal::PrintfFormatter::format( BasicWriter &writer, BasicStringRef format, const ArgList &args); @@ -1245,10 +1254,10 @@ template int fmt::internal::CharTraits::format_float( template void fmt::internal::FixedBuffer::grow(std::size_t); template const wchar_t *fmt::BasicFormatter::format( - const wchar_t *&format_str, const fmt::internal::Arg &arg, const BasicArgMap &map); + const wchar_t *&format_str, const fmt::internal::Arg &arg); template void fmt::BasicFormatter::format( - BasicStringRef format, const ArgList &args, const BasicArgMap &map); + BasicStringRef format, const ArgList &args); template void fmt::internal::PrintfFormatter::format( BasicWriter &writer, BasicStringRef format, diff --git a/format.h b/format.h index 3fd5ba4a..7fc80cba 100644 --- a/format.h +++ b/format.h @@ -768,12 +768,11 @@ struct Value { }; template -struct NamedArg -{ - BasicStringRef name; - T const& arg; +struct NamedArg { + BasicStringRef name; + T const& arg; - NamedArg(BasicStringRef name, T const& arg) : name(name), arg(arg) {} + NamedArg(BasicStringRef name, T const& arg) : name(name), arg(arg) {} }; // A formatting argument. It is a POD type to allow storage in @@ -980,47 +979,13 @@ class MakeValue : public Arg { }; template -inline const T &strip_name(const T &arg) -{ - return arg; +inline const T &strip_name(const T &arg) { + return arg; } template -inline const T &strip_name(const NamedArg &namedArg) -{ - return namedArg.arg; -} - -template -struct NamedArgsCounter -{ - static const int value = 0; -}; - -template -struct NamedArgsCounter : NamedArgsCounter -{}; - -template -struct NamedArgsCounter, U...> -{ - static const int value = 1 + NamedArgsCounter::value; -}; - -template -inline void add_named_args(NameIndexPair*) {} - -template -inline void add_named_args(NameIndexPair* map, T const&, U const&... rest) -{ - add_named_args(map, rest...); -} - -template -inline void add_named_args(NameIndexPair* map, const NamedArg &namedArg, U const&... rest) -{ - *map = NameIndexPair(namedArg.name, N); - add_named_args(map + 1, rest...); +inline const T &strip_name(const NamedArg &namedArg) { + return namedArg.arg; } #define FMT_DISPATCH(call) static_cast(this)->call @@ -1142,6 +1107,45 @@ class RuntimeError : public std::runtime_error { template class ArgFormatter; + +template +struct BasicArgMap { + typedef std::pair, unsigned> value_type; + + struct Compare { + bool operator()(const value_type &lhs, const value_type &rhs) const { + return lhs.first < rhs.first; + } + }; + + BasicArgMap() : map_(), size_() {} + + BasicArgMap(value_type* map, unsigned size) + : map_(map), size_(size) { + std::sort(map, map + size, Compare()); + } + + const unsigned* find(BasicStringRef name) const { + value_type* first = map_; + value_type* last = map_ + size_; + while (first != last) + { + value_type* it(first + ((last - first) >> 1)); + if (name < it->first) + last = it; + else if (it->first < name) + first = ++it; + else + return &it->second; + } + return 0; + } + +private: + + value_type* map_; + unsigned size_; +}; } // namespace internal /** An argument list. */ @@ -1159,6 +1163,7 @@ class ArgList { const internal::Value *values_; const internal::Arg *args_; }; + const void* map_; internal::Arg::Type type(unsigned index) const { unsigned shift = index * 4; @@ -1171,12 +1176,15 @@ class ArgList { // Maximum number of arguments with packed types. enum { MAX_PACKED_ARGS = 16 }; - ArgList() : types_(0) {} + ArgList() : types_(0), map_() {} - ArgList(ULongLong types, const internal::Value *values) - : types_(types), values_(values) {} - ArgList(ULongLong types, const internal::Arg *args) - : types_(types), args_(args) {} + template + ArgList(ULongLong types, const internal::Value *values, const internal::BasicArgMap* map) + : types_(types), values_(values), map_(map) {} + + template + ArgList(ULongLong types, const internal::Arg *args, const internal::BasicArgMap* map) + : types_(types), args_(args), map_(map) {} /** Returns the argument at specified index. */ internal::Arg operator[](unsigned index) const { @@ -1203,52 +1211,15 @@ class ArgList { } return args_[index]; } + + template + const internal::BasicArgMap* get_arg_map() const { + return static_cast*>(map_); + } }; struct FormatSpec; -template -struct BasicArgMap -{ - typedef std::pair, int> value_type; - - BasicArgMap() : map_(), size_() {} - - BasicArgMap(value_type* map, int size) - : map_(map), size_(size) - { - std::sort(map, map + size, [](const value_type &lhs, const value_type &rhs) - { - return lhs.first < rhs.first; - }); - } - - const int* find(BasicStringRef name) const - { - value_type* first = map_; - value_type* last = map_ + size_; - while (first != last) - { - value_type* it(first + (last - first >> 1)); - if (name < it->first) - last = it; - else if (it->first < name) - first = ++it; - else - return &it->second; - } - return nullptr; - } - -private: - - value_type* map_; - int size_; -}; - -typedef BasicArgMap ArgMap; -typedef BasicArgMap WArgMap; - namespace internal { class FormatterBase { @@ -1272,6 +1243,11 @@ class FormatterBase { // specified index. Arg get_arg(unsigned arg_index, const char *&error); + // Checks if manual indexing is used and returns the argument with + // specified name. + template + Arg get_arg(const BasicStringRef& arg_name, const char *&error); + template void write(BasicWriter &w, const Char *start, const Char *end) { if (start != end) @@ -1312,16 +1288,16 @@ class BasicFormatter : private internal::FormatterBase { internal::Arg parse_arg_index(const Char *&s); // Parses argument name and returns corresponding argument. - internal::Arg parse_arg_name(const Char *&s, const BasicArgMap &map); + internal::Arg parse_arg_name(const Char *&s); public: explicit BasicFormatter(BasicWriter &w) : writer_(w) {} BasicWriter &writer() { return writer_; } - void format(BasicStringRef format_str, const ArgList &args, const BasicArgMap &map); + void format(BasicStringRef format_str, const ArgList &args); - const Char *format(const Char *&format_str, const internal::Arg &arg, const BasicArgMap &map); + const Char *format(const Char *&format_str, const internal::Arg &arg); }; enum Alignment { @@ -1582,6 +1558,14 @@ struct ArgArray { (N < ArgList::MAX_PACKED_ARGS), Value, Arg>::type Type[SIZE]; }; +template +inline void add_named_arg(NameIndexPair* map, T const&, unsigned) {} + +template +inline void add_named_arg(NameIndexPair*& map, const NamedArg &namedArg, unsigned n) { + *map++ = NameIndexPair(namedArg.name, n); +} + #if FMT_USE_VARIADIC_TEMPLATES template inline uint64_t make_type(const Arg &first, const Args & ... tail) { @@ -1622,12 +1606,36 @@ inline void store_args(Arg *args, const T &arg, const Args & ... tail) { template ArgList make_arg_list(typename ArgArray::Type array, + const BasicArgMap* map, const Args & ... args) { if (check(sizeof...(Args) >= ArgList::MAX_PACKED_ARGS)) set_types(array, args...); store_args(array, args...); - return ArgList(make_type(args...), array); + return ArgList(make_type(args...), array, map); } + +template +struct NamedArgsCounter { + static const int value = 0; +}; + +template +struct NamedArgsCounter : NamedArgsCounter {}; + +template +struct NamedArgsCounter, U...> { + static const int value = 1 + NamedArgsCounter::value; +}; + +template +inline void add_named_args(NameIndexPair*) {} + +template +inline void add_named_args(NameIndexPair* map, const T &arg, const U &... rest) { + add_named_arg(map, arg, N); + add_named_args(map, rest...); +} + #else struct ArgType { @@ -1653,8 +1661,6 @@ inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) { # define FMT_MAKE_TEMPLATE_ARG(n) typename T##n # define FMT_MAKE_ARG_TYPE(n) T##n # define FMT_MAKE_ARG(n) const T##n &v##n -# define FMT_MAKE_REF_char(n) fmt::internal::MakeValue(v##n) -# define FMT_MAKE_REF_wchar_t(n) fmt::internal::MakeValue(v##n) #if FMT_USE_VARIADIC_TEMPLATES // Defines a variadic function returning void. @@ -1663,10 +1669,10 @@ inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) { void func(arg_type arg0, const Args & ... args) { \ typename fmt::internal::ArgArray::Type array; \ const int count = fmt::internal::NamedArgsCounter::value; \ - fmt::BasicArgMap::value_type mapArray[count + 1]; \ + typename fmt::internal::BasicArgMap::value_type mapArray[count + 1]; \ fmt::internal::add_named_args<0>(mapArray, args...); \ - fmt::BasicArgMap map(mapArray, count); \ - func(arg0, fmt::internal::make_arg_list(array, fmt::internal::strip_name(args)...), map); \ + fmt::internal::BasicArgMap map(mapArray, count); \ + func(arg0, fmt::internal::make_arg_list(array, &map, fmt::internal::strip_name(args)...)); \ } // Defines a variadic constructor. @@ -1675,25 +1681,31 @@ inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) { ctor(arg0_type arg0, arg1_type arg1, const Args & ... args) { \ typename fmt::internal::ArgArray::Type array; \ const int count = fmt::internal::NamedArgsCounter::value; \ - fmt::BasicArgMap::value_type mapArray[count + 1]; \ + typename fmt::internal::BasicArgMap::value_type mapArray[count + 1]; \ fmt::internal::add_named_args<0>(mapArray, args...); \ - fmt::BasicArgMap map(mapArray, count); \ - func(arg0, arg1, fmt::internal::make_arg_list(array, fmt::internal::strip_name(args)...), map); \ + fmt::internal::BasicArgMap map(mapArray, count); \ + func(arg0, arg1, fmt::internal::make_arg_list(array, &map, fmt::internal::strip_name(args)...)); \ } #else -# define FMT_MAKE_REF(n) fmt::internal::MakeValue(v##n) -# define FMT_MAKE_REF2(n) v##n +# define FMT_MAKE_REF(n) fmt::internal::MakeValue(fmt::internal::strip_name(v##n)) +# define FMT_MAKE_REF2(n) fmt::internal::strip_name(v##n) +# define FMT_ADD_NAMED_ARG(n) fmt::internal::add_named_arg(mapPtr, v##n, n) // Defines a wrapper for a function taking one argument of type arg_type // and n additional arguments of arbitrary types. # define FMT_WRAP1(func, arg_type, n) \ template \ inline void func(arg_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ + typedef typename fmt::internal::BasicArgMap::value_type ArgMapValue; \ const fmt::internal::ArgArray::Type array = {FMT_GEN(n, FMT_MAKE_REF)}; \ + ArgMapValue mapArray[n]; \ + ArgMapValue* mapPtr = mapArray; \ + FMT_GEN(n, FMT_ADD_NAMED_ARG); \ + fmt::internal::BasicArgMap map(mapArray, unsigned(mapPtr - mapArray)); \ func(arg1, fmt::ArgList( \ - fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array)); \ + fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array, &map)); \ } // Emulates a variadic function returning void on a pre-C++11 compiler. @@ -1708,9 +1720,14 @@ inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) { # define FMT_CTOR(ctor, func, arg0_type, arg1_type, n) \ template \ ctor(arg0_type arg0, arg1_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ + typedef typename fmt::internal::BasicArgMap::value_type ArgMapValue; \ const fmt::internal::ArgArray::Type array = {FMT_GEN(n, FMT_MAKE_REF)}; \ + ArgMapValue mapArray[n]; \ + ArgMapValue* mapPtr = mapArray; \ + FMT_GEN(n, FMT_ADD_NAMED_ARG); \ + fmt::internal::BasicArgMap map(mapArray, unsigned(mapPtr - mapArray)); \ func(arg0, arg1, fmt::ArgList( \ - fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array)); \ + fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array, &map)); \ } // Emulates a variadic constructor on a pre-C++11 compiler. @@ -1755,7 +1772,7 @@ inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) { */ class SystemError : public internal::RuntimeError { private: - void init(int err_code, StringRef format_str, ArgList args, const ArgMap &map); + void init(int err_code, StringRef format_str, ArgList args); protected: int error_code_; @@ -1791,7 +1808,7 @@ class SystemError : public internal::RuntimeError { \endrst */ SystemError(int error_code, StringRef message) { - init(error_code, message, ArgList(), ArgMap()); + init(error_code, message, ArgList()); } FMT_VARIADIC_CTOR(SystemError, init, int, StringRef) @@ -1966,8 +1983,8 @@ class BasicWriter { See also :ref:`syntax`. \endrst */ - void write(BasicStringRef format, ArgList args, const BasicArgMap &map) { - BasicFormatter(*this).format(format, args, map); + void write(BasicStringRef format, ArgList args) { + BasicFormatter(*this).format(format, args); } FMT_VARIADIC_VOID(write, BasicStringRef) @@ -2515,7 +2532,7 @@ void format(BasicFormatter &f, const Char *&format_str, const T &value) { internal::Arg arg = internal::MakeValue(str); arg.type = static_cast( internal::MakeValue::type(str)); - format_str = f.format(format_str, arg, BasicArgMap()); + format_str = f.format(format_str, arg); } // Reports a system error without throwing an exception. @@ -2527,7 +2544,7 @@ void report_system_error(int error_code, StringRef message) FMT_NOEXCEPT; /** A Windows error. */ class WindowsError : public SystemError { private: - void init(int error_code, StringRef format_str, ArgList args, const ArgMap &map); + void init(int error_code, StringRef format_str, ArgList args); public: /** @@ -2559,7 +2576,7 @@ class WindowsError : public SystemError { \endrst */ WindowsError(int error_code, StringRef message) { - init(error_code, message, ArgList(), ArgMap()); + init(error_code, message, ArgList()); } FMT_VARIADIC_CTOR(WindowsError, init, int, StringRef) }; @@ -2578,7 +2595,7 @@ enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE }; Example: PrintColored(fmt::RED, "Elapsed time: {0:.2f} seconds") << 1.23; */ -void print_colored(Color c, StringRef format, ArgList args, const ArgMap &map); +void print_colored(Color c, StringRef format, ArgList args); /** \rst @@ -2589,15 +2606,15 @@ void print_colored(Color c, StringRef format, ArgList args, const ArgMap &map); std::string message = format("The answer is {}", 42); \endrst */ -inline std::string format(StringRef format_str, ArgList args, const ArgMap &map) { +inline std::string format(StringRef format_str, ArgList args) { MemoryWriter w; - w.write(format_str, args, map); + w.write(format_str, args); return w.str(); } -inline std::wstring format(WStringRef format_str, ArgList args, const WArgMap &map) { +inline std::wstring format(WStringRef format_str, ArgList args) { WMemoryWriter w; - w.write(format_str, args, map); + w.write(format_str, args); return w.str(); } @@ -2610,7 +2627,7 @@ inline std::wstring format(WStringRef format_str, ArgList args, const WArgMap &m print(stderr, "Don't {}!", "panic"); \endrst */ -void print(std::FILE *f, StringRef format_str, ArgList args, const ArgMap &map); +void print(std::FILE *f, StringRef format_str, ArgList args); /** \rst @@ -2621,7 +2638,7 @@ void print(std::FILE *f, StringRef format_str, ArgList args, const ArgMap &map); print("Elapsed time: {0:.2f} seconds", 1.23); \endrst */ -void print(StringRef format_str, ArgList args, const ArgMap &map); +void print(StringRef format_str, ArgList args); /** \rst @@ -2632,10 +2649,10 @@ void print(StringRef format_str, ArgList args, const ArgMap &map); print(cerr, "Don't {}!", "panic"); \endrst */ -void print(std::ostream &os, StringRef format_str, ArgList args, const ArgMap &map); +void print(std::ostream &os, StringRef format_str, ArgList args); template -void printf(BasicWriter &w, BasicStringRef format, ArgList args, const ArgMap &) { +void printf(BasicWriter &w, BasicStringRef format, ArgList args) { internal::PrintfFormatter().format(w, format, args); } @@ -2648,9 +2665,9 @@ void printf(BasicWriter &w, BasicStringRef format, ArgList args, con std::string message = fmt::sprintf("The answer is %d", 42); \endrst */ -inline std::string sprintf(StringRef format, ArgList args, const ArgMap &map) { +inline std::string sprintf(StringRef format, ArgList args) { MemoryWriter w; - printf(w, format, args, map); + printf(w, format, args); return w.str(); } @@ -2663,7 +2680,7 @@ inline std::string sprintf(StringRef format, ArgList args, const ArgMap &map) { fmt::fprintf(stderr, "Don't %s!", "panic"); \endrst */ -int fprintf(std::FILE *f, StringRef format, ArgList args, const ArgMap &); +int fprintf(std::FILE *f, StringRef format, ArgList args); /** \rst @@ -2674,8 +2691,8 @@ int fprintf(std::FILE *f, StringRef format, ArgList args, const ArgMap &); fmt::printf("Elapsed time: %.2f seconds", 1.23); \endrst */ -inline int printf(StringRef format, ArgList args, const ArgMap &map) { - return fprintf(stdout, format, args, map); +inline int printf(StringRef format, ArgList args) { + return fprintf(stdout, format, args); } /** @@ -2782,13 +2799,24 @@ inline void format_decimal(char *&buffer, T value) { buffer += num_digits; } + +/** + \rst + Returns a named argument for formatting functions. + + **Example**:: + + print("Elapsed time: {s:.2f} seconds", arg("s", 1.23)); + + \endrst + */ template -inline internal::NamedArg arg(StringRef name, T const& arg) { +inline internal::NamedArg arg(StringRef name, const T &arg) { return internal::NamedArg(name, arg); } template -inline internal::NamedArg arg(WStringRef name, T const& arg) { +inline internal::NamedArg arg(WStringRef name, const T &arg) { return internal::NamedArg(name, arg); } } @@ -2827,22 +2855,28 @@ inline internal::NamedArg arg(WStringRef name, T const& arg) { const Args & ... args) { \ typename fmt::internal::ArgArray::Type array; \ const int count = fmt::internal::NamedArgsCounter::value; \ - fmt::BasicArgMap::value_type mapArray[count + 1]; \ + fmt::internal::BasicArgMap::value_type mapArray[count + 1]; \ fmt::internal::add_named_args<0>(mapArray, args...); \ - fmt::BasicArgMap map(mapArray, count); \ + fmt::internal::BasicArgMap map(mapArray, count); \ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), \ - fmt::internal::make_arg_list(array, fmt::internal::strip_name(args)...), map); \ + fmt::internal::make_arg_list(array, &map, fmt::internal::strip_name(args)...)); \ } #else // Defines a wrapper for a function taking __VA_ARGS__ arguments // and n additional arguments of arbitrary types. -# define FMT_WRAP(Char, ReturnType, func, call, n, ...) \ +# define FMT_WRAP(Char_, ReturnType, func, call, n, ...) \ template \ inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ FMT_GEN(n, FMT_MAKE_ARG)) { \ - fmt::internal::ArgArray::Type arr = {FMT_GEN(n, FMT_MAKE_REF_##Char)}; \ + typedef Char_ Char; \ + typedef fmt::internal::BasicArgMap::value_type ArgMapValue; \ + fmt::internal::ArgArray::Type arr = {FMT_GEN(n, FMT_MAKE_REF)}; \ + ArgMapValue mapArray[n]; \ + ArgMapValue* mapPtr = mapArray; \ + FMT_GEN(n, FMT_ADD_NAMED_ARG); \ + fmt::internal::BasicArgMap map(mapArray, unsigned(mapPtr - mapArray)); \ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \ - fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), arr)); \ + fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), arr, &map)); \ } # define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ @@ -2901,6 +2935,20 @@ inline internal::NamedArg arg(WStringRef name, T const& arg) { #define FMT_CAPTURE_ARG_(id, index) ::fmt::arg(#id, id) +/** + \rst + Convenient macro to capture the arguments' names and values into several + `fmt::arg(name, value)`. + + **Example**:: + + int x = 1, y = 2; + print("point: ({x}, {y})", FMT_CAPTURE(x, y)); + // same as: + // print("point: ({x}, {y})", arg("x", x), arg("y", y)); + + \endrst + */ #define FMT_CAPTURE(...) FMT_FOR_EACH(FMT_CAPTURE_ARG_, __VA_ARGS__) namespace fmt { diff --git a/posix.h b/posix.h index 00928e11..70a0db1a 100644 --- a/posix.h +++ b/posix.h @@ -197,8 +197,8 @@ public: // of MinGW that define fileno as a macro. int (fileno)() const; - void print(fmt::StringRef format_str, const ArgList &args, const ArgMap &map) { - fmt::print(file_, format_str, args, map); + void print(fmt::StringRef format_str, const ArgList &args) { + fmt::print(file_, format_str, args); } FMT_VARIADIC(void, print, fmt::StringRef) }; diff --git a/test/format-test.cc b/test/format-test.cc index 0c641d3c..99472fb9 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -606,6 +606,7 @@ TEST(FormatterTest, ManyArgs) { EXPECT_THROW_MSG(TestFormat::format(format_str), FormatError, "argument index out of range"); } +#endif TEST(FormatterTest, NamedArg) { char a = 'A', b = 'B', c = 'C'; @@ -620,7 +621,6 @@ TEST(FormatterTest, NamedArg) { EXPECT_EQ(" -42", format("{0:{width}}", -42, fmt::arg("width", 4))); EXPECT_EQ("st", format("{0:.{precision}}", "str", fmt::arg("precision", 2))); } -#endif TEST(FormatterTest, AutoArgIndex) { EXPECT_EQ("abc", format("{}{}{}", 'a', 'b', 'c')); @@ -1564,10 +1564,10 @@ TEST(StrTest, Convert) { } std::string format_message(int id, const char *format, - const fmt::ArgList &args, const fmt::ArgMap &map) { + const fmt::ArgList &args) { MemoryWriter w; w.write("[{}] ", id); - w.write(format, args, map); + w.write(format, args); return w.str(); } From c3362f46b96a375e099dbc6804a52812d91de56d Mon Sep 17 00:00:00 2001 From: Jamboree Date: Sat, 6 Jun 2015 02:09:10 +0800 Subject: [PATCH 03/11] Style edit --- format.cc | 7 +++---- format.h | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/format.cc b/format.cc index 1083f638..6bd8a388 100644 --- a/format.cc +++ b/format.cc @@ -266,9 +266,8 @@ int parse_nonnegative_int(const Char *&s) { } template -inline bool is_name_start(Char c) -{ - return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; +inline bool is_name_start(Char c) { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; } inline void require_numeric_argument(const Arg &arg, char spec) { @@ -746,7 +745,7 @@ inline Arg fmt::internal::FormatterBase::get_arg( assert(map); const unsigned* index = map->find(arg_name); if (index) - return get_arg(*index, error); + return get_arg(*index, error); error = "argument not found"; return Arg(); } diff --git a/format.h b/format.h index 7fc80cba..f1c129ff 100644 --- a/format.h +++ b/format.h @@ -280,7 +280,7 @@ class BasicStringRef { return lhs.data_ != rhs.data_; } friend bool operator<(BasicStringRef lhs, BasicStringRef rhs) { - return std::lexicographical_compare(lhs.data_, lhs.data_ + lhs.size_, rhs.data_, rhs.data_ + rhs.size_); + return std::lexicographical_compare(lhs.data_, lhs.data_ + lhs.size_, rhs.data_, rhs.data_ + rhs.size_); } }; @@ -1563,7 +1563,7 @@ inline void add_named_arg(NameIndexPair* map, T const&, unsigned) {} template inline void add_named_arg(NameIndexPair*& map, const NamedArg &namedArg, unsigned n) { - *map++ = NameIndexPair(namedArg.name, n); + *map++ = NameIndexPair(namedArg.name, n); } #if FMT_USE_VARIADIC_TEMPLATES From 7f96af5eef73b667a1eb7d0f4e5d68211aecc19c Mon Sep 17 00:00:00 2001 From: Jamboree Date: Sat, 6 Jun 2015 13:20:30 +0800 Subject: [PATCH 04/11] Suppress MSVC warning --- format.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/format.h b/format.h index f1c129ff..a9007de9 100644 --- a/format.h +++ b/format.h @@ -773,6 +773,11 @@ struct NamedArg { T const& arg; NamedArg(BasicStringRef name, T const& arg) : name(name), arg(arg) {} + +private: + + // Suppress MSVC warning : assignment operator could not be generated + NamedArg& operator=(const NamedArg&); }; // A formatting argument. It is a POD type to allow storage in @@ -1559,7 +1564,7 @@ struct ArgArray { }; template -inline void add_named_arg(NameIndexPair* map, T const&, unsigned) {} +inline void add_named_arg(NameIndexPair*, const T &, unsigned) {} template inline void add_named_arg(NameIndexPair*& map, const NamedArg &namedArg, unsigned n) { From c26d7311f44eecf12bfb68e34d113ab720fe756b Mon Sep 17 00:00:00 2001 From: Jamboree Date: Sat, 6 Jun 2015 14:05:08 +0800 Subject: [PATCH 05/11] Add FMT_CAPTURE_W --- format.h | 4 ++++ test/format-test.cc | 3 +++ 2 files changed, 7 insertions(+) diff --git a/format.h b/format.h index a9007de9..6e8484a9 100644 --- a/format.h +++ b/format.h @@ -2940,6 +2940,8 @@ inline internal::NamedArg arg(WStringRef name, const T &arg) { #define FMT_CAPTURE_ARG_(id, index) ::fmt::arg(#id, id) +#define FMT_CAPTURE_ARG_W_(id, index) ::fmt::arg(L#id, id) + /** \rst Convenient macro to capture the arguments' names and values into several @@ -2956,6 +2958,8 @@ inline internal::NamedArg arg(WStringRef name, const T &arg) { */ #define FMT_CAPTURE(...) FMT_FOR_EACH(FMT_CAPTURE_ARG_, __VA_ARGS__) +#define FMT_CAPTURE_W(...) FMT_FOR_EACH(FMT_CAPTURE_ARG_W_, __VA_ARGS__) + namespace fmt { FMT_VARIADIC(std::string, format, StringRef) FMT_VARIADIC_W(std::wstring, format, WStringRef) diff --git a/test/format-test.cc b/test/format-test.cc index 99472fb9..6936fb2a 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -613,6 +613,7 @@ TEST(FormatterTest, NamedArg) { EXPECT_EQ("BBAACC", format("{1}{b}{0}{a}{2}{c}", FMT_CAPTURE(a, b, c))); EXPECT_EQ(" A", format("{a:>2}", FMT_CAPTURE(a))); EXPECT_THROW_MSG(format("{a+}", FMT_CAPTURE(a)), FormatError, "missing '}' in format string"); + EXPECT_THROW_MSG(format("{a}"), FormatError, "argument not found"); EXPECT_THROW_MSG(format("{d}", FMT_CAPTURE(a, b, c)), FormatError, "argument not found"); EXPECT_THROW_MSG(format("{a}{}", FMT_CAPTURE(a)), FormatError, "cannot switch from manual to automatic argument indexing"); @@ -620,6 +621,8 @@ TEST(FormatterTest, NamedArg) { FormatError, "cannot switch from automatic to manual argument indexing"); EXPECT_EQ(" -42", format("{0:{width}}", -42, fmt::arg("width", 4))); EXPECT_EQ("st", format("{0:.{precision}}", "str", fmt::arg("precision", 2))); + int n = 100; + EXPECT_EQ(L"n=100", format(L"n={n}", FMT_CAPTURE_W(n))); } TEST(FormatterTest, AutoArgIndex) { From 6bbd3e9bd2165db9de48280113e91c8b6892a35d Mon Sep 17 00:00:00 2001 From: Jamboree Date: Sat, 6 Jun 2015 14:27:11 +0800 Subject: [PATCH 06/11] Fix FMT_CAPTURE_W --- format.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/format.h b/format.h index 6e8484a9..954a3db8 100644 --- a/format.h +++ b/format.h @@ -2940,7 +2940,7 @@ inline internal::NamedArg arg(WStringRef name, const T &arg) { #define FMT_CAPTURE_ARG_(id, index) ::fmt::arg(#id, id) -#define FMT_CAPTURE_ARG_W_(id, index) ::fmt::arg(L#id, id) +#define FMT_CAPTURE_ARG_W_(id, index) ::fmt::arg(L###id, id) /** \rst From 150e5e7963261f19b2cb409fd74012657baa29b1 Mon Sep 17 00:00:00 2001 From: jamboree Date: Tue, 9 Jun 2015 15:29:32 +0800 Subject: [PATCH 07/11] Move name mapping from compile time to runtime --- format.cc | 44 ++++++--- format.h | 266 ++++++++++++++++++++++-------------------------------- 2 files changed, 136 insertions(+), 174 deletions(-) diff --git a/format.cc b/format.cc index 6bd8a388..2f72909a 100644 --- a/format.cc +++ b/format.cc @@ -686,6 +686,20 @@ void fmt::BasicWriter::write_str( write_str(str_value, str_size, spec); } +template +inline Arg fmt::BasicFormatter::get_arg( + const BasicStringRef& arg_name, const char *&error) { + if (check_no_auto_index(error)) { + next_arg_index_ = -1; + map_.init(args_); + const Arg* arg = map_.find(arg_name); + if (arg) + return *arg; + error = "argument not found"; + } + return Arg(); +} + template inline Arg fmt::BasicFormatter::parse_arg_index(const Char *&s) { const char *error = 0; @@ -716,8 +730,13 @@ inline Arg fmt::BasicFormatter::parse_arg_name(const Char *&s) { FMT_FUNC Arg fmt::internal::FormatterBase::do_get_arg( unsigned arg_index, const char *&error) { Arg arg = args_[arg_index]; - if (arg.type == Arg::NONE) + switch (arg.type) { + case Arg::NONE: error = "argument index out of range"; + break; + case Arg::NAMED_ARG: + arg = *static_cast(arg.pointer); + } return arg; } @@ -728,25 +747,20 @@ inline Arg fmt::internal::FormatterBase::next_arg(const char *&error) { return Arg(); } +inline bool fmt::internal::FormatterBase::check_no_auto_index(const char *&error) { + if (next_arg_index_ > 0) { + error = "cannot switch from automatic to manual argument indexing"; + return false; + } + return true; +} + inline Arg fmt::internal::FormatterBase::get_arg( unsigned arg_index, const char *&error) { - if (next_arg_index_ <= 0) { + if (check_no_auto_index(error)) { next_arg_index_ = -1; return do_get_arg(arg_index, error); } - error = "cannot switch from automatic to manual argument indexing"; - return Arg(); -} - -template -inline Arg fmt::internal::FormatterBase::get_arg( - const BasicStringRef& arg_name, const char *&error) { - const BasicArgMap* map = args_.get_arg_map(); - assert(map); - const unsigned* index = map->find(arg_name); - if (index) - return get_arg(*index, error); - error = "argument not found"; return Arg(); } diff --git a/format.h b/format.h index 954a3db8..e9da7c4a 100644 --- a/format.h +++ b/format.h @@ -39,6 +39,7 @@ #include #include #include +#include #if _SECURE_SCL # include @@ -227,11 +228,6 @@ class BasicStringRef { std::size_t size_; public: - /** - Constructs an empty string reference object. - */ - BasicStringRef() : data_(), size_() {} - /** Constructs a string reference object from a C string and a size. */ @@ -758,7 +754,7 @@ struct Value { }; enum Type { - NONE, + NONE, NAMED_ARG, // Integer types should go first, INT, UINT, LONG_LONG, ULONG_LONG, CHAR, LAST_INTEGER_TYPE = CHAR, // followed by floating-point types. @@ -767,25 +763,15 @@ struct Value { }; }; -template -struct NamedArg { - BasicStringRef name; - T const& arg; - - NamedArg(BasicStringRef name, T const& arg) : name(name), arg(arg) {} - -private: - - // Suppress MSVC warning : assignment operator could not be generated - NamedArg& operator=(const NamedArg&); -}; - // A formatting argument. It is a POD type to allow storage in // internal::MemoryBuffer. struct Arg : Value { Type type; }; +template +struct NamedArg; + template struct None {}; @@ -981,17 +967,24 @@ class MakeValue : public Arg { static uint64_t type(const T &) { return IsConvertibleToInt::value ? Arg::INT : Arg::CUSTOM; } + + template + MakeValue(const NamedArg &value) { pointer = &value; } + + template + static uint64_t type(const NamedArg &) { return Arg::NAMED_ARG; } }; -template -inline const T &strip_name(const T &arg) { - return arg; -} +template +struct NamedArg : Arg { + BasicStringRef name; -template -inline const T &strip_name(const NamedArg &namedArg) { - return namedArg.arg; -} + template + NamedArg(BasicStringRef name, const T &value) + : name(name), Arg(MakeValue(value)) { + type = static_cast(MakeValue::type(value)); + } +}; #define FMT_DISPATCH(call) static_cast(this)->call @@ -1112,45 +1105,6 @@ class RuntimeError : public std::runtime_error { template class ArgFormatter; - -template -struct BasicArgMap { - typedef std::pair, unsigned> value_type; - - struct Compare { - bool operator()(const value_type &lhs, const value_type &rhs) const { - return lhs.first < rhs.first; - } - }; - - BasicArgMap() : map_(), size_() {} - - BasicArgMap(value_type* map, unsigned size) - : map_(map), size_(size) { - std::sort(map, map + size, Compare()); - } - - const unsigned* find(BasicStringRef name) const { - value_type* first = map_; - value_type* last = map_ + size_; - while (first != last) - { - value_type* it(first + ((last - first) >> 1)); - if (name < it->first) - last = it; - else if (it->first < name) - first = ++it; - else - return &it->second; - } - return 0; - } - -private: - - value_type* map_; - unsigned size_; -}; } // namespace internal /** An argument list. */ @@ -1168,7 +1122,6 @@ class ArgList { const internal::Value *values_; const internal::Arg *args_; }; - const void* map_; internal::Arg::Type type(unsigned index) const { unsigned shift = index * 4; @@ -1181,15 +1134,15 @@ class ArgList { // Maximum number of arguments with packed types. enum { MAX_PACKED_ARGS = 16 }; - ArgList() : types_(0), map_() {} - template - ArgList(ULongLong types, const internal::Value *values, const internal::BasicArgMap* map) - : types_(types), values_(values), map_(map) {} + struct Map; - template - ArgList(ULongLong types, const internal::Arg *args, const internal::BasicArgMap* map) - : types_(types), args_(args), map_(map) {} + ArgList() : types_(0) {} + + ArgList(ULongLong types, const internal::Value *values) + : types_(types), values_(values) {} + ArgList(ULongLong types, const internal::Arg *args) + : types_(types), args_(args) {} /** Returns the argument at specified index. */ internal::Arg operator[](unsigned index) const { @@ -1216,11 +1169,62 @@ class ArgList { } return args_[index]; } +}; - template - const internal::BasicArgMap* get_arg_map() const { - return static_cast*>(map_); +template +struct fmt::ArgList::Map { + typedef std::map, internal::Arg> MapType; + typedef typename MapType::value_type Pair; + + void init(const ArgList &args) { + if (!map_.empty()) + return; + bool use_values = args.type(MAX_PACKED_ARGS - 1) == internal::Arg::NONE; + if (use_values) { + add_packed(args, args.values_); + return; + } + add_packed(args, args.args_); + const internal::NamedArg* named_arg; + for (unsigned i = MAX_PACKED_ARGS; ; ++i) { + switch (args.args_[i].type) { + case internal::Arg::NONE: + return; + case internal::Arg::NAMED_ARG: + named_arg = static_cast*>(args.args_[i].pointer); + map_.insert(Pair(named_arg->name, *named_arg)); + break; + } + } } + + const internal::Arg* find(const fmt::BasicStringRef &name) const + { + typename MapType::const_iterator it = map_.find(name); + if (it != map_.end()) + return &it->second; + return 0; + } + +private: + + template + void add_packed(const ArgList &args, const T* data) { + const internal::NamedArg* named_arg; + for (unsigned i = 0; i != MAX_PACKED_ARGS; ++i) { + internal::Arg::Type arg_type = args.type(i); + switch (arg_type) { + case internal::Arg::NONE: + return; + case internal::Arg::NAMED_ARG: + named_arg = static_cast*>(data[i].pointer); + map_.insert(Pair(named_arg->name, *named_arg)); + break; + } + } + } + + MapType map_; }; struct FormatSpec; @@ -1228,7 +1232,7 @@ struct FormatSpec; namespace internal { class FormatterBase { - private: + protected: ArgList args_; int next_arg_index_; @@ -1248,10 +1252,7 @@ class FormatterBase { // specified index. Arg get_arg(unsigned arg_index, const char *&error); - // Checks if manual indexing is used and returns the argument with - // specified name. - template - Arg get_arg(const BasicStringRef& arg_name, const char *&error); + bool check_no_auto_index(const char *&error); template void write(BasicWriter &w, const Char *start, const Char *end) { @@ -1286,9 +1287,16 @@ class BasicFormatter : private internal::FormatterBase { private: BasicWriter &writer_; const Char *start_; + ArgList::Map map_; FMT_DISALLOW_COPY_AND_ASSIGN(BasicFormatter); + using FormatterBase::get_arg; + + // Checks if manual indexing is used and returns the argument with + // specified name. + internal::Arg get_arg(const BasicStringRef& arg_name, const char *&error); + // Parses argument index and returns corresponding argument. internal::Arg parse_arg_index(const Char *&s); @@ -1563,14 +1571,6 @@ struct ArgArray { (N < ArgList::MAX_PACKED_ARGS), Value, Arg>::type Type[SIZE]; }; -template -inline void add_named_arg(NameIndexPair*, const T &, unsigned) {} - -template -inline void add_named_arg(NameIndexPair*& map, const NamedArg &namedArg, unsigned n) { - *map++ = NameIndexPair(namedArg.name, n); -} - #if FMT_USE_VARIADIC_TEMPLATES template inline uint64_t make_type(const Arg &first, const Args & ... tail) { @@ -1611,36 +1611,12 @@ inline void store_args(Arg *args, const T &arg, const Args & ... tail) { template ArgList make_arg_list(typename ArgArray::Type array, - const BasicArgMap* map, const Args & ... args) { if (check(sizeof...(Args) >= ArgList::MAX_PACKED_ARGS)) set_types(array, args...); store_args(array, args...); - return ArgList(make_type(args...), array, map); + return ArgList(make_type(args...), array); } - -template -struct NamedArgsCounter { - static const int value = 0; -}; - -template -struct NamedArgsCounter : NamedArgsCounter {}; - -template -struct NamedArgsCounter, U...> { - static const int value = 1 + NamedArgsCounter::value; -}; - -template -inline void add_named_args(NameIndexPair*) {} - -template -inline void add_named_args(NameIndexPair* map, const T &arg, const U &... rest) { - add_named_arg(map, arg, N); - add_named_args(map, rest...); -} - #else struct ArgType { @@ -1666,6 +1642,8 @@ inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) { # define FMT_MAKE_TEMPLATE_ARG(n) typename T##n # define FMT_MAKE_ARG_TYPE(n) T##n # define FMT_MAKE_ARG(n) const T##n &v##n +# define FMT_MAKE_REF_char(n) fmt::internal::MakeValue(v##n) +# define FMT_MAKE_REF_wchar_t(n) fmt::internal::MakeValue(v##n) #if FMT_USE_VARIADIC_TEMPLATES // Defines a variadic function returning void. @@ -1673,11 +1651,7 @@ inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) { template \ void func(arg_type arg0, const Args & ... args) { \ typename fmt::internal::ArgArray::Type array; \ - const int count = fmt::internal::NamedArgsCounter::value; \ - typename fmt::internal::BasicArgMap::value_type mapArray[count + 1]; \ - fmt::internal::add_named_args<0>(mapArray, args...); \ - fmt::internal::BasicArgMap map(mapArray, count); \ - func(arg0, fmt::internal::make_arg_list(array, &map, fmt::internal::strip_name(args)...)); \ + func(arg0, fmt::internal::make_arg_list(array, args...)); \ } // Defines a variadic constructor. @@ -1685,32 +1659,22 @@ inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) { template \ ctor(arg0_type arg0, arg1_type arg1, const Args & ... args) { \ typename fmt::internal::ArgArray::Type array; \ - const int count = fmt::internal::NamedArgsCounter::value; \ - typename fmt::internal::BasicArgMap::value_type mapArray[count + 1]; \ - fmt::internal::add_named_args<0>(mapArray, args...); \ - fmt::internal::BasicArgMap map(mapArray, count); \ - func(arg0, arg1, fmt::internal::make_arg_list(array, &map, fmt::internal::strip_name(args)...)); \ + func(arg0, arg1, fmt::internal::make_arg_list(array, args...)); \ } #else -# define FMT_MAKE_REF(n) fmt::internal::MakeValue(fmt::internal::strip_name(v##n)) -# define FMT_MAKE_REF2(n) fmt::internal::strip_name(v##n) -# define FMT_ADD_NAMED_ARG(n) fmt::internal::add_named_arg(mapPtr, v##n, n) +# define FMT_MAKE_REF(n) fmt::internal::MakeValue(v##n) +# define FMT_MAKE_REF2(n) v##n // Defines a wrapper for a function taking one argument of type arg_type // and n additional arguments of arbitrary types. # define FMT_WRAP1(func, arg_type, n) \ template \ inline void func(arg_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ - typedef typename fmt::internal::BasicArgMap::value_type ArgMapValue; \ const fmt::internal::ArgArray::Type array = {FMT_GEN(n, FMT_MAKE_REF)}; \ - ArgMapValue mapArray[n]; \ - ArgMapValue* mapPtr = mapArray; \ - FMT_GEN(n, FMT_ADD_NAMED_ARG); \ - fmt::internal::BasicArgMap map(mapArray, unsigned(mapPtr - mapArray)); \ func(arg1, fmt::ArgList( \ - fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array, &map)); \ + fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array)); \ } // Emulates a variadic function returning void on a pre-C++11 compiler. @@ -1725,14 +1689,9 @@ inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) { # define FMT_CTOR(ctor, func, arg0_type, arg1_type, n) \ template \ ctor(arg0_type arg0, arg1_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ - typedef typename fmt::internal::BasicArgMap::value_type ArgMapValue; \ const fmt::internal::ArgArray::Type array = {FMT_GEN(n, FMT_MAKE_REF)}; \ - ArgMapValue mapArray[n]; \ - ArgMapValue* mapPtr = mapArray; \ - FMT_GEN(n, FMT_ADD_NAMED_ARG); \ - fmt::internal::BasicArgMap map(mapArray, unsigned(mapPtr - mapArray)); \ func(arg0, arg1, fmt::ArgList( \ - fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array, &map)); \ + fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array)); \ } // Emulates a variadic constructor on a pre-C++11 compiler. @@ -2804,7 +2763,6 @@ inline void format_decimal(char *&buffer, T value) { buffer += num_digits; } - /** \rst Returns a named argument for formatting functions. @@ -2816,13 +2774,13 @@ inline void format_decimal(char *&buffer, T value) { \endrst */ template -inline internal::NamedArg arg(StringRef name, const T &arg) { - return internal::NamedArg(name, arg); +inline internal::NamedArg arg(StringRef name, const T &arg) { + return internal::NamedArg(name, arg); } template -inline internal::NamedArg arg(WStringRef name, const T &arg) { - return internal::NamedArg(name, arg); +inline internal::NamedArg arg(WStringRef name, const T &arg) { + return internal::NamedArg(name, arg); } } @@ -2859,29 +2817,19 @@ inline internal::NamedArg arg(WStringRef name, const T &arg) { ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ const Args & ... args) { \ typename fmt::internal::ArgArray::Type array; \ - const int count = fmt::internal::NamedArgsCounter::value; \ - fmt::internal::BasicArgMap::value_type mapArray[count + 1]; \ - fmt::internal::add_named_args<0>(mapArray, args...); \ - fmt::internal::BasicArgMap map(mapArray, count); \ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), \ - fmt::internal::make_arg_list(array, &map, fmt::internal::strip_name(args)...)); \ + fmt::internal::make_arg_list(array, args...)); \ } #else // Defines a wrapper for a function taking __VA_ARGS__ arguments // and n additional arguments of arbitrary types. -# define FMT_WRAP(Char_, ReturnType, func, call, n, ...) \ +# define FMT_WRAP(Char, ReturnType, func, call, n, ...) \ template \ inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ FMT_GEN(n, FMT_MAKE_ARG)) { \ - typedef Char_ Char; \ - typedef fmt::internal::BasicArgMap::value_type ArgMapValue; \ - fmt::internal::ArgArray::Type arr = {FMT_GEN(n, FMT_MAKE_REF)}; \ - ArgMapValue mapArray[n]; \ - ArgMapValue* mapPtr = mapArray; \ - FMT_GEN(n, FMT_ADD_NAMED_ARG); \ - fmt::internal::BasicArgMap map(mapArray, unsigned(mapPtr - mapArray)); \ + fmt::internal::ArgArray::Type arr = {FMT_GEN(n, FMT_MAKE_REF_##Char)}; \ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \ - fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), arr, &map)); \ + fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), arr)); \ } # define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ From bbcb6e839430807458d6d5bcaf73dbdbb4aaa2ab Mon Sep 17 00:00:00 2001 From: jamboree Date: Tue, 9 Jun 2015 16:13:43 +0800 Subject: [PATCH 08/11] Fix warning --- format.cc | 45 +++++++++++++++++++++++++++++++++++++++++++++ format.h | 49 ++++++------------------------------------------- 2 files changed, 51 insertions(+), 43 deletions(-) diff --git a/format.cc b/format.cc index 34c3405b..9d05991d 100644 --- a/format.cc +++ b/format.cc @@ -585,6 +585,49 @@ FMT_FUNC void fmt::internal::format_windows_error( } #endif +template +void fmt::ArgList::Map::init(const ArgList &args) { + if (!map_.empty()) + return; + const internal::NamedArg* named_arg; + bool use_values = args.type(MAX_PACKED_ARGS - 1) == internal::Arg::NONE; + if (use_values) { + for (unsigned i = 0;/*nothing*/; ++i) { + internal::Arg::Type arg_type = args.type(i); + switch (arg_type) { + case internal::Arg::NONE: + return; + case internal::Arg::NAMED_ARG: + named_arg = static_cast*>(args.values_[i].pointer); + map_.insert(Pair(named_arg->name, *named_arg)); + break; + default: + /*nothing*/; + } + } + return; + } + for (unsigned i = 0; i != MAX_PACKED_ARGS; ++i) { + internal::Arg::Type arg_type = args.type(i); + if (arg_type == internal::Arg::NAMED_ARG) { + named_arg = static_cast*>(args.args_[i].pointer); + map_.insert(Pair(named_arg->name, *named_arg)); + } + } + for (unsigned i = MAX_PACKED_ARGS;/*nothing*/; ++i) { + switch (args.args_[i].type) { + case internal::Arg::NONE: + return; + case internal::Arg::NAMED_ARG: + named_arg = static_cast*>(args.args_[i].pointer); + map_.insert(Pair(named_arg->name, *named_arg)); + break; + default: + /*nothing*/; + } + } +} + // An argument formatter. template class fmt::internal::ArgFormatter : @@ -736,6 +779,8 @@ FMT_FUNC Arg fmt::internal::FormatterBase::do_get_arg( break; case Arg::NAMED_ARG: arg = *static_cast(arg.pointer); + default: + /*nothing*/; } return arg; } diff --git a/format.h b/format.h index e9da7c4a..0157f939 100644 --- a/format.h +++ b/format.h @@ -1176,54 +1176,17 @@ struct fmt::ArgList::Map { typedef std::map, internal::Arg> MapType; typedef typename MapType::value_type Pair; - void init(const ArgList &args) { - if (!map_.empty()) - return; - bool use_values = args.type(MAX_PACKED_ARGS - 1) == internal::Arg::NONE; - if (use_values) { - add_packed(args, args.values_); - return; - } - add_packed(args, args.args_); - const internal::NamedArg* named_arg; - for (unsigned i = MAX_PACKED_ARGS; ; ++i) { - switch (args.args_[i].type) { - case internal::Arg::NONE: - return; - case internal::Arg::NAMED_ARG: - named_arg = static_cast*>(args.args_[i].pointer); - map_.insert(Pair(named_arg->name, *named_arg)); - break; - } - } - } + void init(const ArgList &args); - const internal::Arg* find(const fmt::BasicStringRef &name) const - { - typename MapType::const_iterator it = map_.find(name); - if (it != map_.end()) - return &it->second; - return 0; + const internal::Arg* find(const fmt::BasicStringRef &name) const { + typename MapType::const_iterator it = map_.find(name); + if (it != map_.end()) + return &it->second; + return 0; } private: - template - void add_packed(const ArgList &args, const T* data) { - const internal::NamedArg* named_arg; - for (unsigned i = 0; i != MAX_PACKED_ARGS; ++i) { - internal::Arg::Type arg_type = args.type(i); - switch (arg_type) { - case internal::Arg::NONE: - return; - case internal::Arg::NAMED_ARG: - named_arg = static_cast*>(data[i].pointer); - map_.insert(Pair(named_arg->name, *named_arg)); - break; - } - } - } - MapType map_; }; From 8bfc7dfcea825ea930e30ce154f460c3a7552ac0 Mon Sep 17 00:00:00 2001 From: jamboree Date: Tue, 9 Jun 2015 17:14:39 +0800 Subject: [PATCH 09/11] Prevent nested fmt::arg --- format.h | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/format.h b/format.h index 0157f939..0441a1ac 100644 --- a/format.h +++ b/format.h @@ -164,10 +164,12 @@ inline uint32_t clzll(uint64_t x) { // This should be used in the private: declarations for a class #if FMT_USE_DELETED_FUNCTIONS || FMT_HAS_FEATURE(cxx_deleted_functions) || \ (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1800 +# define FMT_DELETED_OR_UNDEFINED = delete # define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \ TypeName(const TypeName&) = delete; \ TypeName& operator=(const TypeName&) = delete #else +# define FMT_DELETED_OR_UNDEFINED # define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \ TypeName(const TypeName&); \ TypeName& operator=(const TypeName&) @@ -968,11 +970,9 @@ class MakeValue : public Arg { return IsConvertibleToInt::value ? Arg::INT : Arg::CUSTOM; } - template - MakeValue(const NamedArg &value) { pointer = &value; } + MakeValue(const NamedArg &value) { pointer = &value; } - template - static uint64_t type(const NamedArg &) { return Arg::NAMED_ARG; } + static uint64_t type(const NamedArg &) { return Arg::NAMED_ARG; } }; template @@ -2745,6 +2745,12 @@ template inline internal::NamedArg arg(WStringRef name, const T &arg) { return internal::NamedArg(name, arg); } + +template +void arg(StringRef name, const internal::NamedArg&) FMT_DELETED_OR_UNDEFINED; + +template +void arg(WStringRef name, const internal::NamedArg&) FMT_DELETED_OR_UNDEFINED; } #if FMT_GCC_VERSION From 3214fe1e46888050a9d3e0cc8121a275f6b333cf Mon Sep 17 00:00:00 2001 From: jamboree Date: Tue, 9 Jun 2015 18:29:32 +0800 Subject: [PATCH 10/11] Fix MakeValue --- format.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/format.h b/format.h index 0441a1ac..d02b2d4f 100644 --- a/format.h +++ b/format.h @@ -970,9 +970,12 @@ class MakeValue : public Arg { return IsConvertibleToInt::value ? Arg::INT : Arg::CUSTOM; } - MakeValue(const NamedArg &value) { pointer = &value; } + // Additional template param `Char_` is needed here because make_type always uses MakeValue. + template + MakeValue(const NamedArg &value) { pointer = &value; } - static uint64_t type(const NamedArg &) { return Arg::NAMED_ARG; } + template + static uint64_t type(const NamedArg &) { return Arg::NAMED_ARG; } }; template From a7275818f91c54b7e1a70550296394fd0ddb082c Mon Sep 17 00:00:00 2001 From: Jamboree Date: Tue, 9 Jun 2015 23:28:28 +0800 Subject: [PATCH 11/11] Add back test code due to merge mistake --- test/format-test.cc | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/format-test.cc b/test/format-test.cc index b86d78d2..f30498c6 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -932,6 +932,32 @@ TEST(FormatterTest, RuntimeWidth) { format_str[size + 2] = 0; EXPECT_THROW_MSG(format(format_str, 0), FormatError, "number is too big"); + EXPECT_THROW_MSG(format("{0:{", 0), + FormatError, "invalid format string"); + EXPECT_THROW_MSG(format("{0:{}", 0), + FormatError, "cannot switch from manual to automatic argument indexing"); + EXPECT_THROW_MSG(format("{0:{?}}", 0), + FormatError, "invalid format string"); + EXPECT_THROW_MSG(format("{0:{1}}", 0), + FormatError, "argument index out of range"); + + EXPECT_THROW_MSG(format("{0:{0:}}", 0), + FormatError, "invalid format string"); + + EXPECT_THROW_MSG(format("{0:{1}}", 0, -1), + FormatError, "negative width"); + EXPECT_THROW_MSG(format("{0:{1}}", 0, (INT_MAX + 1u)), + FormatError, "number is too big"); + EXPECT_THROW_MSG(format("{0:{1}}", 0, -1l), + FormatError, "negative width"); + if (fmt::internal::check(sizeof(long) > sizeof(int))) { + long value = INT_MAX; + EXPECT_THROW_MSG(format("{0:{1}}", 0, (value + 1)), + FormatError, "number is too big"); + } + EXPECT_THROW_MSG(format("{0:{1}}", 0, (INT_MAX + 1ul)), + FormatError, "number is too big"); + EXPECT_THROW_MSG(format("{0:{1}}", 0, '0'), FormatError, "width is not integer"); EXPECT_THROW_MSG(format("{0:{1}}", 0, 0.0),