X-Git-Url: https://git.lttng.org/?a=blobdiff_plain;f=src%2Fvendor%2Ffmt%2Fcore.h;h=bed8b40d3ee5513529d22c887c5bdf35121c3e97;hb=8b75cd779ffe332281fec189cdf808e4ee452572;hp=92a7aa1df69ba84553c987c591a1bac1ec187c88;hpb=dc65dda314fcd676fabfe73942c34cb93b7fea40;p=lttng-tools.git diff --git a/src/vendor/fmt/core.h b/src/vendor/fmt/core.h index 92a7aa1df..bed8b40d3 100644 --- a/src/vendor/fmt/core.h +++ b/src/vendor/fmt/core.h @@ -10,14 +10,14 @@ #include // std::byte #include // std::FILE -#include +#include // std::strlen #include #include #include #include // The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 80101 +#define FMT_VERSION 90100 #if defined(__clang__) && !defined(__ibmxl__) # define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) @@ -49,29 +49,29 @@ # define FMT_ICC_VERSION 0 #endif -#ifdef __NVCC__ -# define FMT_NVCC __NVCC__ -#else -# define FMT_NVCC 0 -#endif - #ifdef _MSC_VER -# define FMT_MSC_VER _MSC_VER +# define FMT_MSC_VERSION _MSC_VER # define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__)) #else -# define FMT_MSC_VER 0 +# define FMT_MSC_VERSION 0 # define FMT_MSC_WARNING(...) #endif +#ifdef _MSVC_LANG +# define FMT_CPLUSPLUS _MSVC_LANG +#else +# define FMT_CPLUSPLUS __cplusplus +#endif + #ifdef __has_feature # define FMT_HAS_FEATURE(x) __has_feature(x) #else # define FMT_HAS_FEATURE(x) 0 #endif -#if defined(__has_include) && \ - (!defined(__INTELLISENSE__) || FMT_MSC_VER > 1900) && \ - (!FMT_ICC_VERSION || FMT_ICC_VERSION >= 1600) +#if (defined(__has_include) || FMT_ICC_VERSION >= 1600 || \ + FMT_MSC_VERSION > 1900) && \ + !defined(__INTELLISENSE__) # define FMT_HAS_INCLUDE(x) __has_include(x) #else # define FMT_HAS_INCLUDE(x) 0 @@ -83,12 +83,6 @@ # define FMT_HAS_CPP_ATTRIBUTE(x) 0 #endif -#ifdef _MSVC_LANG -# define FMT_CPLUSPLUS _MSVC_LANG -#else -# define FMT_CPLUSPLUS __cplusplus -#endif - #define FMT_HAS_CPP14_ATTRIBUTE(attribute) \ (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute)) @@ -98,37 +92,38 @@ // Check if relaxed C++14 constexpr is supported. // GCC doesn't allow throw in constexpr until version 6 (bug 67371). #ifndef FMT_USE_CONSTEXPR -# define FMT_USE_CONSTEXPR \ - (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1912 || \ - (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) && \ - !FMT_NVCC && !FMT_ICC_VERSION +# if (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 || \ + (FMT_GCC_VERSION >= 600 && FMT_CPLUSPLUS >= 201402L)) && \ + !FMT_ICC_VERSION && !defined(__NVCC__) +# define FMT_USE_CONSTEXPR 1 +# else +# define FMT_USE_CONSTEXPR 0 +# endif #endif #if FMT_USE_CONSTEXPR # define FMT_CONSTEXPR constexpr -# define FMT_CONSTEXPR_DECL constexpr #else # define FMT_CONSTEXPR -# define FMT_CONSTEXPR_DECL #endif -#if ((__cplusplus >= 202002L) && \ +#if ((FMT_CPLUSPLUS >= 202002L) && \ (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) || \ - (__cplusplus >= 201709L && FMT_GCC_VERSION >= 1002) + (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002) # define FMT_CONSTEXPR20 constexpr #else # define FMT_CONSTEXPR20 #endif -// Check if constexpr std::char_traits<>::compare,length is supported. +// Check if constexpr std::char_traits<>::{compare,length} are supported. #if defined(__GLIBCXX__) -# if __cplusplus >= 201703L && defined(_GLIBCXX_RELEASE) && \ +# if FMT_CPLUSPLUS >= 201703L && defined(_GLIBCXX_RELEASE) && \ _GLIBCXX_RELEASE >= 7 // GCC 7+ libstdc++ has _GLIBCXX_RELEASE. # define FMT_CONSTEXPR_CHAR_TRAITS constexpr # endif -#elif defined(_LIBCPP_VERSION) && __cplusplus >= 201703L && \ +#elif defined(_LIBCPP_VERSION) && FMT_CPLUSPLUS >= 201703L && \ _LIBCPP_VERSION >= 4000 # define FMT_CONSTEXPR_CHAR_TRAITS constexpr -#elif FMT_MSC_VER >= 1914 && _MSVC_LANG >= 201703L +#elif FMT_MSC_VERSION >= 1914 && FMT_CPLUSPLUS >= 201703L # define FMT_CONSTEXPR_CHAR_TRAITS constexpr #endif #ifndef FMT_CONSTEXPR_CHAR_TRAITS @@ -138,57 +133,43 @@ // Check if exceptions are disabled. #ifndef FMT_EXCEPTIONS # if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \ - FMT_MSC_VER && !_HAS_EXCEPTIONS + (FMT_MSC_VERSION && !_HAS_EXCEPTIONS) # define FMT_EXCEPTIONS 0 # else # define FMT_EXCEPTIONS 1 # endif #endif -// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature). -#ifndef FMT_USE_NOEXCEPT -# define FMT_USE_NOEXCEPT 0 -#endif - -#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ - FMT_GCC_VERSION >= 408 || FMT_MSC_VER >= 1900 -# define FMT_DETECTED_NOEXCEPT noexcept -# define FMT_HAS_CXX11_NOEXCEPT 1 -#else -# define FMT_DETECTED_NOEXCEPT throw() -# define FMT_HAS_CXX11_NOEXCEPT 0 -#endif - -#ifndef FMT_NOEXCEPT -# if FMT_EXCEPTIONS || FMT_HAS_CXX11_NOEXCEPT -# define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT +#ifndef FMT_DEPRECATED +# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VERSION >= 1900 +# define FMT_DEPRECATED [[deprecated]] # else -# define FMT_NOEXCEPT +# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__) +# define FMT_DEPRECATED __attribute__((deprecated)) +# elif FMT_MSC_VERSION +# define FMT_DEPRECATED __declspec(deprecated) +# else +# define FMT_DEPRECATED /* deprecated */ +# endif # endif #endif // [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code // warnings. -#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER && \ - !FMT_NVCC +#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && \ + !defined(__NVCC__) # define FMT_NORETURN [[noreturn]] #else # define FMT_NORETURN #endif -#if __cplusplus == 201103L || __cplusplus == 201402L -# if defined(__INTEL_COMPILER) || defined(__PGI) -# define FMT_FALLTHROUGH -# elif defined(__clang__) -# define FMT_FALLTHROUGH [[clang::fallthrough]] -# elif FMT_GCC_VERSION >= 700 && \ - (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) -# define FMT_FALLTHROUGH [[gnu::fallthrough]] -# else -# define FMT_FALLTHROUGH -# endif -#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough) +#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough) # define FMT_FALLTHROUGH [[fallthrough]] +#elif defined(__clang__) +# define FMT_FALLTHROUGH [[clang::fallthrough]] +#elif FMT_GCC_VERSION >= 700 && \ + (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) +# define FMT_FALLTHROUGH [[gnu::fallthrough]] #else # define FMT_FALLTHROUGH #endif @@ -219,24 +200,20 @@ # endif #endif -#ifndef FMT_DEPRECATED -# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900 -# define FMT_DEPRECATED [[deprecated]] -# else -# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__) -# define FMT_DEPRECATED __attribute__((deprecated)) -# elif FMT_MSC_VER -# define FMT_DEPRECATED __declspec(deprecated) -# else -# define FMT_DEPRECATED /* deprecated */ -# endif -# endif +// An inline std::forward replacement. +#define FMT_FORWARD(...) static_cast(__VA_ARGS__) + +#ifdef _MSC_VER +# define FMT_UNCHECKED_ITERATOR(It) \ + using _Unchecked_type = It // Mark iterator as checked. +#else +# define FMT_UNCHECKED_ITERATOR(It) using unchecked_type = It #endif #ifndef FMT_BEGIN_NAMESPACE # define FMT_BEGIN_NAMESPACE \ namespace fmt { \ - inline namespace v8 { + inline namespace v9 { # define FMT_END_NAMESPACE \ } \ } @@ -270,25 +247,24 @@ #endif // libc++ supports string_view in pre-c++17. -#if (FMT_HAS_INCLUDE() && \ - (__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \ - (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) +#if FMT_HAS_INCLUDE() && \ + (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION)) # include # define FMT_USE_STRING_VIEW -#elif FMT_HAS_INCLUDE("experimental/string_view") && __cplusplus >= 201402L +#elif FMT_HAS_INCLUDE("experimental/string_view") && FMT_CPLUSPLUS >= 201402L # include # define FMT_USE_EXPERIMENTAL_STRING_VIEW #endif #ifndef FMT_UNICODE -# define FMT_UNICODE !FMT_MSC_VER +# define FMT_UNICODE !FMT_MSC_VERSION #endif #ifndef FMT_CONSTEVAL -# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \ - __cplusplus > 201703L && !defined(__apple_build_version__)) || \ - (defined(__cpp_consteval) && \ - (!FMT_MSC_VER || _MSC_FULL_VER >= 193030704)) +# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \ + FMT_CPLUSPLUS >= 202002L && !defined(__apple_build_version__)) || \ + (defined(__cpp_consteval) && \ + (!FMT_MSC_VERSION || _MSC_FULL_VER >= 193030704)) // consteval is broken in MSVC before VS2022 and Apple clang 13. # define FMT_CONSTEVAL consteval # define FMT_HAS_CONSTEVAL @@ -297,19 +273,20 @@ # endif #endif -#ifndef FMT_USE_NONTYPE_TEMPLATE_PARAMETERS -# if defined(__cpp_nontype_template_args) && \ - ((FMT_GCC_VERSION >= 903 && __cplusplus >= 201709L) || \ - __cpp_nontype_template_args >= 201911L) -# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 1 +#ifndef FMT_USE_NONTYPE_TEMPLATE_ARGS +# if defined(__cpp_nontype_template_args) && \ + ((FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L) || \ + __cpp_nontype_template_args >= 201911L) && \ + !defined(__NVCOMPILER) +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 # else -# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 0 +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0 # endif #endif // Enable minimal optimizations for more compact code in debug mode. FMT_GCC_PRAGMA("GCC push_options") -#ifndef __OPTIMIZE__ +#if !defined(__OPTIMIZE__) && !defined(__NVCOMPILER) FMT_GCC_PRAGMA("GCC optimize(\"Og\")") #endif @@ -330,6 +307,20 @@ template using remove_cvref_t = typename std::remove_cv>::type; template struct type_identity { using type = T; }; template using type_identity_t = typename type_identity::type; +template +using underlying_t = typename std::underlying_type::type; + +template struct disjunction : std::false_type {}; +template struct disjunction

