mirror of
https://github.com/fmtlib/fmt.git
synced 2024-11-19 11:14:41 +00:00
Simplify format string compilation
This commit is contained in:
parent
e2e557e273
commit
4ce006fb6e
@ -113,28 +113,26 @@ class format_preparation_handler : public internal::error_handler {
|
|||||||
if (begin == end) return;
|
if (begin == end) return;
|
||||||
const auto offset = begin - format_.data();
|
const auto offset = begin - format_.data();
|
||||||
const auto size = end - begin;
|
const auto size = end - begin;
|
||||||
parts_.add(part(string_view_metadata(offset, size)));
|
parts_.push_back(part(string_view_metadata(offset, size)));
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_arg_id() {
|
FMT_CONSTEXPR void on_arg_id() {
|
||||||
parts_.add(part(parse_context_.next_arg_id()));
|
parts_.push_back(part(parse_context_.next_arg_id()));
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_arg_id(unsigned id) {
|
FMT_CONSTEXPR void on_arg_id(unsigned id) {
|
||||||
parse_context_.check_arg_id(id);
|
parse_context_.check_arg_id(id);
|
||||||
parts_.add(part(id));
|
parts_.push_back(part(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
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_argument_id(view);
|
||||||
parts_.add(part(arg_id));
|
parts_.push_back(part(arg_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_replacement_field(const Char* ptr) {
|
FMT_CONSTEXPR void on_replacement_field(const Char* ptr) {
|
||||||
auto last_part = parts_.last();
|
parts_.back().end_of_argument_id = ptr - format_.begin();
|
||||||
last_part.end_of_argument_id = ptr - format_.begin();
|
|
||||||
parts_.substitute_last(last_part);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
|
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
|
||||||
@ -148,19 +146,13 @@ class format_preparation_handler : public internal::error_handler {
|
|||||||
|
|
||||||
if (*begin != '}') on_error("missing '}' in format string");
|
if (*begin != '}') on_error("missing '}' in format string");
|
||||||
|
|
||||||
const auto last_part = parts_.last();
|
auto& last_part = parts_.back();
|
||||||
|
|
||||||
auto specs = last_part.which == part::which_value::argument_id
|
auto specs = last_part.which == part::which_value::argument_id
|
||||||
? typename part::specification(last_part.val.arg_id)
|
? typename part::specification(last_part.val.arg_id)
|
||||||
: typename part::specification(last_part.val.named_arg_id);
|
: typename part::specification(last_part.val.named_arg_id);
|
||||||
|
|
||||||
specs.parsed_specs = parsed_specs;
|
specs.parsed_specs = parsed_specs;
|
||||||
|
last_part = part(specs);
|
||||||
auto new_part = part(specs);
|
last_part.end_of_argument_id = specs_offset;
|
||||||
new_part.end_of_argument_id = specs_offset;
|
|
||||||
|
|
||||||
parts_.substitute_last(new_part);
|
|
||||||
|
|
||||||
return begin;
|
return begin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -346,7 +338,6 @@ template <typename Format> class compiletime_prepared_parts_type_provider {
|
|||||||
FMT_CONSTEXPR value_type& operator[](unsigned ind) { return arr[ind]; }
|
FMT_CONSTEXPR value_type& operator[](unsigned ind) { return arr[ind]; }
|
||||||
|
|
||||||
FMT_CONSTEXPR const value_type* begin() const { return arr; }
|
FMT_CONSTEXPR const value_type* begin() const { return arr; }
|
||||||
|
|
||||||
FMT_CONSTEXPR const value_type* end() const { return begin() + N; }
|
FMT_CONSTEXPR const value_type* end() const { return begin() + N; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -370,13 +361,9 @@ template <typename Parts> class compiletime_prepared_parts_collector {
|
|||||||
FMT_CONSTEXPR explicit compiletime_prepared_parts_collector(Parts& parts)
|
FMT_CONSTEXPR explicit compiletime_prepared_parts_collector(Parts& parts)
|
||||||
: parts_{parts}, counter_{0u} {}
|
: parts_{parts}, counter_{0u} {}
|
||||||
|
|
||||||
FMT_CONSTEXPR void add(format_part part) { parts_[counter_++] = part; }
|
FMT_CONSTEXPR void push_back(format_part part) { parts_[counter_++] = part; }
|
||||||
|
|
||||||
FMT_CONSTEXPR void substitute_last(format_part part) {
|
FMT_CONSTEXPR format_part& back() { return parts_[counter_ - 1]; }
|
||||||
parts_[counter_ - 1] = part;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR format_part last() { return parts_[counter_ - 1]; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Parts& parts_;
|
Parts& parts_;
|
||||||
@ -431,87 +418,31 @@ struct compiletime_parts_provider {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <bool IS_CONSTEXPR, typename Format, typename /*PartsContainer*/>
|
|
||||||
struct parts_provider_type {
|
|
||||||
using type = compiletime_parts_provider<
|
|
||||||
Format, typename compiletime_prepared_parts_type_provider<Format>::type>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Format, typename PartsContainer>
|
|
||||||
struct parts_provider_type</*IS_CONSTEXPR=*/false, Format, PartsContainer> {
|
|
||||||
using type = runtime_parts_provider<PartsContainer>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Format, typename PreparedPartsContainer, typename... Args>
|
template <typename Format, typename PreparedPartsContainer, typename... Args>
|
||||||
struct basic_prepared_format {
|
using basic_prepared_format = internal::prepared_format<
|
||||||
using type =
|
Format,
|
||||||
internal::prepared_format<Format,
|
conditional_t<is_compile_string<Format>::value,
|
||||||
typename internal::parts_provider_type<
|
compiletime_parts_provider<
|
||||||
is_compile_string<Format>::value, Format,
|
Format, typename compiletime_prepared_parts_type_provider<
|
||||||
PreparedPartsContainer>::type,
|
Format>::type>,
|
||||||
Args...>;
|
runtime_parts_provider<PreparedPartsContainer>>,
|
||||||
};
|
Args...>;
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
std::basic_string<Char> to_runtime_format(basic_string_view<Char> format) {
|
|
||||||
return std::basic_string<Char>(format.begin(), format.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
std::basic_string<Char> to_runtime_format(const Char* format) {
|
|
||||||
return std::basic_string<Char>(format);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, typename Container = std::vector<format_part<Char>>>
|
|
||||||
class parts_container {
|
|
||||||
public:
|
|
||||||
using format_part_type = format_part<Char>;
|
|
||||||
|
|
||||||
void add(format_part_type part) { parts_.push_back(std::move(part)); }
|
|
||||||
|
|
||||||
void substitute_last(format_part_type part) {
|
|
||||||
parts_.back() = std::move(part);
|
|
||||||
}
|
|
||||||
|
|
||||||
format_part_type last() { return parts_.back(); }
|
|
||||||
|
|
||||||
auto begin() -> decltype(std::declval<Container>().begin()) {
|
|
||||||
return parts_.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto begin() const -> decltype(std::declval<const Container>().begin()) {
|
|
||||||
return parts_.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto end() -> decltype(std::declval<Container>().end()) {
|
|
||||||
return parts_.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto end() const -> decltype(std::declval<const Container>().end()) {
|
|
||||||
return parts_.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Container parts_;
|
|
||||||
};
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
#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) {
|
FMT_CONSTEXPR auto compile(S format_str) {
|
||||||
return typename internal::basic_prepared_format<S, void, Args...>::type(
|
return internal::basic_prepared_format<S, void, Args...>(format_str);
|
||||||
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]) ->
|
auto compile(const Char (&format_str)[N])
|
||||||
typename internal::basic_prepared_format<std::basic_string<Char>,
|
-> internal::basic_prepared_format<std::basic_string<Char>,
|
||||||
internal::parts_container<Char>,
|
std::vector<internal::format_part<Char>>,
|
||||||
Args...>::type {
|
Args...> {
|
||||||
const auto view = basic_string_view<Char>(format_str, N - 1);
|
return std::basic_string<Char>(format_str, N - 1);
|
||||||
return internal::to_runtime_format(view);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename CompiledFormat, typename... Args,
|
template <typename CompiledFormat, typename... Args,
|
||||||
|
@ -34,13 +34,6 @@
|
|||||||
using testing::Return;
|
using testing::Return;
|
||||||
using testing::StrictMock;
|
using testing::StrictMock;
|
||||||
|
|
||||||
class mock_parts_collector {
|
|
||||||
public:
|
|
||||||
MOCK_METHOD1(add, void(fmt::internal::format_part<char>));
|
|
||||||
MOCK_METHOD1(substitute_last, void(fmt::internal::format_part<char>));
|
|
||||||
MOCK_METHOD0(last, fmt::internal::format_part<char>());
|
|
||||||
};
|
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
namespace internal {
|
namespace internal {
|
||||||
bool operator==(const internal::string_view_metadata& lhs,
|
bool operator==(const internal::string_view_metadata& lhs,
|
||||||
@ -227,179 +220,6 @@ TEST(CompileTest, FormatPart_ComparisonOperators) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CompileTest, FormatPreparationHandler_OnText_AddsPartWithText) {
|
|
||||||
typedef fmt::internal::format_part<char> format_part;
|
|
||||||
typedef StrictMock<mock_parts_collector> parts_mock;
|
|
||||||
|
|
||||||
parts_mock parts;
|
|
||||||
const auto format = fmt::internal::to_string_view("text");
|
|
||||||
fmt::internal::format_preparation_handler<char, parts_mock> handler(format,
|
|
||||||
parts);
|
|
||||||
|
|
||||||
const auto expected_text = fmt::internal::string_view_metadata(
|
|
||||||
0u, static_cast<unsigned>(format.size()));
|
|
||||||
EXPECT_CALL(parts, add(format_part(expected_text)));
|
|
||||||
|
|
||||||
handler.on_text(format.begin(), format.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(CompileTest, FormatPreparationHandler_OnArgId_AddsPartWithIncrementedId) {
|
|
||||||
typedef fmt::internal::format_part<char> format_part;
|
|
||||||
typedef StrictMock<mock_parts_collector> parts_mock;
|
|
||||||
|
|
||||||
parts_mock parts;
|
|
||||||
const auto format = fmt::internal::to_string_view("");
|
|
||||||
fmt::internal::format_preparation_handler<char, parts_mock> handler(format,
|
|
||||||
parts);
|
|
||||||
|
|
||||||
const auto expected_first_arg_id = 0u;
|
|
||||||
const auto expected_second_arg_id = 1u;
|
|
||||||
EXPECT_CALL(parts, add(format_part(expected_first_arg_id)));
|
|
||||||
EXPECT_CALL(parts, add(format_part(expected_second_arg_id)));
|
|
||||||
|
|
||||||
handler.on_arg_id();
|
|
||||||
handler.on_arg_id();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(CompileTest, FormatPreparationHandler_OnArgId_AddsPartWithPassedId) {
|
|
||||||
typedef fmt::internal::format_part<char> format_part;
|
|
||||||
typedef StrictMock<mock_parts_collector> parts_mock;
|
|
||||||
|
|
||||||
parts_mock parts;
|
|
||||||
const auto format = fmt::internal::to_string_view("");
|
|
||||||
fmt::internal::format_preparation_handler<char, parts_mock> handler(format,
|
|
||||||
parts);
|
|
||||||
|
|
||||||
const auto expected_first_arg_id = 2u;
|
|
||||||
const auto expected_second_arg_id = 0u;
|
|
||||||
const auto expected_third_arg_id = 1u;
|
|
||||||
EXPECT_CALL(parts, add(format_part(expected_first_arg_id)));
|
|
||||||
EXPECT_CALL(parts, add(format_part(expected_second_arg_id)));
|
|
||||||
EXPECT_CALL(parts, add(format_part(expected_third_arg_id)));
|
|
||||||
|
|
||||||
handler.on_arg_id(expected_first_arg_id);
|
|
||||||
handler.on_arg_id(expected_second_arg_id);
|
|
||||||
handler.on_arg_id(expected_third_arg_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(CompileTest, FormatPreparationHandler_OnArgId_AddsPartWithPassedNamedId) {
|
|
||||||
typedef fmt::internal::format_part<char> format_part;
|
|
||||||
typedef format_part::named_argument_id named_argument_id;
|
|
||||||
typedef StrictMock<mock_parts_collector> parts_mock;
|
|
||||||
|
|
||||||
parts_mock parts;
|
|
||||||
const auto format = fmt::internal::to_string_view("0123456789");
|
|
||||||
fmt::internal::format_preparation_handler<char, parts_mock> handler(format,
|
|
||||||
parts);
|
|
||||||
|
|
||||||
const auto expected_first_arg_id = fmt::string_view(format.data(), 1);
|
|
||||||
const auto expected_first_arg_view_metadata =
|
|
||||||
fmt::internal::string_view_metadata(0, 1);
|
|
||||||
const auto expected_second_arg_id = fmt::string_view(format.data() + 3, 2);
|
|
||||||
const auto expected_second_arg_view_metadata =
|
|
||||||
fmt::internal::string_view_metadata(3, 2);
|
|
||||||
const auto expected_third_arg_id = fmt::string_view(format.data() + 6, 3);
|
|
||||||
const auto expected_third_arg_view_metadata =
|
|
||||||
fmt::internal::string_view_metadata(6, 3);
|
|
||||||
EXPECT_CALL(
|
|
||||||
parts,
|
|
||||||
add(format_part(named_argument_id(expected_first_arg_view_metadata))));
|
|
||||||
EXPECT_CALL(
|
|
||||||
parts,
|
|
||||||
add(format_part(named_argument_id(expected_second_arg_view_metadata))));
|
|
||||||
EXPECT_CALL(
|
|
||||||
parts,
|
|
||||||
add(format_part(named_argument_id(expected_third_arg_view_metadata))));
|
|
||||||
|
|
||||||
handler.on_arg_id(expected_first_arg_id);
|
|
||||||
handler.on_arg_id(expected_second_arg_id);
|
|
||||||
handler.on_arg_id(expected_third_arg_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(CompileTest,
|
|
||||||
FormatPreparationHandler_OnReplacementField_SetsEndOfArgumentId) {
|
|
||||||
typedef fmt::internal::format_part<char> format_part;
|
|
||||||
typedef StrictMock<mock_parts_collector> parts_mock;
|
|
||||||
|
|
||||||
const auto format = fmt::internal::to_string_view("{:<}");
|
|
||||||
parts_mock parts;
|
|
||||||
|
|
||||||
const auto last_part = format_part(0u);
|
|
||||||
EXPECT_CALL(parts, last()).WillOnce(Return(last_part));
|
|
||||||
|
|
||||||
auto expected_substitution_part = last_part;
|
|
||||||
expected_substitution_part.end_of_argument_id = 1;
|
|
||||||
EXPECT_CALL(parts, substitute_last(expected_substitution_part));
|
|
||||||
|
|
||||||
fmt::internal::format_preparation_handler<char, parts_mock> handler(format,
|
|
||||||
parts);
|
|
||||||
|
|
||||||
handler.on_replacement_field(format.data() + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(
|
|
||||||
CompileTest,
|
|
||||||
FormatPreparationHandlerLastPartArgIndex_OnFormatSpecs_UpdatesLastAddedPart) {
|
|
||||||
typedef fmt::internal::format_part<char> format_part;
|
|
||||||
typedef StrictMock<mock_parts_collector> parts_mock;
|
|
||||||
|
|
||||||
parts_mock parts;
|
|
||||||
const auto specification_test_text = fmt::internal::to_string_view("{:<10}");
|
|
||||||
const auto specification_offset = 2u;
|
|
||||||
const auto specification_begin_it =
|
|
||||||
specification_test_text.begin() + specification_offset;
|
|
||||||
fmt::internal::format_preparation_handler<char, parts_mock> handler(
|
|
||||||
specification_test_text, parts);
|
|
||||||
|
|
||||||
const auto last_part = format_part(0u);
|
|
||||||
format_part::specification expected_specification(0u);
|
|
||||||
fmt::internal::dynamic_format_specs<char> specs{};
|
|
||||||
specs.align = fmt::align::left;
|
|
||||||
specs.width = 10;
|
|
||||||
expected_specification.parsed_specs = specs;
|
|
||||||
|
|
||||||
auto expected_substitution_part = format_part(expected_specification);
|
|
||||||
expected_substitution_part.end_of_argument_id = specification_offset;
|
|
||||||
|
|
||||||
EXPECT_CALL(parts, last()).WillOnce(Return(last_part));
|
|
||||||
EXPECT_CALL(parts, substitute_last(expected_substitution_part));
|
|
||||||
|
|
||||||
handler.on_format_specs(specification_begin_it,
|
|
||||||
specification_test_text.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(
|
|
||||||
CompileTest,
|
|
||||||
FormatPreparationHandlerLastPartNamedArgIndex_OnFormatSpecs_UpdatesLastAddedPart) {
|
|
||||||
typedef fmt::internal::format_part<char> format_part;
|
|
||||||
typedef StrictMock<mock_parts_collector> parts_mock;
|
|
||||||
|
|
||||||
parts_mock parts;
|
|
||||||
const auto specification_test_text = fmt::internal::to_string_view("{:<10}");
|
|
||||||
const auto specification_offset = 2u;
|
|
||||||
const auto specification_begin_it =
|
|
||||||
specification_test_text.begin() + specification_offset;
|
|
||||||
fmt::internal::format_preparation_handler<char, parts_mock> handler(
|
|
||||||
specification_test_text, parts);
|
|
||||||
|
|
||||||
const auto arg_id = fmt::internal::string_view_metadata(0, 42);
|
|
||||||
const auto last_part = format_part(format_part::named_argument_id(arg_id));
|
|
||||||
format_part::specification expected_specification(arg_id);
|
|
||||||
fmt::internal::dynamic_format_specs<char> specs{};
|
|
||||||
specs.align = fmt::align::left;
|
|
||||||
specs.width = 10;
|
|
||||||
expected_specification.parsed_specs = specs;
|
|
||||||
|
|
||||||
auto expected_substitution_part = format_part(expected_specification);
|
|
||||||
expected_substitution_part.end_of_argument_id = specification_offset;
|
|
||||||
|
|
||||||
EXPECT_CALL(parts, last()).WillOnce(Return(last_part));
|
|
||||||
EXPECT_CALL(parts, substitute_last(expected_substitution_part));
|
|
||||||
|
|
||||||
handler.on_format_specs(specification_begin_it,
|
|
||||||
specification_test_text.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
||||||
|
Loading…
Reference in New Issue
Block a user