Simplify format string compilation
This commit is contained in:
parent
466128de00
commit
a128b5b2cb
@ -172,11 +172,12 @@ template <typename Context, typename Range, typename CompiledFormat>
|
|||||||
auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
|
auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
|
||||||
-> typename Context::iterator {
|
-> typename Context::iterator {
|
||||||
using char_type = typename Context::char_type;
|
using char_type = typename Context::char_type;
|
||||||
basic_parse_context<char_type> parse_ctx(to_string_view(cf.format_));
|
basic_parse_context<char_type> parse_ctx(to_string_view(cf.format_str_));
|
||||||
Context ctx(out.begin(), args);
|
Context ctx(out.begin(), args);
|
||||||
|
|
||||||
const auto& parts = cf.parts_provider_.parts();
|
const auto& parts = cf.parts();
|
||||||
for (auto part_it = parts.begin(); part_it != parts.end(); ++part_it) {
|
for (auto part_it = std::begin(parts); part_it != std::end(parts);
|
||||||
|
++part_it) {
|
||||||
const auto& part = *part_it;
|
const auto& part = *part_it;
|
||||||
const auto& value = part.val;
|
const auto& value = part.val;
|
||||||
|
|
||||||
@ -188,7 +189,8 @@ auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
|
|||||||
auto&& it = reserve(output, text.size());
|
auto&& it = reserve(output, text.size());
|
||||||
it = std::copy_n(text.begin(), text.size(), it);
|
it = std::copy_n(text.begin(), text.size(), it);
|
||||||
ctx.advance_to(output);
|
ctx.advance_to(output);
|
||||||
} break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case format_part_t::kind::arg_index:
|
case format_part_t::kind::arg_index:
|
||||||
advance_to(parse_ctx, part.arg_id_end);
|
advance_to(parse_ctx, part.arg_id_end);
|
||||||
@ -222,18 +224,92 @@ auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
|
|||||||
advance_to(parse_ctx, part.arg_id_end);
|
advance_to(parse_ctx, part.arg_id_end);
|
||||||
ctx.advance_to(
|
ctx.advance_to(
|
||||||
visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
|
visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
|
||||||
} break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ctx.out();
|
return ctx.out();
|
||||||
}
|
}
|
||||||
} // namespace cf
|
} // namespace cf
|
||||||
|
|
||||||
template <typename S, typename PreparedPartsProvider, typename... Args>
|
template <typename S, typename = void> struct compiled_format_base {
|
||||||
class compiled_format {
|
using char_type = char_t<S>;
|
||||||
|
using parts_container = std::vector<internal::format_part<char_type>>;
|
||||||
|
|
||||||
|
parts_container compiled_parts;
|
||||||
|
|
||||||
|
explicit compiled_format_base(basic_string_view<char_type> format_str) {
|
||||||
|
compile_format_string<false>(format_str,
|
||||||
|
[this](const format_part<char_type>& part) {
|
||||||
|
compiled_parts.push_back(part);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const parts_container& parts() const { return compiled_parts; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, unsigned N> struct format_part_array {
|
||||||
|
format_part<Char> data[N] = {};
|
||||||
|
FMT_CONSTEXPR format_part_array() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, unsigned N>
|
||||||
|
FMT_CONSTEXPR format_part_array<Char, N> compile_to_parts(
|
||||||
|
basic_string_view<Char> format_str) {
|
||||||
|
format_part_array<Char, N> parts;
|
||||||
|
unsigned counter = 0;
|
||||||
|
// This is not a lambda for compatibility with older compilers.
|
||||||
|
struct {
|
||||||
|
format_part<Char>* parts;
|
||||||
|
unsigned* counter;
|
||||||
|
FMT_CONSTEXPR void operator()(const format_part<Char>& part) {
|
||||||
|
parts[(*counter)++] = part;
|
||||||
|
}
|
||||||
|
} collector{parts.data, &counter};
|
||||||
|
compile_format_string<true>(format_str, collector);
|
||||||
|
if (counter < N) {
|
||||||
|
parts.data[counter] =
|
||||||
|
format_part<Char>::make_text(basic_string_view<Char>());
|
||||||
|
}
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> constexpr const T& constexpr_max(const T& a, const T& b) {
|
||||||
|
return (a < b) ? b : a;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S>
|
||||||
|
struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>> {
|
||||||
|
using char_type = char_t<S>;
|
||||||
|
|
||||||
|
FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {}
|
||||||
|
|
||||||
|
// Workaround for old compilers. Format string compilation will not be
|
||||||
|
// performed there anyway.
|
||||||
|
#if FMT_USE_CONSTEXPR
|
||||||
|
static FMT_CONSTEXPR_DECL const unsigned num_format_parts =
|
||||||
|
constexpr_max(count_parts(to_string_view(S())), 1u);
|
||||||
|
#else
|
||||||
|
static const unsigned num_format_parts = 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using parts_container = format_part<char_type>[num_format_parts];
|
||||||
|
|
||||||
|
const parts_container& parts() const {
|
||||||
|
static FMT_CONSTEXPR_DECL const auto compiled_parts =
|
||||||
|
compile_to_parts<char_type, num_format_parts>(
|
||||||
|
internal::to_string_view(S()));
|
||||||
|
return compiled_parts.data;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename S, typename... Args>
|
||||||
|
class compiled_format : private compiled_format_base<S> {
|
||||||
|
public:
|
||||||
|
using typename compiled_format_base<S>::char_type;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
S format_;
|
basic_string_view<char_type> format_str_;
|
||||||
PreparedPartsProvider parts_provider_;
|
|
||||||
|
|
||||||
template <typename Context, typename Range, typename CompiledFormat>
|
template <typename Context, typename Range, typename CompiledFormat>
|
||||||
friend auto cf::vformat_to(Range out, CompiledFormat& cf,
|
friend auto cf::vformat_to(Range out, CompiledFormat& cf,
|
||||||
@ -241,95 +317,9 @@ class compiled_format {
|
|||||||
typename Context::iterator;
|
typename Context::iterator;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using char_type = char_t<S>;
|
|
||||||
|
|
||||||
compiled_format() = delete;
|
compiled_format() = delete;
|
||||||
constexpr compiled_format(S f)
|
explicit constexpr compiled_format(basic_string_view<char_type> format_str)
|
||||||
: format_(std::move(f)), parts_provider_(to_string_view(format_)) {}
|
: compiled_format_base<S>(format_str), format_str_(format_str) {}
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Format> class compiletime_prepared_parts_type_provider {
|
|
||||||
private:
|
|
||||||
using char_type = char_t<Format>;
|
|
||||||
|
|
||||||
// Workaround for old compilers. Compiletime parts preparation will not be
|
|
||||||
// performed with them anyway.
|
|
||||||
#if FMT_USE_CONSTEXPR
|
|
||||||
static FMT_CONSTEXPR_DECL const unsigned number_of_format_parts =
|
|
||||||
count_parts(to_string_view(Format()));
|
|
||||||
#else
|
|
||||||
static const unsigned number_of_format_parts = 0u;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public:
|
|
||||||
template <unsigned N> struct format_parts_array {
|
|
||||||
using value_type = format_part<char_type>;
|
|
||||||
|
|
||||||
FMT_CONSTEXPR format_parts_array() : arr{} {}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR value_type& operator[](unsigned ind) { return arr[ind]; }
|
|
||||||
|
|
||||||
FMT_CONSTEXPR const value_type* begin() const { return arr; }
|
|
||||||
FMT_CONSTEXPR const value_type* end() const { return begin() + N; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
value_type arr[N];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct empty {
|
|
||||||
// Parts preparator will search for it
|
|
||||||
using value_type = format_part<char_type>;
|
|
||||||
};
|
|
||||||
|
|
||||||
using type = conditional_t<number_of_format_parts != 0,
|
|
||||||
format_parts_array<number_of_format_parts>, empty>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename PartsContainer, typename Char>
|
|
||||||
FMT_CONSTEXPR PartsContainer
|
|
||||||
prepare_compiletime_parts(basic_string_view<Char> format_str) {
|
|
||||||
// This is not a lambda for compatibility with older compilers.
|
|
||||||
struct collector {
|
|
||||||
PartsContainer& parts;
|
|
||||||
unsigned counter = 0;
|
|
||||||
FMT_CONSTEXPR void operator()(const format_part<Char>& part) {
|
|
||||||
parts[counter++] = part;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
PartsContainer parts;
|
|
||||||
compile_format_string<true>(format_str, collector{parts});
|
|
||||||
return parts;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char> class runtime_parts_provider {
|
|
||||||
public:
|
|
||||||
using parts_container = std::vector<internal::format_part<Char>>;
|
|
||||||
|
|
||||||
runtime_parts_provider(basic_string_view<Char> format_str) {
|
|
||||||
compile_format_string<false>(
|
|
||||||
format_str,
|
|
||||||
[this](const format_part<Char>& part) { parts_.push_back(part); });
|
|
||||||
}
|
|
||||||
|
|
||||||
const parts_container& parts() const { return parts_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
parts_container parts_;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Format> struct compiletime_parts_provider {
|
|
||||||
using parts_container =
|
|
||||||
typename internal::compiletime_prepared_parts_type_provider<Format>::type;
|
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
FMT_CONSTEXPR compiletime_parts_provider(basic_string_view<Char>) {}
|
|
||||||
|
|
||||||
const parts_container& parts() const {
|
|
||||||
static FMT_CONSTEXPR_DECL const parts_container prepared_parts =
|
|
||||||
prepare_compiletime_parts<parts_container>(
|
|
||||||
internal::to_string_view(Format()));
|
|
||||||
return prepared_parts;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
@ -337,17 +327,17 @@ template <typename Format> struct compiletime_parts_provider {
|
|||||||
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)
|
||||||
-> internal::compiled_format<S, internal::compiletime_parts_provider<S>,
|
-> internal::compiled_format<S, Args...> {
|
||||||
Args...> {
|
return internal::compiled_format<S, Args...>(to_string_view(format_str));
|
||||||
return format_str;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Compiles the format string which must be a string literal.
|
// Compiles the format string which must be a string literal.
|
||||||
template <typename... Args, typename Char, size_t N>
|
template <typename... Args, typename Char, size_t N>
|
||||||
auto compile(const Char (&format_str)[N]) -> internal::compiled_format<
|
auto compile(const Char (&format_str)[N])
|
||||||
std::basic_string<Char>, internal::runtime_parts_provider<Char>, Args...> {
|
-> internal::compiled_format<const Char*, Args...> {
|
||||||
return std::basic_string<Char>(format_str, N - 1);
|
return internal::compiled_format<const Char*, Args...>(
|
||||||
|
basic_string_view<Char>(format_str, N - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename CompiledFormat, typename... Args,
|
template <typename CompiledFormat, typename... Args,
|
||||||
@ -383,7 +373,7 @@ format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
|
|||||||
|
|
||||||
template <typename CompiledFormat, typename... Args>
|
template <typename CompiledFormat, typename... Args>
|
||||||
std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
|
std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
|
||||||
return fmt::format_to(
|
return format_to(
|
||||||
internal::counting_iterator<typename CompiledFormat::char_type>(),
|
internal::counting_iterator<typename CompiledFormat::char_type>(),
|
||||||
cf, args...)
|
cf, args...)
|
||||||
.count();
|
.count();
|
||||||
|
@ -39,14 +39,12 @@ using testing::StrictMock;
|
|||||||
#if FMT_USE_CONSTEXPR
|
#if FMT_USE_CONSTEXPR
|
||||||
template <unsigned EXPECTED_PARTS_COUNT, typename Format>
|
template <unsigned EXPECTED_PARTS_COUNT, typename Format>
|
||||||
void check_prepared_parts_type(Format format) {
|
void check_prepared_parts_type(Format format) {
|
||||||
typedef fmt::internal::compiletime_prepared_parts_type_provider<decltype(
|
typedef fmt::internal::compiled_format_base<decltype(format)> provider;
|
||||||
format)>
|
typedef fmt::internal::format_part<char>
|
||||||
provider;
|
expected_parts_type[EXPECTED_PARTS_COUNT];
|
||||||
typedef typename provider::template format_parts_array<EXPECTED_PARTS_COUNT>
|
static_assert(std::is_same<typename provider::parts_container,
|
||||||
expected_parts_type;
|
expected_parts_type>::value,
|
||||||
static_assert(
|
"CompileTimePreparedPartsTypeProvider test failed");
|
||||||
std::is_same<typename provider::type, expected_parts_type>::value,
|
|
||||||
"CompileTimePreparedPartsTypeProvider test failed");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CompileTest, CompileTimePreparedPartsTypeProvider) {
|
TEST(CompileTest, CompileTimePreparedPartsTypeProvider) {
|
||||||
@ -122,8 +120,7 @@ TEST(CompileTest, FormattedSize) {
|
|||||||
struct formattable {};
|
struct formattable {};
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
template <>
|
template <> struct formatter<formattable> : formatter<const char*> {
|
||||||
struct formatter<formattable> : formatter<const char*> {
|
|
||||||
auto format(formattable, format_context& ctx) -> decltype(ctx.out()) {
|
auto format(formattable, format_context& ctx) -> decltype(ctx.out()) {
|
||||||
return formatter<const char*>::format("foo", ctx);
|
return formatter<const char*>::format("foo", ctx);
|
||||||
}
|
}
|
||||||
@ -134,3 +131,8 @@ TEST(CompileTest, FormatUserDefinedType) {
|
|||||||
auto f = fmt::compile<formattable>("{}");
|
auto f = fmt::compile<formattable>("{}");
|
||||||
EXPECT_EQ(fmt::format(f, formattable()), "foo");
|
EXPECT_EQ(fmt::format(f, formattable()), "foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(CompileTest, EmptyFormatString) {
|
||||||
|
auto f = fmt::compile<>("");
|
||||||
|
EXPECT_EQ(fmt::format(f), "");
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user