From e1ab6bc0067b709569778617de3799d9b3dc6e0c Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 31 Aug 2019 10:23:42 -0700 Subject: [PATCH] Simplify format string compilation --- include/fmt/compile.h | 192 +++++++++++++++++------------------------- test/compile-test.cc | 186 ---------------------------------------- 2 files changed, 75 insertions(+), 303 deletions(-) diff --git a/include/fmt/compile.h b/include/fmt/compile.h index 082b0762..486fe475 100644 --- a/include/fmt/compile.h +++ b/include/fmt/compile.h @@ -15,79 +15,51 @@ FMT_BEGIN_NAMESPACE namespace internal { template struct format_part { - public: - struct named_argument_id { - FMT_CONSTEXPR named_argument_id(internal::string_view_metadata id) - : id(id) {} - internal::string_view_metadata id; + struct named_arg_id { + FMT_CONSTEXPR named_arg_id(string_view_metadata id) : id(id) {} + string_view_metadata id; }; - struct argument_id { - FMT_CONSTEXPR argument_id() : argument_id(0u) {} + struct replacement { + FMT_CONSTEXPR replacement() : arg_id(0u) {} + FMT_CONSTEXPR replacement(unsigned id) : arg_id(id) {} - FMT_CONSTEXPR argument_id(unsigned id) - : which(which_arg_id::index), val(id) {} + FMT_CONSTEXPR replacement(string_view_metadata id) : arg_id(id) {} - FMT_CONSTEXPR argument_id(internal::string_view_metadata id) - : which(which_arg_id::named_index), val(id) {} - - enum class which_arg_id { index, named_index }; - - which_arg_id which; - - union value { - FMT_CONSTEXPR value() : index(0u) {} - FMT_CONSTEXPR value(unsigned id) : index(id) {} - FMT_CONSTEXPR value(internal::string_view_metadata id) - : named_index(id) {} - - unsigned index; - internal::string_view_metadata named_index; - } val; + arg_ref arg_id; + dynamic_format_specs parsed_specs; }; - struct specification { - FMT_CONSTEXPR specification() : arg_id(0u) {} - FMT_CONSTEXPR specification(unsigned id) : arg_id(id) {} + enum class kind { arg_index, arg_name, text, replacement }; - FMT_CONSTEXPR specification(internal::string_view_metadata id) - : arg_id(id) {} - - argument_id arg_id; - internal::dynamic_format_specs parsed_specs; - }; - - FMT_CONSTEXPR format_part() - : which(kind::argument_id), end_of_argument_id(0u), val(0u) {} - - FMT_CONSTEXPR format_part(internal::string_view_metadata text) - : which(kind::text), end_of_argument_id(0u), val(text) {} - - FMT_CONSTEXPR format_part(unsigned id) - : which(kind::argument_id), end_of_argument_id(0u), val(id) {} - - FMT_CONSTEXPR format_part(named_argument_id arg_id) - : which(kind::named_argument_id), end_of_argument_id(0u), val(arg_id) {} - - FMT_CONSTEXPR format_part(specification spec) - : which(kind::specification), end_of_argument_id(0u), val(spec) {} - - enum class kind { argument_id, named_argument_id, text, specification }; - - kind which; + kind part_kind; std::size_t end_of_argument_id; union value { - FMT_CONSTEXPR value() : arg_id(0u) {} - FMT_CONSTEXPR value(unsigned id) : arg_id(id) {} - FMT_CONSTEXPR value(named_argument_id named_id) - : named_arg_id(named_id.id) {} - FMT_CONSTEXPR value(internal::string_view_metadata t) : text(t) {} - FMT_CONSTEXPR value(specification s) : spec(s) {} - unsigned arg_id; - internal::string_view_metadata named_arg_id; - internal::string_view_metadata text; - specification spec; + FMT_CONSTEXPR value() : arg_index(0u) {} + FMT_CONSTEXPR value(unsigned id) : arg_index(id) {} + FMT_CONSTEXPR value(named_arg_id named_id) : arg_name(named_id.id) {} + FMT_CONSTEXPR value(string_view_metadata t) : text(t) {} + FMT_CONSTEXPR value(replacement r) : repl(r) {} + unsigned arg_index; + string_view_metadata arg_name; + string_view_metadata text; + replacement repl; } val; + + FMT_CONSTEXPR format_part() + : part_kind(kind::arg_index), end_of_argument_id(0u), val(0u) {} + + FMT_CONSTEXPR format_part(unsigned arg_index) + : part_kind(kind::arg_index), end_of_argument_id(0u), val(arg_index) {} + + FMT_CONSTEXPR format_part(named_arg_id arg_name) + : part_kind(kind::arg_name), end_of_argument_id(0u), val(arg_name) {} + + FMT_CONSTEXPR format_part(string_view_metadata text) + : part_kind(kind::text), end_of_argument_id(0u), val(text) {} + + FMT_CONSTEXPR format_part(replacement repl) + : part_kind(kind::replacement), end_of_argument_id(0u), val(repl) {} }; template @@ -96,8 +68,6 @@ class format_preparation_handler : public internal::error_handler { using part = format_part; public: - using iterator = typename basic_string_view::iterator; - FMT_CONSTEXPR format_preparation_handler(basic_string_view format, PartsContainer& parts) : parts_(parts), format_(format), parse_context_(format) {} @@ -120,7 +90,7 @@ class format_preparation_handler : public internal::error_handler { FMT_CONSTEXPR void on_arg_id(basic_string_view id) { const auto view = string_view_metadata(format_, id); - const auto arg_id = typename part::named_argument_id(view); + const auto arg_id = typename part::named_arg_id(view); parts_.push_back(part(arg_id)); } @@ -140,9 +110,9 @@ class format_preparation_handler : public internal::error_handler { if (*begin != '}') on_error("missing '}' in format string"); auto& last_part = parts_.back(); - auto specs = last_part.which == part::kind::argument_id - ? typename part::specification(last_part.val.arg_id) - : typename part::specification(last_part.val.named_arg_id); + auto specs = last_part.part_kind == part::kind::arg_index + ? typename part::replacement(last_part.val.arg_index) + : typename part::replacement(last_part.val.arg_name); specs.parsed_specs = parsed_specs; last_part = part(specs); last_part.end_of_argument_id = specs_offset; @@ -156,15 +126,15 @@ class format_preparation_handler : public internal::error_handler { }; template -class prepared_format { +class compiled_format { public: using char_type = char_t; using format_part_t = format_part; - constexpr prepared_format(Format f) + constexpr compiled_format(Format f) : format_(std::move(f)), parts_provider_(to_string_view(format_)) {} - prepared_format() = delete; + compiled_format() = delete; using context = buffer_context; @@ -180,7 +150,7 @@ class prepared_format { const auto& part = *part_it; const auto& value = part.val; - switch (part.which) { + switch (part.part_kind) { case format_part_t::kind::text: { const auto text = value.text.to_view(format_view.data()); auto output = ctx.out(); @@ -189,26 +159,24 @@ class prepared_format { ctx.advance_to(output); } break; - case format_part_t::kind::argument_id: { + case format_part_t::kind::arg_index: { advance_parse_context_to_specification(parse_ctx, part); - format_arg(parse_ctx, ctx, value.arg_id); + format_arg(parse_ctx, ctx, value.arg_index); } break; - case format_part_t::kind::named_argument_id: { + case format_part_t::kind::arg_name: { advance_parse_context_to_specification(parse_ctx, part); - const auto named_arg_id = - value.named_arg_id.to_view(format_view.data()); + const auto named_arg_id = value.arg_name.to_view(format_view.data()); format_arg(parse_ctx, ctx, named_arg_id); } break; - case format_part_t::kind::specification: { - const auto& arg_id_value = value.spec.arg_id.val; - const auto arg = value.spec.arg_id.which == - format_part_t::argument_id::which_arg_id::index + case format_part_t::kind::replacement: { + const auto& arg_id_value = value.repl.arg_id.val; + const auto arg = value.repl.arg_id.kind == arg_id_kind::index ? ctx.arg(arg_id_value.index) - : ctx.arg(arg_id_value.named_index.to_view( + : ctx.arg(arg_id_value.name.to_view( to_string_view(format_).data())); - auto specs = value.spec.parsed_specs; + auto specs = value.repl.parsed_specs; handle_dynamic_spec( specs.width, specs.width_ref, ctx, format_view.begin()); @@ -352,14 +320,6 @@ template class compiletime_prepared_parts_collector { unsigned counter_; }; -template -FMT_CONSTEXPR PartsContainer prepare_parts(basic_string_view format) { - PartsContainer parts; - internal::parse_format_string( - format, format_preparation_handler(format, parts)); - return parts; -} - template FMT_CONSTEXPR PartsContainer prepare_compiletime_parts(basic_string_view format) { @@ -372,30 +332,33 @@ prepare_compiletime_parts(basic_string_view format) { return parts; } -template class runtime_parts_provider { +template class runtime_parts_provider { public: - runtime_parts_provider() = delete; - template - runtime_parts_provider(basic_string_view format) - : parts_(prepare_parts(format)) {} + using parts_container = std::vector>; - const PartsContainer& parts() const { return parts_; } + runtime_parts_provider(basic_string_view format) { + internal::parse_format_string( + format, + format_preparation_handler(format, parts_)); + } + + const parts_container& parts() const { return parts_; } private: - PartsContainer parts_; + parts_container parts_; }; -template -struct compiletime_parts_provider { - compiletime_parts_provider() = delete; +template struct compiletime_parts_provider { + using parts_container = + typename internal::compiletime_prepared_parts_type_provider::type; + template FMT_CONSTEXPR compiletime_parts_provider(basic_string_view) {} - const PartsContainer& parts() const { - static FMT_CONSTEXPR_DECL const PartsContainer prepared_parts = - prepare_compiletime_parts( - internal::to_string_view(Format{})); - + const parts_container& parts() const { + static FMT_CONSTEXPR_DECL const parts_container prepared_parts = + prepare_compiletime_parts( + internal::to_string_view(Format())); return prepared_parts; } }; @@ -404,21 +367,16 @@ struct compiletime_parts_provider { #if FMT_USE_CONSTEXPR template ::value)> -FMT_CONSTEXPR auto compile(S format_str) -> internal::prepared_format< - S, - internal::compiletime_parts_provider< - S, - typename internal::compiletime_prepared_parts_type_provider::type>, - Args...> { +FMT_CONSTEXPR auto compile(S format_str) + -> internal::compiled_format, + Args...> { return format_str; } #endif template -auto compile(const Char (&format_str)[N]) -> internal::prepared_format< - std::basic_string, - internal::runtime_parts_provider>>, - Args...> { +auto compile(const Char (&format_str)[N]) -> internal::compiled_format< + std::basic_string, internal::runtime_parts_provider, Args...> { return std::basic_string(format_str, N - 1); } diff --git a/test/compile-test.cc b/test/compile-test.cc index 32a1da0a..73de9a55 100644 --- a/test/compile-test.cc +++ b/test/compile-test.cc @@ -34,192 +34,6 @@ using testing::Return; using testing::StrictMock; -FMT_BEGIN_NAMESPACE -namespace internal { -bool operator==(const internal::string_view_metadata& lhs, - const internal::string_view_metadata& rhs) { - return std::tie(lhs.offset_, lhs.size_) == std::tie(rhs.offset_, rhs.size_); -} -bool operator!=(const internal::string_view_metadata& lhs, - const internal::string_view_metadata& rhs) { - return !(lhs == rhs); -} - -bool operator==(const format_part::specification& lhs, - const format_part::specification& rhs) { - if (lhs.arg_id.which != rhs.arg_id.which) { - return false; - } - - typedef format_part::argument_id::which_arg_id which_arg_id; - switch (lhs.arg_id.which) { - case which_arg_id::index: { - if (lhs.arg_id.val.index != rhs.arg_id.val.index) { - return false; - } - } break; - case which_arg_id::named_index: { - if (lhs.arg_id.val.named_index != rhs.arg_id.val.named_index) { - return false; - } - } break; - } - - return std::tie(lhs.parsed_specs.width, lhs.parsed_specs.fill[0], - lhs.parsed_specs.align, lhs.parsed_specs.precision, - lhs.parsed_specs.sign, lhs.parsed_specs.type) == - std::tie(rhs.parsed_specs.width, rhs.parsed_specs.fill[0], - rhs.parsed_specs.align, rhs.parsed_specs.precision, - rhs.parsed_specs.sign, rhs.parsed_specs.type); -} - -bool operator!=(const format_part::specification& lhs, - const format_part::specification& rhs) { - return !(lhs == rhs); -} - -bool operator==(const format_part& lhs, - const fmt::internal::format_part& rhs) { - typedef format_part::kind kind; - - if (lhs.which != rhs.which || - lhs.end_of_argument_id != rhs.end_of_argument_id) { - return false; - } - - switch (lhs.which) { - case kind::argument_id: { - return lhs.val.arg_id == rhs.val.arg_id; - } - - case kind::named_argument_id: { - return lhs.val.named_arg_id == rhs.val.named_arg_id; - } - - case kind::text: { - return lhs.val.text == rhs.val.text; - } - - case kind::specification: { - return lhs.val.spec == rhs.val.spec; - } - } - - return false; -} - -bool operator!=(const fmt::internal::format_part& lhs, - const fmt::internal::format_part& rhs) { - return !(lhs == rhs); -} -} -FMT_END_NAMESPACE - -TEST(CompileTest, FormatPart_ComparisonOperators) { - typedef fmt::internal::format_part format_part; - typedef fmt::internal::dynamic_format_specs prepared_specs; - - { - const auto part = format_part(0u); - const auto other = format_part(0u); - EXPECT_EQ(part, other); - } - { - const auto lhs = format_part(0u); - const auto rhs = format_part(1u); - EXPECT_NE(lhs, rhs); - } - { - const auto lhs = format_part(fmt::internal::string_view_metadata(0, 42)); - const auto rhs = format_part(fmt::internal::string_view_metadata(0, 42)); - EXPECT_EQ(lhs, rhs); - } - { - const auto lhs = format_part(fmt::internal::string_view_metadata(0, 42)); - const auto rhs = format_part(fmt::internal::string_view_metadata(0, 4422)); - EXPECT_NE(lhs, rhs); - } - { - auto lhs = format_part(0u); - auto rhs = format_part(fmt::internal::string_view_metadata(0, 42)); - EXPECT_NE(lhs, rhs); - rhs = format_part(fmt::internal::string_view_metadata(0, 0)); - EXPECT_NE(lhs, rhs); - } - { - auto lhs = format_part(0u); - lhs.end_of_argument_id = 42; - auto rhs = format_part(0u); - rhs.end_of_argument_id = 42; - EXPECT_EQ(lhs, rhs); - rhs.end_of_argument_id = 13; - EXPECT_NE(lhs, rhs); - } - { - const auto specs_argument_id = 0u; - const auto specs_named_argument_id = - fmt::internal::string_view_metadata(0, 42); - auto specs = format_part::specification(0u); - auto lhs = format_part(specs); - auto rhs = format_part(specs); - EXPECT_EQ(lhs, rhs); - - specs.parsed_specs = prepared_specs(); - lhs = format_part(specs); - rhs = format_part(specs); - EXPECT_EQ(lhs, rhs); - - specs = format_part::specification(specs_named_argument_id); - lhs = format_part(specs); - rhs = format_part(specs); - EXPECT_EQ(lhs, rhs); - - specs.parsed_specs = prepared_specs(); - lhs = format_part(specs); - rhs = format_part(specs); - EXPECT_EQ(lhs, rhs); - - auto lhs_spec = format_part::specification(specs_argument_id); - auto rhs_spec = format_part::specification(specs_named_argument_id); - lhs = format_part(lhs_spec); - rhs = format_part(rhs_spec); - EXPECT_NE(lhs, rhs); - - lhs_spec = format_part::specification(specs_argument_id); - rhs_spec = format_part::specification(specs_argument_id); - lhs_spec.parsed_specs.precision = 1; - rhs_spec.parsed_specs.precision = 2; - lhs = format_part(lhs_spec); - rhs = format_part(rhs_spec); - EXPECT_NE(lhs, rhs); - } - { - const auto specs_argument_id = 0u; - const auto specs_named_argument_id = - fmt::internal::string_view_metadata(0, 42); - auto specs = format_part::specification(specs_argument_id); - auto lhs = format_part(specs); - auto rhs = format_part(0u); - auto rhs2 = format_part(fmt::internal::string_view_metadata(0, 42)); - EXPECT_NE(lhs, rhs); - EXPECT_NE(lhs, rhs2); - - specs.parsed_specs = prepared_specs(); - lhs = format_part{specs}; - EXPECT_NE(lhs, rhs); - EXPECT_NE(lhs, rhs2); - - specs = format_part::specification(specs_named_argument_id); - EXPECT_NE(lhs, rhs); - EXPECT_NE(lhs, rhs2); - - specs.parsed_specs = prepared_specs(); - lhs = format_part(specs); - EXPECT_NE(lhs, rhs); - EXPECT_NE(lhs, rhs2); - } -} - // compiletime_prepared_parts_type_provider is useful only with relaxed // constexpr. #if FMT_USE_CONSTEXPR