: P {}; +template +struct disjunction + : conditional_t> {}; + +template struct conjunction : std::true_type {}; +template struct conjunction

: P {}; +template +struct conjunction + : conditional_t, P1> {}; struct monostate { constexpr monostate() {} @@ -346,13 +337,13 @@ struct monostate { FMT_BEGIN_DETAIL_NAMESPACE -// Suppress "unused variable" warnings with the method described in +// Suppresses "unused variable" warnings with the method described in // https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/. // (void)var does not work on many Intel compilers. template FMT_CONSTEXPR void ignore_unused(const T&...) {} -constexpr FMT_INLINE auto is_constant_evaluated(bool default_value = false) - FMT_NOEXCEPT -> bool { +constexpr FMT_INLINE auto is_constant_evaluated( + bool default_value = false) noexcept -> bool { #ifdef __cpp_lib_is_constant_evaluated ignore_unused(default_value); return std::is_constant_evaluated(); @@ -361,7 +352,7 @@ constexpr FMT_INLINE auto is_constant_evaluated(bool default_value = false) #endif } -// A function to suppress "conditional expression is constant" warnings. +// Suppresses "conditional expression is constant" warnings. template constexpr FMT_INLINE auto const_check(T value) -> T { return value; } @@ -371,7 +362,7 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line, #ifndef FMT_ASSERT # ifdef NDEBUG -// FMT_ASSERT is not empty to avoid -Werror=empty-body. +// FMT_ASSERT is not empty to avoid -Wempty-body. # define FMT_ASSERT(condition, message) \ ::fmt::detail::ignore_unused((condition), (message)) # else @@ -382,12 +373,6 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line, # endif #endif -#ifdef __cpp_lib_byte -using byte = std::byte; -#else -enum class byte : unsigned char {}; -#endif - #if defined(FMT_USE_STRING_VIEW) template using std_string_view = std::basic_string_view; #elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW) @@ -399,11 +384,11 @@ template struct std_string_view {}; #ifdef FMT_USE_INT128 // Do nothing. -#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && \ - !(FMT_CLANG_VERSION && FMT_MSC_VER) +#elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && \ + !(FMT_CLANG_VERSION && FMT_MSC_VERSION) # define FMT_USE_INT128 1 -using int128_t = __int128_t; -using uint128_t = __uint128_t; +using int128_opt = __int128_t; // An optional native 128-bit integer. +using uint128_opt = __uint128_t; template inline auto convert_for_visit(T value) -> T { return value; } @@ -411,27 +396,24 @@ template inline auto convert_for_visit(T value) -> T { # define FMT_USE_INT128 0 #endif #if !FMT_USE_INT128 -enum class int128_t {}; -enum class uint128_t {}; +enum class int128_opt {}; +enum class uint128_opt {}; // Reduce template instantiations. -template inline auto convert_for_visit(T) -> monostate { - return {}; -} +template auto convert_for_visit(T) -> monostate { return {}; } #endif // Casts a nonnegative integer to unsigned. template FMT_CONSTEXPR auto to_unsigned(Int value) -> typename std::make_unsigned::type { - FMT_ASSERT(value >= 0, "negative value"); + FMT_ASSERT(std::is_unsigned::value || value >= 0, "negative value"); return static_cast::type>(value); } FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char micro[] = "\u00B5"; constexpr auto is_utf8() -> bool { - // Avoid buggy sign extensions in MSVC's constant evaluation mode. - // https://developercommunity.visualstudio.com/t/C-difference-in-behavior-for-unsigned/1233612 + // Avoid buggy sign extensions in MSVC's constant evaluation mode (#2297). using uchar = unsigned char; return FMT_UNICODE || (sizeof(micro) == 3 && uchar(micro[0]) == 0xC2 && uchar(micro[1]) == 0xB5); @@ -454,12 +436,11 @@ template class basic_string_view { using value_type = Char; using iterator = const Char*; - constexpr basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {} + constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {} /** Constructs a string reference object from a C string and a size. */ - constexpr basic_string_view(const Char* s, size_t count) FMT_NOEXCEPT - : data_(s), - size_(count) {} + constexpr basic_string_view(const Char* s, size_t count) noexcept + : data_(s), size_(count) {} /** \rst @@ -479,29 +460,28 @@ template class basic_string_view { /** Constructs a string reference from a ``std::basic_string`` object. */ template FMT_CONSTEXPR basic_string_view( - const std::basic_string& s) FMT_NOEXCEPT - : data_(s.data()), - size_(s.size()) {} + const std::basic_string& s) noexcept + : data_(s.data()), size_(s.size()) {} template >::value)> - FMT_CONSTEXPR basic_string_view(S s) FMT_NOEXCEPT : data_(s.data()), - size_(s.size()) {} + FMT_CONSTEXPR basic_string_view(S s) noexcept + : data_(s.data()), size_(s.size()) {} /** Returns a pointer to the string data. */ - constexpr auto data() const FMT_NOEXCEPT -> const Char* { return data_; } + constexpr auto data() const noexcept -> const Char* { return data_; } /** Returns the string size. */ - constexpr auto size() const FMT_NOEXCEPT -> size_t { return size_; } + constexpr auto size() const noexcept -> size_t { return size_; } - constexpr auto begin() const FMT_NOEXCEPT -> iterator { return data_; } - constexpr auto end() const FMT_NOEXCEPT -> iterator { return data_ + size_; } + constexpr auto begin() const noexcept -> iterator { return data_; } + constexpr auto end() const noexcept -> iterator { return data_ + size_; } - constexpr auto operator[](size_t pos) const FMT_NOEXCEPT -> const Char& { + constexpr auto operator[](size_t pos) const noexcept -> const Char& { return data_[pos]; } - FMT_CONSTEXPR void remove_prefix(size_t n) FMT_NOEXCEPT { + FMT_CONSTEXPR void remove_prefix(size_t n) noexcept { data_ += n; size_ -= n; } @@ -543,6 +523,14 @@ using string_view = basic_string_view; template struct is_char : std::false_type {}; template <> struct is_char : std::true_type {}; +FMT_BEGIN_DETAIL_NAMESPACE + +// A base class for compile-time strings. +struct compile_string {}; + +template +struct is_compile_string : std::is_base_of {}; + // Returns a string view of `s`. template ::value)> FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view { @@ -559,33 +547,21 @@ constexpr auto to_string_view(basic_string_view s) return s; } template >::value)> -inline auto to_string_view(detail::std_string_view s) - -> basic_string_view { + FMT_ENABLE_IF(!std::is_empty>::value)> +inline auto to_string_view(std_string_view s) -> basic_string_view { return 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_STRING("{}"), 42). -struct compile_string {}; - -template -struct is_compile_string : std::is_base_of {}; - template ::value)> constexpr auto to_string_view(const S& s) -> basic_string_view { return basic_string_view(s); } - -FMT_BEGIN_DETAIL_NAMESPACE - void to_string_view(...); -using fmt::to_string_view; // Specifies whether S is a string type convertible to fmt::basic_string_view. // It should be a constexpr function but MSVC 2017 fails to compile it in // enable_if and MSVC 2015 fails to compile it as an alias template. +// ADL invocation of to_string_view is DEPRECATED! template struct is_string : std::is_class()))> { }; @@ -596,17 +572,60 @@ template struct char_t_impl::value>> { using type = typename result::value_type; }; -// Reports a compile-time error if S is not a valid format string. -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 use " - "FMT_STRING."); -#endif +enum class type { + none_type, + // Integer types should go first, + int_type, + uint_type, + long_long_type, + ulong_long_type, + int128_type, + uint128_type, + bool_type, + char_type, + last_integer_type = char_type, + // followed by floating-point types. + float_type, + double_type, + long_double_type, + last_numeric_type = long_double_type, + cstring_type, + string_type, + pointer_type, + custom_type +}; + +// Maps core type T to the corresponding type enum constant. +template +struct type_constant : std::integral_constant {}; + +#define FMT_TYPE_CONSTANT(Type, constant) \ + template \ + struct type_constant \ + : std::integral_constant {} + +FMT_TYPE_CONSTANT(int, int_type); +FMT_TYPE_CONSTANT(unsigned, uint_type); +FMT_TYPE_CONSTANT(long long, long_long_type); +FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type); +FMT_TYPE_CONSTANT(int128_opt, int128_type); +FMT_TYPE_CONSTANT(uint128_opt, uint128_type); +FMT_TYPE_CONSTANT(bool, bool_type); +FMT_TYPE_CONSTANT(Char, char_type); +FMT_TYPE_CONSTANT(float, float_type); +FMT_TYPE_CONSTANT(double, double_type); +FMT_TYPE_CONSTANT(long double, long_double_type); +FMT_TYPE_CONSTANT(const Char*, cstring_type); +FMT_TYPE_CONSTANT(basic_string_view, string_type); +FMT_TYPE_CONSTANT(const void*, pointer_type); + +constexpr bool is_integral_type(type t) { + return t > type::none_type && t <= type::last_integer_type; +} + +constexpr bool is_arithmetic_type(type t) { + return t > type::none_type && t <= type::last_numeric_type; } -template ::value)> -void check_format_string(S); FMT_NORETURN FMT_API void throw_format_error(const char* message); @@ -615,7 +634,9 @@ struct error_handler { constexpr error_handler(const error_handler&) = default; // This function is intentionally not constexpr to give a compile-time error. - FMT_NORETURN FMT_API void on_error(const char* message); + FMT_NORETURN void on_error(const char* message) { + throw_format_error(message); + } }; FMT_END_DETAIL_NAMESPACE @@ -635,6 +656,8 @@ class basic_format_parse_context : private ErrorHandler { basic_string_view format_str_; int next_arg_id_; + FMT_CONSTEXPR void do_check_arg_id(int id); + public: using char_type = Char; using iterator = typename basic_string_view::iterator; @@ -648,16 +671,14 @@ class basic_format_parse_context : private ErrorHandler { Returns an iterator to the beginning of the format string range being parsed. */ - constexpr auto begin() const FMT_NOEXCEPT -> iterator { + constexpr auto begin() const noexcept -> iterator { return format_str_.begin(); } /** Returns an iterator past the end of the format string range being parsed. */ - constexpr auto end() const FMT_NOEXCEPT -> iterator { - return format_str_.end(); - } + constexpr auto end() const noexcept -> iterator { return format_str_.end(); } /** Advances the begin iterator to ``it``. */ FMT_CONSTEXPR void advance_to(iterator it) { @@ -669,25 +690,29 @@ class basic_format_parse_context : private ErrorHandler { the next argument index and switches to the automatic indexing. */ FMT_CONSTEXPR auto next_arg_id() -> int { - // Don't check if the argument id is valid to avoid overhead and because it - // will be checked during formatting anyway. - if (next_arg_id_ >= 0) return next_arg_id_++; - on_error("cannot switch from manual to automatic argument indexing"); - return 0; + if (next_arg_id_ < 0) { + on_error("cannot switch from manual to automatic argument indexing"); + return 0; + } + int id = next_arg_id_++; + do_check_arg_id(id); + return id; } /** Reports an error if using the automatic argument indexing; otherwise switches to the manual indexing. */ - FMT_CONSTEXPR void check_arg_id(int) { - if (next_arg_id_ > 0) + FMT_CONSTEXPR void check_arg_id(int id) { + if (next_arg_id_ > 0) { on_error("cannot switch from automatic to manual argument indexing"); - else - next_arg_id_ = -1; + return; + } + next_arg_id_ = -1; + do_check_arg_id(id); } - FMT_CONSTEXPR void check_arg_id(basic_string_view) {} + FMT_CONSTEXPR void check_dynamic_spec(int arg_id); FMT_CONSTEXPR void on_error(const char* message) { ErrorHandler::on_error(message); @@ -698,6 +723,65 @@ class basic_format_parse_context : private ErrorHandler { using format_parse_context = basic_format_parse_context; +FMT_BEGIN_DETAIL_NAMESPACE +// A parse context with extra data used only in compile-time checks. +template +class compile_parse_context + : public basic_format_parse_context { + private: + int num_args_; + const type* types_; + using base = basic_format_parse_context; + + public: + explicit FMT_CONSTEXPR compile_parse_context( + basic_string_view format_str, int num_args, const type* types, + ErrorHandler eh = {}, int next_arg_id = 0) + : base(format_str, eh, next_arg_id), num_args_(num_args), types_(types) {} + + constexpr auto num_args() const -> int { return num_args_; } + constexpr auto arg_type(int id) const -> type { return types_[id]; } + + FMT_CONSTEXPR auto next_arg_id() -> int { + int id = base::next_arg_id(); + if (id >= num_args_) this->on_error("argument not found"); + return id; + } + + FMT_CONSTEXPR void check_arg_id(int id) { + base::check_arg_id(id); + if (id >= num_args_) this->on_error("argument not found"); + } + using base::check_arg_id; + + FMT_CONSTEXPR void check_dynamic_spec(int arg_id) { + if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id])) + this->on_error("width/precision is not integer"); + } +}; +FMT_END_DETAIL_NAMESPACE + +template +FMT_CONSTEXPR void +basic_format_parse_context::do_check_arg_id(int id) { + // Argument id is only checked at compile-time during parsing because + // formatting has its own validation. + if (detail::is_constant_evaluated() && FMT_GCC_VERSION >= 1200) { + using context = detail::compile_parse_context; + if (id >= static_cast(this)->num_args()) + on_error("argument not found"); + } +} + +template +FMT_CONSTEXPR void +basic_format_parse_context::check_dynamic_spec(int arg_id) { + if (detail::is_constant_evaluated()) { + using context = detail::compile_parse_context; + static_cast(this)->check_dynamic_spec(arg_id); + } +} + template class basic_format_arg; template class basic_format_args; template class dynamic_format_arg_store; @@ -744,10 +828,10 @@ constexpr auto has_const_formatter() -> bool { template inline auto get_container(std::back_insert_iterator it) -> Container& { - using bi_iterator = std::back_insert_iterator; - struct accessor : bi_iterator { - accessor(bi_iterator iter) : bi_iterator(iter) {} - using bi_iterator::container; + using base = std::back_insert_iterator; + struct accessor : base { + accessor(base b) : base(b) {} + using base::container; }; return *accessor(it).container; } @@ -784,18 +868,16 @@ template class buffer { protected: // Don't initialize ptr_ since it is not accessed to save a few cycles. FMT_MSC_WARNING(suppress : 26495) - buffer(size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz) {} + buffer(size_t sz) noexcept : size_(sz), capacity_(sz) {} - FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, - size_t cap = 0) FMT_NOEXCEPT : ptr_(p), - size_(sz), - capacity_(cap) {} + FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) noexcept + : ptr_(p), size_(sz), capacity_(cap) {} FMT_CONSTEXPR20 ~buffer() = default; buffer(buffer&&) = default; /** Sets the buffer data and capacity. */ - FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT { + FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept { ptr_ = buf_data; capacity_ = buf_capacity; } @@ -810,23 +892,23 @@ template class buffer { buffer(const buffer&) = delete; void operator=(const buffer&) = delete; - auto begin() FMT_NOEXCEPT -> T* { return ptr_; } - auto end() FMT_NOEXCEPT -> T* { return ptr_ + size_; } + auto begin() noexcept -> T* { return ptr_; } + auto end() noexcept -> T* { return ptr_ + size_; } - auto begin() const FMT_NOEXCEPT -> const T* { return ptr_; } - auto end() const FMT_NOEXCEPT -> const T* { return ptr_ + size_; } + auto begin() const noexcept -> const T* { return ptr_; } + auto end() const noexcept -> const T* { return ptr_ + size_; } /** Returns the size of this buffer. */ - constexpr auto size() const FMT_NOEXCEPT -> size_t { return size_; } + constexpr auto size() const noexcept -> size_t { return size_; } /** Returns the capacity of this buffer. */ - constexpr auto capacity() const FMT_NOEXCEPT -> size_t { return capacity_; } + constexpr auto capacity() const noexcept -> size_t { return capacity_; } /** Returns a pointer to the buffer data. */ - FMT_CONSTEXPR auto data() FMT_NOEXCEPT -> T* { return ptr_; } + FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; } /** Returns a pointer to the buffer data. */ - FMT_CONSTEXPR auto data() const FMT_NOEXCEPT -> const T* { return ptr_; } + FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; } /** Clears this buffer. */ void clear() { size_ = 0; } @@ -854,11 +936,11 @@ template class buffer { /** Appends data to the end of the buffer. */ template void append(const U* begin, const U* end); - template FMT_CONSTEXPR auto operator[](I index) -> T& { + template FMT_CONSTEXPR auto operator[](Idx index) -> T& { return ptr_[index]; } - template - FMT_CONSTEXPR auto operator[](I index) const -> const T& { + template + FMT_CONSTEXPR auto operator[](Idx index) const -> const T& { return ptr_[index]; } }; @@ -993,6 +1075,7 @@ class iterator_buffer, : buffer(c.size()), container_(c) {} explicit iterator_buffer(std::back_insert_iterator out, size_t = 0) : iterator_buffer(get_container(out)) {} + auto out() -> std::back_insert_iterator { return std::back_inserter(container_); } @@ -1044,7 +1127,11 @@ struct fallback_formatter { // Specifies if T has an enabled fallback_formatter specialization. template using has_fallback_formatter = +#ifdef FMT_DEPRECATED_OSTREAM std::is_constructible>; +#else + std::false_type; +#endif struct view {}; @@ -1128,61 +1215,6 @@ constexpr auto count_statically_named_args() -> size_t { return count::value...>(); } -enum class type { - none_type, - // Integer types should go first, - int_type, - uint_type, - long_long_type, - ulong_long_type, - int128_type, - uint128_type, - bool_type, - char_type, - last_integer_type = char_type, - // followed by floating-point types. - float_type, - double_type, - long_double_type, - last_numeric_type = long_double_type, - cstring_type, - string_type, - pointer_type, - custom_type -}; - -// Maps core type T to the corresponding type enum constant. -template -struct type_constant : std::integral_constant {}; - -#define FMT_TYPE_CONSTANT(Type, constant) \ - template \ - struct type_constant \ - : std::integral_constant {} - -FMT_TYPE_CONSTANT(int, int_type); -FMT_TYPE_CONSTANT(unsigned, uint_type); -FMT_TYPE_CONSTANT(long long, long_long_type); -FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type); -FMT_TYPE_CONSTANT(int128_t, int128_type); -FMT_TYPE_CONSTANT(uint128_t, uint128_type); -FMT_TYPE_CONSTANT(bool, bool_type); -FMT_TYPE_CONSTANT(Char, char_type); -FMT_TYPE_CONSTANT(float, float_type); -FMT_TYPE_CONSTANT(double, double_type); -FMT_TYPE_CONSTANT(long double, long_double_type); -FMT_TYPE_CONSTANT(const Char*, cstring_type); -FMT_TYPE_CONSTANT(basic_string_view, string_type); -FMT_TYPE_CONSTANT(const void*, pointer_type); - -constexpr bool is_integral_type(type t) { - return t > type::none_type && t <= type::last_integer_type; -} - -constexpr bool is_arithmetic_type(type t) { - return t > type::none_type && t <= type::last_numeric_type; -} - struct unformattable {}; struct unformattable_char : unformattable {}; struct unformattable_const : unformattable {}; @@ -1215,8 +1247,8 @@ template class value { unsigned uint_value; long long long_long_value; unsigned long long ulong_long_value; - int128_t int128_value; - uint128_t uint128_value; + int128_opt int128_value; + uint128_opt uint128_value; bool bool_value; char_type char_value; float float_value; @@ -1233,8 +1265,8 @@ template class value { constexpr FMT_INLINE value(unsigned val) : uint_value(val) {} constexpr FMT_INLINE value(long long val) : long_long_value(val) {} constexpr FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {} - FMT_INLINE value(int128_t val) : int128_value(val) {} - FMT_INLINE value(uint128_t val) : uint128_value(val) {} + FMT_INLINE value(int128_opt val) : int128_value(val) {} + FMT_INLINE value(uint128_opt val) : uint128_value(val) {} constexpr FMT_INLINE value(float val) : float_value(val) {} constexpr FMT_INLINE value(double val) : double_value(val) {} FMT_INLINE value(long double val) : long_double_value(val) {} @@ -1284,7 +1316,7 @@ template class value { }; template -FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg; +FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg; // To minimize the number of types we need to deal with, long is translated // either to int or to long long depending on its size. @@ -1292,6 +1324,21 @@ enum { long_short = sizeof(long) == sizeof(int) }; using long_type = conditional_t; using ulong_type = conditional_t; +#ifdef __cpp_lib_byte +inline auto format_as(std::byte b) -> unsigned char { + return static_cast(b); +} +#endif + +template struct has_format_as { + template ::value&& std::is_integral::value)> + static auto check(U*) -> std::true_type; + static auto check(...) -> std::false_type; + + enum { value = decltype(check(static_cast(nullptr)))::value }; +}; + // Maps formatting arguments to core types. // arg_mapper reports errors by returning unformattable instead of using // static_assert because it's used in the is_formattable trait. @@ -1317,8 +1364,12 @@ template struct arg_mapper { -> unsigned long long { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(int128_t val) -> int128_t { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(uint128_t val) -> uint128_t { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(int128_opt val) -> int128_opt { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(uint128_opt val) -> uint128_opt { + return val; + } FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { return val; } template ::value || @@ -1365,45 +1416,24 @@ template struct arg_mapper { } template , T>::value && + std::is_convertible>::value && !is_string::value && !has_formatter::value && !has_fallback_formatter::value)> FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> basic_string_view { return basic_string_view(val); } - template < - typename T, - FMT_ENABLE_IF( - std::is_constructible, T>::value && - !std::is_constructible, T>::value && - !is_string::value && !has_formatter::value && - !has_fallback_formatter::value)> + template >::value && + !std::is_convertible>::value && + !is_string::value && !has_formatter::value && + !has_fallback_formatter::value)> FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> basic_string_view { return std_string_view(val); } - using cstring_result = conditional_t::value, - const char*, unformattable_pointer>; - - FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const signed char* val) - -> cstring_result { - return map(reinterpret_cast(val)); - } - FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const unsigned char* val) - -> cstring_result { - return map(reinterpret_cast(val)); - } - FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(signed char* val) - -> cstring_result { - return map(reinterpret_cast(val)); - } - FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(unsigned char* val) - -> cstring_result { - return map(reinterpret_cast(val)); - } - FMT_CONSTEXPR FMT_INLINE auto map(void* val) -> const void* { return val; } FMT_CONSTEXPR FMT_INLINE auto map(const void* val) -> const void* { return val; @@ -1417,10 +1447,11 @@ template struct arg_mapper { template < typename T, FMT_ENABLE_IF( - std::is_member_pointer::value || + std::is_pointer::value || std::is_member_pointer::value || std::is_function::type>::value || (std::is_convertible::value && - !std::is_convertible::value))> + !std::is_convertible::value && + !has_formatter::value))> FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer { return {}; } @@ -1434,16 +1465,19 @@ template struct arg_mapper { template ::value&& std::is_convertible::value && - !has_formatter::value && + !has_format_as::value && !has_formatter::value && !has_fallback_formatter::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> decltype(std::declval().map( - static_cast::type>(val))) { - return map(static_cast::type>(val)); + static_cast>(val))) { + return map(static_cast>(val)); } - FMT_CONSTEXPR FMT_INLINE auto map(detail::byte val) -> unsigned { - return map(static_cast(val)); + template ::value && + !has_formatter::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + -> decltype(std::declval().map(format_as(T()))) { + return map(format_as(val)); } template > @@ -1452,8 +1486,9 @@ template struct arg_mapper { !std::is_const>::value || has_fallback_formatter::value> {}; -#if FMT_MSC_VER != 0 && FMT_MSC_VER < 1910 - // Workaround a bug in MSVC. +#if (FMT_MSC_VERSION != 0 && FMT_MSC_VERSION < 1910) || \ + FMT_ICC_VERSION != 0 || defined(__NVCC__) + // Workaround a bug in MSVC and Intel (Issue 2746). template FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& { return val; } @@ -1471,6 +1506,8 @@ template struct arg_mapper { template , FMT_ENABLE_IF(!is_string::value && !is_char::value && !std::is_array::value && + !std::is_pointer::value && + !has_format_as::value && (has_formatter::value || has_fallback_formatter::value))> FMT_CONSTEXPR FMT_INLINE auto map(T&& val) @@ -1513,12 +1550,11 @@ class appender : public std::back_insert_iterator> { public: using std::back_insert_iterator>::back_insert_iterator; - appender(base it) FMT_NOEXCEPT : base(it) {} - using _Unchecked_type = appender; // Mark iterator as checked. + appender(base it) noexcept : base(it) {} + FMT_UNCHECKED_ITERATOR(appender); - auto operator++() FMT_NOEXCEPT -> appender& { return *this; } - - auto operator++(int) FMT_NOEXCEPT -> appender { return *this; } + auto operator++() noexcept -> appender& { return *this; } + auto operator++(int) noexcept -> appender { return *this; } }; // A formatting argument. It is a trivially copyable/constructible type to @@ -1529,7 +1565,7 @@ template class basic_format_arg { detail::type type_; template - friend FMT_CONSTEXPR auto detail::make_arg(const T& value) + friend FMT_CONSTEXPR auto detail::make_arg(T&& value) -> basic_format_arg; template @@ -1564,7 +1600,7 @@ template class basic_format_arg { constexpr basic_format_arg() : type_(detail::type::none_type) {} - constexpr explicit operator bool() const FMT_NOEXCEPT { + constexpr explicit operator bool() const noexcept { return type_ != detail::type::none_type; } @@ -1632,6 +1668,11 @@ auto copy_str(InputIt begin, InputIt end, appender out) -> appender { return out; } +template +FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt { + return detail::copy_str(rng.begin(), rng.end(), out); +} + #if FMT_GCC_VERSION && FMT_GCC_VERSION < 500 // A workaround for gcc 4.8 to make void_t work in a SFINAE context. template struct void_t_impl { using type = void; }; @@ -1665,7 +1706,7 @@ struct is_contiguous_back_insert_iterator> template <> struct is_contiguous_back_insert_iterator : std::true_type {}; -// A type-erased reference to an std::locale to avoid heavy include. +// A type-erased reference to an std::locale to avoid a heavy include. class locale_ref { private: const void* locale_; // A type-erased pointer to std::locale. @@ -1674,7 +1715,7 @@ class locale_ref { constexpr locale_ref() : locale_(nullptr) {} template explicit locale_ref(const Locale& loc); - explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; } + explicit operator bool() const noexcept { return locale_ != nullptr; } template auto get() const -> Locale; }; @@ -1690,20 +1731,8 @@ constexpr auto encode_types() -> unsigned long long { } template -FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg { - basic_format_arg arg; - arg.type_ = mapped_type_constant::value; - arg.value_ = arg_mapper().map(value); - return arg; -} - -// The type template parameter is there to avoid an ODR violation when using -// a fallback formatter in one translation unit and an implicit conversion in -// another (not recommended). -template -FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value { - const auto& arg = arg_mapper().map(std::forward(val)); +FMT_CONSTEXPR FMT_INLINE auto make_value(T&& val) -> value { + const auto& arg = arg_mapper().map(FMT_FORWARD(val)); constexpr bool formattable_char = !std::is_same::value; @@ -1731,9 +1760,26 @@ FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value { return {arg}; } +template +FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg { + basic_format_arg arg; + arg.type_ = mapped_type_constant::value; + arg.value_ = make_value(value); + return arg; +} + +// The type template parameter is there to avoid an ODR violation when using +// a fallback formatter in one translation unit and an implicit conversion in +// another (not recommended). +template +FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value { + return make_value(val); +} + template -inline auto make_arg(const T& value) -> basic_format_arg { +FMT_CONSTEXPR inline auto make_arg(T&& value) -> basic_format_arg { return make_arg(value); } FMT_END_DETAIL_NAMESPACE @@ -1853,7 +1899,7 @@ class format_arg_store data_{detail::make_arg< is_packed, Context, detail::mapped_type_constant, Context>::value>( - std::forward(args))...} { + FMT_FORWARD(args))...} { detail::init_named_args(data_.named_args(), 0, 0, args...); } }; @@ -1869,7 +1915,7 @@ class format_arg_store template constexpr auto make_format_args(Args&&... args) -> format_arg_store...> { - return {std::forward(args)...}; + return {FMT_FORWARD(args)...}; } /** @@ -2015,14 +2061,22 @@ template class basic_format_args { // between clang and gcc on ARM (#1919). using format_args = basic_format_args; -// We cannot use enum classes as bit fields because of a gcc bug -// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414. +// We cannot use enum classes as bit fields because of a gcc bug, so we put them +// in namespaces instead (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414). +// Additionally, if an underlying type is specified, older gcc incorrectly warns +// that the type is too small. Both bugs are fixed in gcc 9.3. +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 903 +# define FMT_ENUM_UNDERLYING_TYPE(type) +#else +# define FMT_ENUM_UNDERLYING_TYPE(type) : type +#endif namespace align { -enum type { none, left, right, center, numeric }; +enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, left, right, center, + numeric}; } using align_t = align::type; namespace sign { -enum type { none, minus, plus, space }; +enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, minus, plus, space}; } using sign_t = sign::type; @@ -2072,7 +2126,8 @@ enum class presentation_type : unsigned char { general_upper, // 'G' chr, // 'c' string, // 's' - pointer // 'p' + pointer, // 'p' + debug // '?' }; // Format specifiers for built-in and string types. @@ -2209,11 +2264,14 @@ class dynamic_specs_handler FMT_CONSTEXPR auto make_arg_ref(int arg_id) -> arg_ref_type { context_.check_arg_id(arg_id); + context_.check_dynamic_spec(arg_id); return arg_ref_type(arg_id); } FMT_CONSTEXPR auto make_arg_ref(auto_id) -> arg_ref_type { - return arg_ref_type(context_.next_arg_id()); + int arg_id = context_.next_arg_id(); + context_.check_dynamic_spec(arg_id); + return arg_ref_type(arg_id); } FMT_CONSTEXPR auto make_arg_ref(basic_string_view arg_id) @@ -2231,21 +2289,23 @@ template constexpr bool is_ascii_letter(Char c) { // Converts a character to ASCII. Returns a number > 127 on conversion failure. template ::value)> -constexpr auto to_ascii(Char value) -> Char { - return value; +constexpr auto to_ascii(Char c) -> Char { + return c; } template ::value)> -constexpr auto to_ascii(Char value) -> - typename std::underlying_type::type { - return value; +constexpr auto to_ascii(Char c) -> underlying_t { + return c; +} + +FMT_CONSTEXPR inline auto code_point_length_impl(char c) -> int { + return "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4" + [static_cast(c) >> 3]; } template FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int { if (const_check(sizeof(Char) != 1)) return 1; - auto lengths = - "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4"; - int len = lengths[static_cast(*begin) >> 3]; + int len = code_point_length_impl(static_cast(*begin)); // Compute the pointer to the next character early so that the next // iteration can start working on the next character. Neither Clang @@ -2302,7 +2362,7 @@ FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, FMT_ASSERT(begin != end, ""); auto align = align::none; auto p = begin + code_point_length(begin); - if (p >= end) p = begin; + if (end - p <= 0) p = begin; for (;;) { switch (to_ascii(*p)) { case '<': @@ -2488,6 +2548,8 @@ FMT_CONSTEXPR auto parse_presentation_type(Char type) -> presentation_type { return presentation_type::string; case 'p': return presentation_type::pointer; + case '?': + return presentation_type::debug; default: return presentation_type::none; } @@ -2634,22 +2696,22 @@ FMT_CONSTEXPR FMT_INLINE void parse_format_string( handler.on_text(begin, end); return; } - struct writer { - FMT_CONSTEXPR void operator()(const Char* pbegin, const Char* pend) { - if (pbegin == pend) return; + struct pfs_writer { + FMT_CONSTEXPR void operator()(const Char* from, const Char* to) { + if (from == to) return; for (;;) { const Char* p = nullptr; - if (!find(pbegin, pend, Char('}'), p)) - return handler_.on_text(pbegin, pend); + if (!find(from, to, Char('}'), p)) + return handler_.on_text(from, to); ++p; - if (p == pend || *p != '}') + if (p == to || *p != '}') return handler_.on_error("unmatched '}' in format string"); - handler_.on_text(pbegin, p); - pbegin = p + 1; + handler_.on_text(from, p); + from = p + 1; } } Handler& handler_; - } write{handler}; + } write = {handler}; while (begin != end) { // Doing two passes with memchr (one for '{' and another for '}') is up to // 2.5x faster than the naive one-pass implementation on big format strings. @@ -2661,50 +2723,29 @@ FMT_CONSTEXPR FMT_INLINE void parse_format_string( } } +template ::value> struct strip_named_arg { + using type = T; +}; +template struct strip_named_arg { + using type = remove_cvref_t; +}; + template FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx) -> decltype(ctx.begin()) { using char_type = typename ParseContext::char_type; using context = buffer_context; + using stripped_type = typename strip_named_arg::type; using mapped_type = conditional_t< mapped_type_constant::value != type::custom_type, - decltype(arg_mapper().map(std::declval())), T>; + decltype(arg_mapper().map(std::declval())), + stripped_type>; auto f = conditional_t::value, formatter, - fallback_formatter>(); + fallback_formatter>(); return f.parse(ctx); } -// A parse context with extra argument id checks. It is only used at compile -// time because adding checks at runtime would introduce substantial overhead -// and would be redundant since argument ids are checked when arguments are -// retrieved anyway. -template -class compile_parse_context - : public basic_format_parse_context { - private: - int num_args_; - using base = basic_format_parse_context; - - public: - explicit FMT_CONSTEXPR compile_parse_context( - basic_string_view format_str, - int num_args = (std::numeric_limits::max)(), ErrorHandler eh = {}) - : base(format_str, eh), num_args_(num_args) {} - - FMT_CONSTEXPR auto next_arg_id() -> int { - int id = base::next_arg_id(); - if (id >= num_args_) this->on_error("argument not found"); - return id; - } - - FMT_CONSTEXPR void check_arg_id(int id) { - base::check_arg_id(id); - if (id >= num_args_) this->on_error("argument not found"); - } - using base::check_arg_id; -}; - template FMT_CONSTEXPR void check_int_type_spec(presentation_type type, ErrorHandler&& eh) { @@ -2717,7 +2758,8 @@ template FMT_CONSTEXPR auto check_char_specs(const basic_format_specs& specs, ErrorHandler&& eh = {}) -> bool { if (specs.type != presentation_type::none && - specs.type != presentation_type::chr) { + specs.type != presentation_type::chr && + specs.type != presentation_type::debug) { check_int_type_spec(specs.type, eh); return false; } @@ -2741,7 +2783,6 @@ struct float_specs { bool upper : 1; bool locale : 1; bool binary32 : 1; - bool fallback : 1; bool showpoint : 1; }; @@ -2792,7 +2833,8 @@ FMT_CONSTEXPR auto parse_float_type_spec(const basic_format_specs& specs, template FMT_CONSTEXPR auto check_cstring_type_spec(presentation_type type, ErrorHandler&& eh = {}) -> bool { - if (type == presentation_type::none || type == presentation_type::string) + if (type == presentation_type::none || type == presentation_type::string || + type == presentation_type::debug) return true; if (type != presentation_type::pointer) eh.on_error("invalid type specifier"); return false; @@ -2801,7 +2843,8 @@ FMT_CONSTEXPR auto check_cstring_type_spec(presentation_type type, template FMT_CONSTEXPR void check_string_type_spec(presentation_type type, ErrorHandler&& eh = {}) { - if (type != presentation_type::none && type != presentation_type::string) + if (type != presentation_type::none && type != presentation_type::string && + type != presentation_type::debug) eh.on_error("invalid type specifier"); } @@ -2835,7 +2878,8 @@ template class specs_checker : public Handler { FMT_CONSTEXPR void on_sign(sign_t s) { require_numeric_argument(); if (is_integral_type(arg_type_) && arg_type_ != type::int_type && - arg_type_ != type::long_long_type && arg_type_ != type::char_type) { + arg_type_ != type::long_long_type && arg_type_ != type::int128_type && + arg_type_ != type::char_type) { this->on_error("format specifier requires signed argument"); } Handler::on_sign(s); @@ -2864,7 +2908,7 @@ template class specs_checker : public Handler { constexpr int invalid_arg_index = -1; -#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +#if FMT_USE_NONTYPE_TEMPLATE_ARGS template constexpr auto get_arg_index_by_name(basic_string_view name) -> int { if constexpr (detail::is_statically_named_arg()) { @@ -2879,7 +2923,7 @@ constexpr auto get_arg_index_by_name(basic_string_view name) -> int { template FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { -#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +#if FMT_USE_NONTYPE_TEMPLATE_ARGS if constexpr (sizeof...(Args) > 0) return get_arg_index_by_name<0, Args...>(name); #endif @@ -2890,20 +2934,28 @@ FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { template class format_string_checker { private: + // In the future basic_format_parse_context will replace compile_parse_context + // here and will use is_constant_evaluated and downcasting to access the data + // needed for compile-time checks: https://godbolt.org/z/GvWzcTjh1. using parse_context_type = compile_parse_context; - enum { num_args = sizeof...(Args) }; + static constexpr int num_args = sizeof...(Args); // Format specifier parsing function. using parse_func = const Char* (*)(parse_context_type&); parse_context_type context_; - parse_func parse_funcs_[num_args > 0 ? num_args : 1]; + parse_func parse_funcs_[num_args > 0 ? static_cast(num_args) : 1]; + type types_[num_args > 0 ? static_cast(num_args) : 1]; public: explicit FMT_CONSTEXPR format_string_checker( basic_string_view format_str, ErrorHandler eh) - : context_(format_str, num_args, eh), - parse_funcs_{&parse_format_specs...} {} + : context_(format_str, num_args, types_, eh), + parse_funcs_{&parse_format_specs...}, + types_{ + mapped_type_constant>::value...} { + } FMT_CONSTEXPR void on_text(const Char*, const Char*) {} @@ -2912,7 +2964,7 @@ class format_string_checker { return context_.check_arg_id(id), id; } FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { -#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +#if FMT_USE_NONTYPE_TEMPLATE_ARGS auto index = get_arg_index_by_name(id); if (index == invalid_arg_index) on_error("named argument is not found"); return context_.check_arg_id(index), index; @@ -2937,10 +2989,19 @@ class format_string_checker { } }; +// Reports a compile-time error if S is not a valid format string. +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 use " + "FMT_STRING."); +#endif +} template ::value), int>> + FMT_ENABLE_IF(is_compile_string::value)> void check_format_string(S format_str) { - FMT_CONSTEXPR auto s = to_string_view(format_str); + FMT_CONSTEXPR auto s = basic_string_view(format_str); using checker = format_string_checker...>; FMT_CONSTEXPR bool invalid_format = @@ -3038,11 +3099,41 @@ struct formatter::value, + enable_if_t<(U == detail::type::string_type || + U == detail::type::cstring_type || + U == detail::type::char_type), + int> = 0> + FMT_CONSTEXPR void set_debug_format() { + specs_.type = presentation_type::debug; + } + template FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const -> decltype(ctx.out()); }; +#define FMT_FORMAT_AS(Type, Base) \ + template \ + struct formatter : formatter { \ + template \ + auto format(Type const& val, FormatContext& ctx) const \ + -> decltype(ctx.out()) { \ + return formatter::format(static_cast(val), ctx); \ + } \ + } + +FMT_FORMAT_AS(signed char, int); +FMT_FORMAT_AS(unsigned char, unsigned); +FMT_FORMAT_AS(short, int); +FMT_FORMAT_AS(unsigned short, unsigned); +FMT_FORMAT_AS(long, long long); +FMT_FORMAT_AS(unsigned long, unsigned long long); +FMT_FORMAT_AS(Char*, const Char*); +FMT_FORMAT_AS(std::basic_string, basic_string_view); +FMT_FORMAT_AS(std::nullptr_t, const void*); +FMT_FORMAT_AS(detail::std_string_view, basic_string_view); + template struct basic_runtime { basic_string_view str; }; /** A compile-time format string. */ @@ -3078,10 +3169,8 @@ template class basic_format_string { #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 // Workaround broken conversion on older gcc. -template using format_string = string_view; -template auto runtime(const S& s) -> basic_string_view> { - return s; -} +template using format_string = string_view; +inline auto runtime(string_view s) -> string_view { return s; } #else template using format_string = basic_format_string...>; @@ -3095,9 +3184,7 @@ using format_string = basic_format_string...>; fmt::print(fmt::runtime("{:d}"), "I am not a number"); \endrst */ -template auto runtime(const S& s) -> basic_runtime> { - return {{s}}; -} +inline auto runtime(string_view s) -> basic_runtime { return {{s}}; } #endif FMT_API auto vformat(string_view fmt, format_args args) -> std::string;