From 6b3840b73c2520a658cf2cfa067f26771bf9ea70 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 22 Oct 2017 08:18:26 -0700 Subject: [PATCH] Make format_specs construction constexpr --- include/fmt/format.h | 54 +++++++++++++++++++--------------- test/format-test.cc | 70 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 84 insertions(+), 40 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index 62100abc..7b9a6e5b 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1630,12 +1630,13 @@ struct align_spec : empty_spec { wchar_t fill_; alignment align_; - align_spec(unsigned width, wchar_t fill, alignment align = ALIGN_DEFAULT) + constexpr align_spec( + unsigned width, wchar_t fill, alignment align = ALIGN_DEFAULT) : width_(width), fill_(fill), align_(align) {} - unsigned width() const { return width_; } - wchar_t fill() const { return fill_; } - alignment align() const { return align_; } + constexpr unsigned width() const { return width_; } + constexpr wchar_t fill() const { return fill_; } + constexpr alignment align() const { return align_; } int precision() const { return -1; } }; @@ -1670,7 +1671,8 @@ class basic_format_specs : public align_spec { int precision_; Char type_; - basic_format_specs(unsigned width = 0, char type = 0, wchar_t fill = ' ') + constexpr basic_format_specs( + unsigned width = 0, char type = 0, wchar_t fill = ' ') : align_spec(width, fill), flags_(0), precision_(-1), type_(type) {} template @@ -1679,9 +1681,9 @@ class basic_format_specs : public align_spec { set(specs...); } - bool flag(unsigned f) const { return (flags_ & f) != 0; } - int precision() const { return precision_; } - Char type() const { return type_; } + constexpr bool flag(unsigned f) const { return (flags_ & f) != 0; } + constexpr int precision() const { return precision_; } + constexpr Char type() const { return type_; } }; typedef basic_format_specs format_specs; @@ -3114,29 +3116,30 @@ struct error_handler { template class specs_setter : public error_handler { public: - explicit specs_setter(basic_format_specs &specs): specs_(specs) {} + explicit constexpr specs_setter(basic_format_specs &specs): + specs_(specs) {} - void on_align(alignment align) { specs_.align_ = align; } - void on_fill(Char fill) { specs_.fill_ = fill; } - void on_plus() { specs_.flags_ |= SIGN_FLAG | PLUS_FLAG; } - void on_minus() { specs_.flags_ |= MINUS_FLAG; } - void on_space() { specs_.flags_ |= SIGN_FLAG; } - void on_hash() { specs_.flags_ |= HASH_FLAG; } + constexpr void on_align(alignment align) { specs_.align_ = align; } + constexpr void on_fill(Char fill) { specs_.fill_ = fill; } + constexpr void on_plus() { specs_.flags_ |= SIGN_FLAG | PLUS_FLAG; } + constexpr void on_minus() { specs_.flags_ |= MINUS_FLAG; } + constexpr void on_space() { specs_.flags_ |= SIGN_FLAG; } + constexpr void on_hash() { specs_.flags_ |= HASH_FLAG; } - void on_zero() { + constexpr void on_zero() { specs_.align_ = ALIGN_NUMERIC; specs_.fill_ = '0'; } - void on_width(unsigned width) { specs_.width_ = width; } - void on_precision(unsigned precision) { specs_.precision_ = precision; } - void end_precision() {} + constexpr void on_width(unsigned width) { specs_.width_ = width; } + constexpr void on_precision(unsigned precision) { + specs_.precision_ = precision; + } + constexpr void end_precision() {} - void on_type(Char type) { specs_.type_ = type; } + constexpr void on_type(Char type) { specs_.type_ = type; } protected: - ~specs_setter() {} - basic_format_specs &specs_; }; @@ -3229,7 +3232,7 @@ class specs_handler: public specs_setter { public: typedef typename Context::char_type char_type; - specs_handler(basic_format_specs &specs, Context &ctx) + constexpr specs_handler(basic_format_specs &specs, Context &ctx) : specs_setter(specs), context_(ctx) {} template @@ -3508,8 +3511,11 @@ const Char *do_format_arg(basic_buffer &buffer, basic_format_specs specs; if (*it == ':') { ctx.advance_to(pointer_from(++it)); - if (visit(custom_formatter(buffer, ctx), arg)) + if (visit(custom_formatter(buffer, ctx), arg)) { + // TODO: if constexpr, then use formatter::parse, else dispatch + // dynamically return ctx.begin(); + } specs_checker> handler(specs_handler(specs, ctx), arg.type()); it = parse_format_specs(it, handler); diff --git a/test/format-test.cc b/test/format-test.cc index 27ad78c4..1cf6cd0d 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1619,7 +1619,7 @@ TEST(FormatTest, ConstexprParseArgID) { static_assert(parse_arg_id("!").res == test_arg_id_handler::ERROR, ""); } -struct test_format_specs_handlers { +struct test_format_specs_handler { enum Result { NONE, PLUS, MINUS, SPACE, HASH, ZERO, ERROR }; Result res = NONE; @@ -1656,24 +1656,62 @@ struct test_format_specs_handlers { constexpr void on_error(const char *) { res = ERROR; } }; -constexpr test_format_specs_handlers parse_specs(const char *s) { - test_format_specs_handlers h; +constexpr test_format_specs_handler parse_test_specs(const char *s) { + test_format_specs_handler 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("+").res == test_format_specs_handlers::PLUS, ""); - static_assert(parse_specs("-").res == test_format_specs_handlers::MINUS, ""); - static_assert(parse_specs(" ").res == test_format_specs_handlers::SPACE, ""); - static_assert(parse_specs("#").res == test_format_specs_handlers::HASH, ""); - static_assert(parse_specs("0").res == test_format_specs_handlers::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("{<").res == test_format_specs_handlers::ERROR, ""); + using handler = test_format_specs_handler; + static_assert(parse_test_specs("<").align == fmt::ALIGN_LEFT, ""); + static_assert(parse_test_specs("*^").fill == '*', ""); + static_assert(parse_test_specs("+").res == handler::PLUS, ""); + static_assert(parse_test_specs("-").res == handler::MINUS, ""); + static_assert(parse_test_specs(" ").res == handler::SPACE, ""); + static_assert(parse_test_specs("#").res == handler::HASH, ""); + static_assert(parse_test_specs("0").res == handler::ZERO, ""); + static_assert(parse_test_specs("42").width == 42, ""); + static_assert(parse_test_specs("{42}").width_ref.index == 42, ""); + static_assert(parse_test_specs(".42").precision == 42, ""); + static_assert(parse_test_specs(".{42}").precision_ref.index == 42, ""); + static_assert(parse_test_specs("d").type == 'd', ""); + static_assert(parse_test_specs("{<").res == handler::ERROR, ""); +} + +struct test_context { + using char_type = char; + + fmt::basic_arg next_arg() { + return fmt::basic_arg(); + } + + template + fmt::basic_arg get_arg(Id) { + return fmt::basic_arg(); + } + + template + void check_arg_id(Id) {} +}; + +constexpr fmt::format_specs parse_specs(const char *s) { + fmt::format_specs specs; + test_context ctx; + fmt::internal::specs_handler h(specs, ctx); + parse_format_specs(s, h); + return specs; +} + +TEST(FormatTest, ConstexprSpecsHandler) { + static_assert(parse_specs("<").align() == fmt::ALIGN_LEFT, ""); + static_assert(parse_specs("*^").fill() == '*', ""); + static_assert(parse_specs("+").flag(fmt::PLUS_FLAG), ""); + static_assert(parse_specs("-").flag(fmt::MINUS_FLAG), ""); + static_assert(parse_specs(" ").flag(fmt::SIGN_FLAG), ""); + static_assert(parse_specs("#").flag(fmt::HASH_FLAG), ""); + static_assert(parse_specs("0").align() == fmt::ALIGN_NUMERIC, ""); + static_assert(parse_specs("42").width() == 42, ""); + static_assert(parse_specs(".42").precision() == 42, ""); + static_assert(parse_specs("d").type() == 'd', ""); }