From 2d71d7e030c05d9a02abf1cd6678d527c0781d29 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 12 Jun 2020 07:05:49 -0700 Subject: [PATCH] Add a simple format string compilation API --- include/fmt/compile.h | 57 +++++++++++++++++++++---------------------- include/fmt/core.h | 6 ++--- include/fmt/format.h | 19 +++++++++++---- 3 files changed, 45 insertions(+), 37 deletions(-) diff --git a/include/fmt/compile.h b/include/fmt/compile.h index fb66ae7c..d486a867 100644 --- a/include/fmt/compile.h +++ b/include/fmt/compile.h @@ -15,6 +15,17 @@ FMT_BEGIN_NAMESPACE namespace detail { +// A compile-time string which is compiled into fast formatting code. +class compiled_string {}; + +template +struct is_compiled_string : std::is_base_of {}; + +#define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string) + +template +const T& first(const T& value, const Tail&...) { return value; } + // Part of a compiled format string. It can be either literal text or a // replacement field. template struct format_part { @@ -381,31 +392,6 @@ constexpr text make_text(basic_string_view s, size_t pos, 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, double value) { - return detail::write(out, value); -} - -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 { using char_type = Char; @@ -414,7 +400,7 @@ template struct field { 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); + return write(out, arg); } }; @@ -456,7 +442,8 @@ constexpr auto compile_format_string(S format_str); template constexpr auto parse_tail(T head, S format_str) { - if constexpr (POS != to_string_view(format_str).size()) { + if constexpr (POS != + basic_string_view(format_str).size()) { constexpr auto tail = compile_format_string(format_str); if constexpr (std::is_same, unknown_format>()) @@ -506,7 +493,8 @@ constexpr auto compile_format_string(S format_str) { #if FMT_USE_CONSTEXPR # ifdef __cpp_if_constexpr template ::value)> + FMT_ENABLE_IF(is_compile_string::value || + detail::is_compiled_string::value)> constexpr auto compile(S format_str) { constexpr basic_string_view str = format_str; if constexpr (str.size() == 0) { @@ -529,7 +517,8 @@ template ::value)> std::basic_string format(const CompiledFormat& cf, const Args&... args) { basic_memory_buffer buffer; - cf.format(std::back_inserter(buffer), args...); + detail::buffer& base = buffer; + cf.format(std::back_inserter(base), args...); return to_string(buffer); } @@ -569,6 +558,16 @@ std::basic_string format(const CompiledFormat& cf, const Args&... args) { return to_string(buffer); } +template ::value)> +FMT_INLINE std::basic_string format(S, Args&&... args) { + constexpr basic_string_view str = S(); + if (str.size() == 2 && str[0] == '{' && str[1] == '}') + return fmt::to_string(detail::first(args...)); + constexpr auto compiled = compile(S()); + return format(compiled, std::forward(args...)); +} + template ::value)> diff --git a/include/fmt/core.h b/include/fmt/core.h index fc82546f..f1ab240f 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -481,7 +481,7 @@ inline basic_string_view to_string_view(detail::std_string_view s) { } // A base class for compile-time strings. It is defined in the fmt namespace to -// make formatting functions visible via ADL, e.g. format(fmt("{}"), 42). +// make formatting functions visible via ADL, e.g. format(FMT_STRING("{}"), 42). struct compile_string {}; template @@ -1745,8 +1745,8 @@ template ::value)> FMT_INLINE void check_format_string(const S&) { #ifdef FMT_ENFORCE_COMPILE_STRING static_assert(is_compile_string::value, - "FMT_ENFORCE_COMPILE_STRING requires all format strings to " - "utilize FMT_STRING() or fmt()."); + "FMT_ENFORCE_COMPILE_STRING requires all format strings to use " + "FMT_STRING."); #endif } template ::value)> diff --git a/include/fmt/format.h b/include/fmt/format.h index 8a0a1594..1c45a867 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -700,6 +700,10 @@ void basic_memory_buffer::grow(size_t size) { using memory_buffer = basic_memory_buffer; using wmemory_buffer = basic_memory_buffer; +template +struct is_contiguous> : std::true_type { +}; + /** A formatting error such as invalid format string. */ FMT_CLASS_API class FMT_API format_error : public std::runtime_error { @@ -2746,12 +2750,12 @@ FMT_CONSTEXPR basic_string_view compile_string_to_view( return {s.data(), s.size()}; } -#define FMT_STRING_IMPL(s, ...) \ +#define FMT_STRING_IMPL(s, base) \ [] { \ /* Use a macro-like name to avoid shadowing warnings. */ \ - struct FMT_COMPILE_STRING : fmt::compile_string { \ + struct FMT_COMPILE_STRING : base { \ using char_type = fmt::remove_cvref_t; \ - FMT_MAYBE_UNUSED __VA_ARGS__ FMT_CONSTEXPR \ + FMT_MAYBE_UNUSED FMT_CONSTEXPR \ operator fmt::basic_string_view() const { \ return fmt::detail::compile_string_to_view(s); \ } \ @@ -2769,7 +2773,7 @@ FMT_CONSTEXPR basic_string_view compile_string_to_view( std::string s = format(FMT_STRING("{:d}"), "foo"); \endrst */ -#define FMT_STRING(s) FMT_STRING_IMPL(s, ) +#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::compile_string) template ::value), int>> @@ -3344,9 +3348,14 @@ join(const Range& range, wstring_view sep) { std::string answer = fmt::to_string(42); \endrst */ -template inline std::string to_string(const T& value) { +template ::value)> +inline std::string to_string(const T& value) { return format("{}", value); } +template ::value)> +inline std::string to_string(T value) { + return format_int(value).str(); +} /** Converts *value* to ``std::wstring`` using the default format for type *T*.