From 2fd8f9ec8a6a25071d536cb3379dc49e58c013f7 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 2 Sep 2019 17:08:58 -0700 Subject: [PATCH] Initial implementation of optimal compile-time formatter generation --- include/fmt/compile.h | 134 ++++++++++++++++++++++++++++++++++++++++++ include/fmt/core.h | 2 + 2 files changed, 136 insertions(+) diff --git a/include/fmt/compile.h b/include/fmt/compile.h index 9cd6c937..ad89eada 100644 --- a/include/fmt/compile.h +++ b/include/fmt/compile.h @@ -321,6 +321,140 @@ class compiled_format : private compiled_format_base { explicit constexpr compiled_format(basic_string_view format_str) : compiled_format_base(format_str), format_str_(format_str) {} }; + +#ifdef __cpp_if_constexpr +template struct type_list {}; + +// Returns a reference to the argument at index N from [first, rest...]. +template +const auto& get(const T& first, const Args&... rest) { + if constexpr (sizeof...(Args) == 0) + return first; + else + return get(rest...); +} + +template struct get_type_impl; + +template struct get_type_impl> { + using type = remove_cvref_t(std::declval()...))>; +}; + +template +using get_type = typename get_type_impl::type; + +template struct text { + basic_string_view data; + + template + OutputIt format(OutputIt out, const Args&...) { + // TODO: reserve + return copy_str(data.begin(), data.end(), out); + } +}; + +template +constexpr text make_text(basic_string_view s, size_t pos, + size_t size) { + return {{&s[pos], size}}; +} + +template , int> = 0> +OutputIt format_default(OutputIt out, T value) { + // TODO: reserve + format_int fi(value); + return std::copy(fi.data(), fi.data() + fi.size(), out); +} + +template +OutputIt format_default(OutputIt out, Char value) { + *out++ = value; + return out; +} + +template +OutputIt format_default(OutputIt out, const Char* value) { + auto length = std::char_traits::length(value); + return copy_str(value, value + length, out); +} + +// A replacement field that refers to argument N. +template struct field { + template + OutputIt format(OutputIt out, const Args&... args) { + // This ensures that the argument type is convertile to `const T&`. + const T& arg = get(args...); + return format_default(out, arg); + } +}; + +template struct concat { + L lhs; + R rhs; + + template + OutputIt format(OutputIt out, const Args&... args) { + out = lhs.format(out, args...); + return rhs.format(out, args...); + } +}; + +template +constexpr concat make_concat(L lhs, R rhs) { + return {lhs, rhs}; +} + +template +constexpr size_t parse_text(basic_string_view str, size_t pos) { + for (size_t size = str.size(); pos != size; ++pos) { + if (str[pos] == '{' || str[pos] == '}') break; + } + return pos; +} + +template +constexpr auto compile_format_string(S format_str); + +template +auto parse_tail(T head, S format_str) { + if constexpr (POS != to_string_view(format_str).size()) + return make_concat(head, compile_format_string(format_str)); + else + return head; +} + +template +constexpr auto compile_format_string(S format_str) { + using char_type = char_t; + constexpr basic_string_view str = format_str; + if constexpr (str.size() == 0) { + return make_text(str, 0, 0); + } else if constexpr (str[POS] == '{') { + if (POS + 1 == str.size()) + throw format_error("unmatched '{' in format string"); + if constexpr (str[POS + 1] == '{') { + return parse_tail(make_text(str, POS, 1), format_str); + } else if constexpr (str[POS + 1] == '}') { + return parse_tail( + field, ID>(), format_str); + } else { + throw format_error("invalid format string"); + } + } else if constexpr (str[POS] == '}') { + if (POS + 1 == str.size()) + throw format_error("unmatched '}' in format string"); + return parse_tail(make_text(str, POS, 1), format_str); + } else { + constexpr auto end = parse_text(str, POS + 1); + return parse_tail(make_text(str, POS, end - POS), + format_str); + } +} +template constexpr auto compile(S format_str) { + return compile_format_string, 0, 0>(format_str); +} +#endif // __cpp_if_constexpr } // namespace internal #if FMT_USE_CONSTEXPR diff --git a/include/fmt/core.h b/include/fmt/core.h index 264f093a..6fdf0b6c 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -295,6 +295,8 @@ template class basic_string_view { FMT_CONSTEXPR iterator begin() const { return data_; } FMT_CONSTEXPR iterator end() const { return data_ + size_; } + FMT_CONSTEXPR const Char& operator[](size_t pos) const { return data_[pos]; } + FMT_CONSTEXPR void remove_prefix(size_t n) { data_ += n; size_ -= n;