diff --git a/include/fmt/compile.h b/include/fmt/compile.h index ad89eada..8617ef85 100644 --- a/include/fmt/compile.h +++ b/include/fmt/compile.h @@ -232,7 +232,10 @@ auto vformat_to(Range out, CompiledFormat& cf, basic_format_args args) } } // namespace cf -template struct compiled_format_base { +struct basic_compiled_format {}; + +template +struct compiled_format_base : basic_compiled_format { using char_type = char_t; using parts_container = std::vector>; @@ -279,7 +282,8 @@ template constexpr const T& constexpr_max(const T& a, const T& b) { } template -struct compiled_format_base::value>> { +struct compiled_format_base::value>> + : basic_compiled_format { using char_type = char_t; FMT_CONSTEXPR explicit compiled_format_base(basic_string_view) {} @@ -345,9 +349,10 @@ using get_type = typename get_type_impl::type; template struct text { basic_string_view data; + using char_type = Char; template - OutputIt format(OutputIt out, const Args&...) { + OutputIt format(OutputIt out, const Args&...) const { // TODO: reserve return copy_str(data.begin(), data.end(), out); } @@ -381,8 +386,10 @@ OutputIt format_default(OutputIt out, const Char* value) { // A replacement field that refers to argument N. template struct field { + using char_type = Char; + template - OutputIt format(OutputIt out, const Args&... args) { + OutputIt format(OutputIt out, const Args&... args) const { // This ensures that the argument type is convertile to `const T&`. const T& arg = get(args...); return format_default(out, arg); @@ -392,9 +399,10 @@ template struct field { template struct concat { L lhs; R rhs; + using char_type = typename L::char_type; template - OutputIt format(OutputIt out, const Args&... args) { + OutputIt format(OutputIt out, const Args&... args) const { out = lhs.format(out, args...); return rhs.format(out, args...); } @@ -405,6 +413,8 @@ constexpr concat make_concat(L lhs, R rhs) { return {lhs, rhs}; } +struct unknown {}; + template constexpr size_t parse_text(basic_string_view str, size_t pos) { for (size_t size = str.size(); pos != size; ++pos) { @@ -417,29 +427,39 @@ 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 +constexpr auto parse_tail(T head, S format_str) { + if constexpr (POS != to_string_view(format_str).size()) { + constexpr auto tail = compile_format_string(format_str); + if constexpr (std::is_same, unknown>()) + return tail; + else + return make_concat(head, tail); + } else { return head; + } } +// Compiles a non-empty format string and returns the compiled representation +// or unknown() on unrecognized input. template constexpr auto compile_format_string(S format_str) { - using char_type = char_t; + using char_type = typename S::char_type; constexpr basic_string_view str = format_str; - if constexpr (str.size() == 0) { - return make_text(str, 0, 0); - } else if constexpr (str[POS] == '{') { + 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); + using type = get_type; + if constexpr (std::is_same::value) { + return parse_tail(field(), + format_str); + } else { + return unknown(); + } } else { - throw format_error("invalid format string"); + return unknown(); } } else if constexpr (str[POS] == '}') { if (POS + 1 == str.size()) @@ -451,20 +471,57 @@ constexpr auto compile_format_string(S format_str) { 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 +# ifdef __cpp_if_constexpr template ::value)> -FMT_CONSTEXPR auto compile(S format_str) - -> internal::compiled_format { +constexpr auto compile(S format_str) { + constexpr basic_string_view str = format_str; + if constexpr (str.size() == 0) { + return internal::make_text(str, 0, 0); + } else { + constexpr auto result = + internal::compile_format_string, 0, 0>( + format_str); + if constexpr (std::is_same, + internal::unknown>()) { + return internal::compiled_format(to_string_view(format_str)); + } else { + return result; + } + } +} + +template ::value)> +std::basic_string format(const CompiledFormat& cf, const Args&... args) { + basic_memory_buffer buffer; + using range = buffer_range; + using context = buffer_context; + cf.format(std::back_inserter(buffer), args...); + return to_string(buffer); +} + +template ::value)> +OutputIt format_to(OutputIt out, const CompiledFormat& cf, + const Args&... args) { + return cf.format(out, args...); +} +# else +template ::value)> +constexpr auto compile(S format_str) -> internal::compiled_format { return internal::compiled_format(to_string_view(format_str)); } -#endif +# endif // __cpp_if_constexpr +#endif // FMT_USE_CONSTEXPR // Compiles the format string which must be a string literal. template @@ -475,7 +532,9 @@ auto compile(const Char (&format_str)[N]) } template + typename Char = typename CompiledFormat::char_type, + FMT_ENABLE_IF(std::is_base_of::value)> std::basic_string format(const CompiledFormat& cf, const Args&... args) { basic_memory_buffer buffer; using range = buffer_range; @@ -485,7 +544,9 @@ std::basic_string format(const CompiledFormat& cf, const Args&... args) { return to_string(buffer); } -template +template ::value)> OutputIt format_to(OutputIt out, const CompiledFormat& cf, const Args&... args) { using char_type = typename CompiledFormat::char_type; diff --git a/include/fmt/format.h b/include/fmt/format.h index a229053d..a182e906 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -186,7 +186,7 @@ FMT_END_NAMESPACE #endif #ifndef FMT_NUMERIC_ALIGN -# define FMT_NUMERIC_ALIGN 1 +# define FMT_NUMERIC_ALIGN 1 #endif FMT_BEGIN_NAMESPACE @@ -2791,8 +2791,9 @@ void internal::basic_writer::write_double(T value, bool use_grisu = USE_GRISU && (specs.type != 'a' && specs.type != 'A' && specs.type != 'e' && specs.type != 'E') && - internal::grisu_format(static_cast(value), buffer, - precision, handler.fixed ? internal::grisu_options::fixed : 0, exp); + internal::grisu_format( + static_cast(value), buffer, precision, + handler.fixed ? internal::grisu_options::fixed : 0, exp); char* decimal_point_pos = nullptr; if (!use_grisu) decimal_point_pos = internal::sprintf_format(value, buffer, specs);