diff --git a/fmt/format.cc b/fmt/format.cc index e759084b..bd12cb9b 100644 --- a/fmt/format.cc +++ b/fmt/format.cc @@ -58,9 +58,11 @@ # define FMT_CATCH(x) if (false) #endif +#ifdef __GNUC__ // Disable the warning about declaration shadowing because it affects too // many valid cases. -#pragma GCC diagnostic ignored "-Wshadow" +# pragma GCC diagnostic ignored "-Wshadow" +#endif #ifdef _MSC_VER # pragma warning(push) diff --git a/fmt/format.h b/fmt/format.h index 7110827e..37a19c92 100644 --- a/fmt/format.h +++ b/fmt/format.h @@ -3263,10 +3263,16 @@ template struct arg_ref { enum Kind { NONE, INDEX, NAME }; - arg_ref() : kind(NONE) {} + constexpr arg_ref() : kind(NONE), index(0) {} explicit arg_ref(unsigned index) : kind(INDEX), index(index) {} explicit arg_ref(basic_string_view name) : kind(NAME), name(name) {} + constexpr arg_ref &operator=(unsigned index) { + kind = INDEX; + this->index = index; + return *this; + } + Kind kind; union { unsigned index; @@ -3361,13 +3367,14 @@ constexpr Iterator parse_arg_id(Iterator it, Handler& handler) { // characters, possibly emulated via null_terminating_iterator, representing // format specifiers. template -Iterator parse_format_specs(Iterator it, Handler &handler) { +constexpr Iterator parse_format_specs(Iterator it, Handler &handler) { using char_type = typename std::iterator_traits::value_type; // Parse fill and alignment. if (char_type c = *it) { - auto p = it + 1; alignment align = ALIGN_DEFAULT; + int i = 1; do { + auto p = it + i; switch (*p) { case '<': align = ALIGN_LEFT; @@ -3395,7 +3402,7 @@ Iterator parse_format_specs(Iterator it, Handler &handler) { } else ++it; break; } - } while (--p >= it); + } while (--i >= 0); } // Parse sign. @@ -3430,15 +3437,15 @@ Iterator parse_format_specs(Iterator it, Handler &handler) { handler.on_width(parse_nonnegative_int(it)); } else if (*it == '{') { struct width_handler { - explicit width_handler(Handler &h) : handler(h) {} + explicit constexpr width_handler(Handler &h) : handler(h) {} - void operator()() { handler.on_dynamic_width(auto_id()); } - void operator()(unsigned id) { handler.on_dynamic_width(id); } - void operator()(basic_string_view id) { + constexpr void operator()() { handler.on_dynamic_width(auto_id()); } + constexpr void operator()(unsigned id) { handler.on_dynamic_width(id); } + constexpr void operator()(basic_string_view id) { handler.on_dynamic_width(id); } - void on_error(const char *message) { + constexpr void on_error(const char *message) { handler.on_error(message); } @@ -3458,15 +3465,17 @@ Iterator parse_format_specs(Iterator it, Handler &handler) { handler.on_precision(parse_nonnegative_int(it)); } else if (*it == '{') { struct precision_handler { - explicit precision_handler(Handler &h) : handler(h) {} + explicit constexpr precision_handler(Handler &h) : handler(h) {} - void operator()() { handler.on_dynamic_precision(auto_id()); } - void operator()(unsigned id) { handler.on_dynamic_precision(id); } - void operator()(basic_string_view id) { + constexpr void operator()() { handler.on_dynamic_precision(auto_id()); } + constexpr void operator()(unsigned id) { + handler.on_dynamic_precision(id); + } + constexpr void operator()(basic_string_view id) { handler.on_dynamic_precision(id); } - void on_error(const char *message) { + constexpr void on_error(const char *message) { handler.on_error(message); } diff --git a/test/format-test.cc b/test/format-test.cc index a857f7ed..f0c71b9a 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1582,7 +1582,7 @@ TEST(FormatTest, DynamicFormatter) { format_error, "precision not allowed in integer format specifier"); } -struct TestHandler { +struct TestArgIDHandler { enum Result { NONE, EMPTY, INDEX, NAME, ERROR }; Result result = NONE; unsigned index = 0; @@ -1603,18 +1603,77 @@ struct TestHandler { constexpr void on_error(const char *) { result = ERROR; } }; -constexpr TestHandler parse_arg_id(const char* id) { - TestHandler h; - fmt::internal::parse_arg_id(id, h); +constexpr TestArgIDHandler parse_arg_id(const char* s) { + TestArgIDHandler h; + fmt::internal::parse_arg_id(s, h); return h; } -TEST(FormatTest, ConstexprParseArgId) { - static_assert(parse_arg_id(":").result == TestHandler::EMPTY, ""); - static_assert(parse_arg_id("}").result == TestHandler::EMPTY, ""); - static_assert(parse_arg_id("42:").result == TestHandler::INDEX, ""); +TEST(FormatTest, ConstexprParseArgID) { + static_assert(parse_arg_id(":").result == TestArgIDHandler::EMPTY, ""); + static_assert(parse_arg_id("}").result == TestArgIDHandler::EMPTY, ""); + static_assert(parse_arg_id("42:").result == TestArgIDHandler::INDEX, ""); static_assert(parse_arg_id("42:").index == 42, ""); - static_assert(parse_arg_id("foo:").result == TestHandler::NAME, ""); + static_assert(parse_arg_id("foo:").result == TestArgIDHandler::NAME, ""); static_assert(parse_arg_id("foo:").name.size() == 3, ""); - static_assert(parse_arg_id("!").result == TestHandler::ERROR, ""); + static_assert(parse_arg_id("!").result == TestArgIDHandler::ERROR, ""); +} + +struct TestFormatSpecsHandler { + enum Result { NONE, PLUS, MINUS, SPACE, HASH, ZERO, ERROR }; + Result result = NONE; + + fmt::alignment align = fmt::ALIGN_DEFAULT; + char fill = 0; + unsigned width = 0; + fmt::internal::arg_ref width_ref; + unsigned precision = 0; + fmt::internal::arg_ref precision_ref; + char type = 0; + + constexpr void on_align(fmt::alignment align) { this->align = align; } + constexpr void on_fill(char fill) { this->fill = fill; } + constexpr void on_plus() { result = PLUS; } + constexpr void on_minus() { result = MINUS; } + constexpr void on_space() { result = SPACE; } + constexpr void on_hash() { result = HASH; } + constexpr void on_zero() { result = ZERO; } + + constexpr void on_width(unsigned width) { this->width = width; } + constexpr void on_dynamic_width(fmt::internal::auto_id) {} + constexpr void on_dynamic_width(unsigned index) { width_ref = index; } + constexpr void on_dynamic_width(string_view) {} + + constexpr void on_precision(unsigned precision) { + this->precision = precision; + } + constexpr void on_dynamic_precision(fmt::internal::auto_id) {} + constexpr void on_dynamic_precision(unsigned index) { precision_ref = index; } + constexpr void on_dynamic_precision(string_view) {} + + constexpr void end_precision() {} + constexpr void on_type(char type) { this->type = type; } + constexpr void on_error(const char *) { result = ERROR; } +}; + +constexpr TestFormatSpecsHandler parse_specs(const char *s) { + TestFormatSpecsHandler h; + fmt::internal::parse_format_specs(s, h); + return h; +} + +TEST(FormatTest, ConstexprParseFormatSpecs) { + static_assert(parse_specs("<").align == fmt::ALIGN_LEFT, ""); + static_assert(parse_specs("*^").fill == '*', ""); + static_assert(parse_specs("+").result == TestFormatSpecsHandler::PLUS, ""); + static_assert(parse_specs("-").result == TestFormatSpecsHandler::MINUS, ""); + static_assert(parse_specs(" ").result == TestFormatSpecsHandler::SPACE, ""); + static_assert(parse_specs("#").result == TestFormatSpecsHandler::HASH, ""); + static_assert(parse_specs("0").result == TestFormatSpecsHandler::ZERO, ""); + static_assert(parse_specs("42").width == 42, ""); + static_assert(parse_specs("{42}").width_ref.index == 42, ""); + static_assert(parse_specs(".42").precision == 42, ""); + static_assert(parse_specs(".{42}").precision_ref.index == 42, ""); + static_assert(parse_specs("d").type == 'd', ""); + static_assert(parse_specs("{<").result == TestFormatSpecsHandler::ERROR, ""); }