Commit e1ab6bc0 authored by Victor Zverovich's avatar Victor Zverovich

Simplify format string compilation

parent 24a88545
...@@ -15,79 +15,51 @@ FMT_BEGIN_NAMESPACE ...@@ -15,79 +15,51 @@ FMT_BEGIN_NAMESPACE
namespace internal { namespace internal {
template <typename Char> struct format_part { template <typename Char> struct format_part {
public: struct named_arg_id {
struct named_argument_id { FMT_CONSTEXPR named_arg_id(string_view_metadata id) : id(id) {}
FMT_CONSTEXPR named_argument_id(internal::string_view_metadata id) string_view_metadata id;
: id(id) {}
internal::string_view_metadata id;
}; };
struct argument_id { struct replacement {
FMT_CONSTEXPR argument_id() : argument_id(0u) {} 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 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; FMT_CONSTEXPR replacement(string_view_metadata id) : arg_id(id) {}
union value { arg_ref<Char> arg_id;
FMT_CONSTEXPR value() : index(0u) {} dynamic_format_specs<Char> parsed_specs;
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;
}; };
struct specification { enum class kind { arg_index, arg_name, text, replacement };
FMT_CONSTEXPR specification() : arg_id(0u) {}
FMT_CONSTEXPR specification(unsigned id) : arg_id(id) {}
FMT_CONSTEXPR specification(internal::string_view_metadata id)
: arg_id(id) {}
argument_id arg_id; kind part_kind;
internal::dynamic_format_specs<Char> parsed_specs; std::size_t end_of_argument_id;
}; union value {
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() FMT_CONSTEXPR format_part()
: which(kind::argument_id), end_of_argument_id(0u), val(0u) {} : part_kind(kind::arg_index), end_of_argument_id(0u), val(0u) {}
FMT_CONSTEXPR format_part(internal::string_view_metadata text) FMT_CONSTEXPR format_part(unsigned arg_index)
: which(kind::text), end_of_argument_id(0u), val(text) {} : part_kind(kind::arg_index), end_of_argument_id(0u), val(arg_index) {}
FMT_CONSTEXPR format_part(unsigned id) FMT_CONSTEXPR format_part(named_arg_id arg_name)
: which(kind::argument_id), end_of_argument_id(0u), val(id) {} : part_kind(kind::arg_name), end_of_argument_id(0u), val(arg_name) {}
FMT_CONSTEXPR format_part(named_argument_id arg_id) FMT_CONSTEXPR format_part(string_view_metadata text)
: which(kind::named_argument_id), end_of_argument_id(0u), val(arg_id) {} : part_kind(kind::text), end_of_argument_id(0u), val(text) {}
FMT_CONSTEXPR format_part(specification spec) FMT_CONSTEXPR format_part(replacement repl)
: which(kind::specification), end_of_argument_id(0u), val(spec) {} : part_kind(kind::replacement), end_of_argument_id(0u), val(repl) {}
enum class kind { argument_id, named_argument_id, text, specification };
kind which;
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;
} val;
}; };
template <typename Char, typename PartsContainer> template <typename Char, typename PartsContainer>
...@@ -96,8 +68,6 @@ class format_preparation_handler : public internal::error_handler { ...@@ -96,8 +68,6 @@ class format_preparation_handler : public internal::error_handler {
using part = format_part<Char>; using part = format_part<Char>;
public: public:
using iterator = typename basic_string_view<Char>::iterator;
FMT_CONSTEXPR format_preparation_handler(basic_string_view<Char> format, FMT_CONSTEXPR format_preparation_handler(basic_string_view<Char> format,
PartsContainer& parts) PartsContainer& parts)
: parts_(parts), format_(format), parse_context_(format) {} : parts_(parts), format_(format), parse_context_(format) {}
...@@ -120,7 +90,7 @@ class format_preparation_handler : public internal::error_handler { ...@@ -120,7 +90,7 @@ class format_preparation_handler : public internal::error_handler {
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) { FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) {
const auto view = string_view_metadata(format_, 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)); parts_.push_back(part(arg_id));
} }
...@@ -140,9 +110,9 @@ class format_preparation_handler : public internal::error_handler { ...@@ -140,9 +110,9 @@ class format_preparation_handler : public internal::error_handler {
if (*begin != '}') on_error("missing '}' in format string"); if (*begin != '}') on_error("missing '}' in format string");
auto& last_part = parts_.back(); auto& last_part = parts_.back();
auto specs = last_part.which == part::kind::argument_id auto specs = last_part.part_kind == part::kind::arg_index
? typename part::specification(last_part.val.arg_id) ? typename part::replacement(last_part.val.arg_index)
: typename part::specification(last_part.val.named_arg_id); : typename part::replacement(last_part.val.arg_name);
specs.parsed_specs = parsed_specs; specs.parsed_specs = parsed_specs;
last_part = part(specs); last_part = part(specs);
last_part.end_of_argument_id = specs_offset; last_part.end_of_argument_id = specs_offset;
...@@ -156,15 +126,15 @@ class format_preparation_handler : public internal::error_handler { ...@@ -156,15 +126,15 @@ class format_preparation_handler : public internal::error_handler {
}; };
template <typename Format, typename PreparedPartsProvider, typename... Args> template <typename Format, typename PreparedPartsProvider, typename... Args>
class prepared_format { class compiled_format {
public: public:
using char_type = char_t<Format>; using char_type = char_t<Format>;
using format_part_t = format_part<char_type>; using format_part_t = format_part<char_type>;
constexpr prepared_format(Format f) constexpr compiled_format(Format f)
: format_(std::move(f)), parts_provider_(to_string_view(format_)) {} : format_(std::move(f)), parts_provider_(to_string_view(format_)) {}
prepared_format() = delete; compiled_format() = delete;
using context = buffer_context<char_type>; using context = buffer_context<char_type>;
...@@ -180,7 +150,7 @@ class prepared_format { ...@@ -180,7 +150,7 @@ class prepared_format {
const auto& part = *part_it; const auto& part = *part_it;
const auto& value = part.val; const auto& value = part.val;
switch (part.which) { switch (part.part_kind) {
case format_part_t::kind::text: { case format_part_t::kind::text: {
const auto text = value.text.to_view(format_view.data()); const auto text = value.text.to_view(format_view.data());
auto output = ctx.out(); auto output = ctx.out();
...@@ -189,26 +159,24 @@ class prepared_format { ...@@ -189,26 +159,24 @@ class prepared_format {
ctx.advance_to(output); ctx.advance_to(output);
} break; } break;
case format_part_t::kind::argument_id: { case format_part_t::kind::arg_index: {
advance_parse_context_to_specification(parse_ctx, part); advance_parse_context_to_specification(parse_ctx, part);
format_arg<Range>(parse_ctx, ctx, value.arg_id); format_arg<Range>(parse_ctx, ctx, value.arg_index);
} break; } break;
case format_part_t::kind::named_argument_id: { case format_part_t::kind::arg_name: {
advance_parse_context_to_specification(parse_ctx, part); advance_parse_context_to_specification(parse_ctx, part);
const auto named_arg_id = const auto named_arg_id = value.arg_name.to_view(format_view.data());
value.named_arg_id.to_view(format_view.data());
format_arg<Range>(parse_ctx, ctx, named_arg_id); format_arg<Range>(parse_ctx, ctx, named_arg_id);
} break; } break;
case format_part_t::kind::specification: { case format_part_t::kind::replacement: {
const auto& arg_id_value = value.spec.arg_id.val; const auto& arg_id_value = value.repl.arg_id.val;
const auto arg = value.spec.arg_id.which == const auto arg = value.repl.arg_id.kind == arg_id_kind::index
format_part_t::argument_id::which_arg_id::index
? ctx.arg(arg_id_value.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())); to_string_view(format_).data()));
auto specs = value.spec.parsed_specs; auto specs = value.repl.parsed_specs;
handle_dynamic_spec<internal::width_checker>( handle_dynamic_spec<internal::width_checker>(
specs.width, specs.width_ref, ctx, format_view.begin()); specs.width, specs.width_ref, ctx, format_view.begin());
...@@ -352,14 +320,6 @@ template <typename Parts> class compiletime_prepared_parts_collector { ...@@ -352,14 +320,6 @@ template <typename Parts> class compiletime_prepared_parts_collector {
unsigned counter_; unsigned counter_;
}; };
template <typename PartsContainer, typename Char>
FMT_CONSTEXPR PartsContainer prepare_parts(basic_string_view<Char> format) {
PartsContainer parts;
internal::parse_format_string</*IS_CONSTEXPR=*/false>(
format, format_preparation_handler<Char, PartsContainer>(format, parts));
return parts;
}
template <typename PartsContainer, typename Char> template <typename PartsContainer, typename Char>
FMT_CONSTEXPR PartsContainer FMT_CONSTEXPR PartsContainer
prepare_compiletime_parts(basic_string_view<Char> format) { prepare_compiletime_parts(basic_string_view<Char> format) {
...@@ -372,30 +332,33 @@ prepare_compiletime_parts(basic_string_view<Char> format) { ...@@ -372,30 +332,33 @@ prepare_compiletime_parts(basic_string_view<Char> format) {
return parts; return parts;
} }
template <typename PartsContainer> class runtime_parts_provider { template <typename Char> class runtime_parts_provider {
public: public:
runtime_parts_provider() = delete; using parts_container = std::vector<internal::format_part<Char>>;
template <typename Char>
runtime_parts_provider(basic_string_view<Char> format) runtime_parts_provider(basic_string_view<Char> format) {
: parts_(prepare_parts<PartsContainer>(format)) {} internal::parse_format_string<false>(
format,
format_preparation_handler<Char, parts_container>(format, parts_));
}
const PartsContainer& parts() const { return parts_; } const parts_container& parts() const { return parts_; }
private: private:
PartsContainer parts_; parts_container parts_;
}; };
template <typename Format, typename PartsContainer> template <typename Format> struct compiletime_parts_provider {
struct compiletime_parts_provider { using parts_container =
compiletime_parts_provider() = delete; typename internal::compiletime_prepared_parts_type_provider<Format>::type;
template <typename Char> template <typename Char>
FMT_CONSTEXPR compiletime_parts_provider(basic_string_view<Char>) {} FMT_CONSTEXPR compiletime_parts_provider(basic_string_view<Char>) {}
const PartsContainer& parts() const { const parts_container& parts() const {
static FMT_CONSTEXPR_DECL const PartsContainer prepared_parts = static FMT_CONSTEXPR_DECL const parts_container prepared_parts =
prepare_compiletime_parts<PartsContainer>( prepare_compiletime_parts<parts_container>(
internal::to_string_view(Format{})); internal::to_string_view(Format()));
return prepared_parts; return prepared_parts;
} }
}; };
...@@ -404,21 +367,16 @@ struct compiletime_parts_provider { ...@@ -404,21 +367,16 @@ struct compiletime_parts_provider {
#if FMT_USE_CONSTEXPR #if FMT_USE_CONSTEXPR
template <typename... Args, typename S, template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)> FMT_ENABLE_IF(is_compile_string<S>::value)>
FMT_CONSTEXPR auto compile(S format_str) -> internal::prepared_format< FMT_CONSTEXPR auto compile(S format_str)
S, -> internal::compiled_format<S, internal::compiletime_parts_provider<S>,
internal::compiletime_parts_provider< Args...> {
S,
typename internal::compiletime_prepared_parts_type_provider<S>::type>,
Args...> {
return format_str; return format_str;
} }
#endif #endif
template <typename... Args, typename Char, size_t N> template <typename... Args, typename Char, size_t N>
auto compile(const Char (&format_str)[N]) -> internal::prepared_format< auto compile(const Char (&format_str)[N]) -> internal::compiled_format<
std::basic_string<Char>, std::basic_string<Char>, internal::runtime_parts_provider<Char>, Args...> {
internal::runtime_parts_provider<std::vector<internal::format_part<Char>>>,
Args...> {
return std::basic_string<Char>(format_str, N - 1); return std::basic_string<Char>(format_str, N - 1);
} }
......
...@@ -34,192 +34,6 @@ ...@@ -34,192 +34,6 @@
using testing::Return; using testing::Return;
using testing::StrictMock; 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<char>::specification& lhs,
const format_part<char>::specification& rhs) {
if (lhs.arg_id.which != rhs.arg_id.which) {
return false;
}
typedef format_part<char>::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<char>::specification& lhs,
const format_part<char>::specification& rhs) {
return !(lhs == rhs);
}
bool operator==(const format_part<char>& lhs,
const fmt::internal::format_part<char>& rhs) {
typedef format_part<char>::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<char>& lhs,
const fmt::internal::format_part<char>& rhs) {
return !(lhs == rhs);
}
}
FMT_END_NAMESPACE
TEST(CompileTest, FormatPart_ComparisonOperators) {
typedef fmt::internal::format_part<char> format_part;
typedef fmt::internal::dynamic_format_specs<char> 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 // compiletime_prepared_parts_type_provider is useful only with relaxed
// constexpr. // constexpr.
#if FMT_USE_CONSTEXPR #if FMT_USE_CONSTEXPR
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment