diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-07 04:48:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-07 04:48:35 +0000 |
commit | 207df6fc406e81bfeebdff7f404bd242ff3f099f (patch) | |
tree | a1a796b056909dd0a04ffec163db9363a8757808 /src/fmtlib/fmt/core.h | |
parent | Releasing progress-linux version 0.11.2-1~progress7.99u1. (diff) | |
download | lnav-207df6fc406e81bfeebdff7f404bd242ff3f099f.tar.xz lnav-207df6fc406e81bfeebdff7f404bd242ff3f099f.zip |
Merging upstream version 0.12.2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/fmtlib/fmt/core.h')
-rw-r--r-- | src/fmtlib/fmt/core.h | 535 |
1 files changed, 253 insertions, 282 deletions
diff --git a/src/fmtlib/fmt/core.h b/src/fmtlib/fmt/core.h index 46723d5..f9e3b7d 100644 --- a/src/fmtlib/fmt/core.h +++ b/src/fmtlib/fmt/core.h @@ -13,11 +13,12 @@ #include <cstring> // std::strlen #include <iterator> #include <limits> +#include <memory> // std::addressof #include <string> #include <type_traits> // The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 100000 +#define FMT_VERSION 100101 #if defined(__clang__) && !defined(__ibmxl__) # define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) @@ -92,7 +93,7 @@ #ifndef FMT_USE_CONSTEXPR # if (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 || \ (FMT_GCC_VERSION >= 600 && FMT_CPLUSPLUS >= 201402L)) && \ - !FMT_ICC_VERSION && !defined(__NVCC__) + !FMT_ICC_VERSION && (!defined(__NVCC__) || FMT_CPLUSPLUS >= 202002L) # define FMT_USE_CONSTEXPR 1 # else # define FMT_USE_CONSTEXPR 0 @@ -162,9 +163,6 @@ # endif #endif -// An inline std::forward replacement. -#define FMT_FORWARD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__) - #ifdef _MSC_VER # define FMT_UNCHECKED_ITERATOR(It) \ using _Unchecked_type = It // Mark iterator as checked. @@ -181,8 +179,8 @@ } #endif -#ifndef FMT_MODULE_EXPORT -# define FMT_MODULE_EXPORT +#ifndef FMT_EXPORT +# define FMT_EXPORT # define FMT_BEGIN_EXPORT # define FMT_END_EXPORT #endif @@ -244,12 +242,6 @@ # endif #endif -#if defined __cpp_inline_variables && __cpp_inline_variables >= 201606L -# define FMT_INLINE_VARIABLE inline -#else -# define FMT_INLINE_VARIABLE -#endif - // Enable minimal optimizations for more compact code in debug mode. FMT_GCC_PRAGMA("GCC push_options") #if !defined(__OPTIMIZE__) && !defined(__NVCOMPILER) && !defined(__LCC__) && \ @@ -276,6 +268,11 @@ template <typename T> using type_identity_t = typename type_identity<T>::type; template <typename T> using underlying_t = typename std::underlying_type<T>::type; +// Checks whether T is a container with contiguous storage. +template <typename T> struct is_contiguous : std::false_type {}; +template <typename Char> +struct is_contiguous<std::basic_string<Char>> : std::true_type {}; + struct monostate { constexpr monostate() {} }; @@ -289,8 +286,11 @@ struct monostate { # define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0 #endif +// This is defined in core.h instead of format.h to avoid injecting in std. +// It is a template to avoid undesirable implicit conversions to std::byte. #ifdef __cpp_lib_byte -inline auto format_as(std::byte b) -> unsigned char { +template <typename T, FMT_ENABLE_IF(std::is_same<T, std::byte>::value)> +inline auto format_as(T b) -> unsigned char { return static_cast<unsigned char>(b); } #endif @@ -394,7 +394,7 @@ FMT_CONSTEXPR inline auto is_utf8() -> bool { compiled with a different ``-std`` option than the client code (which is not recommended). */ -FMT_MODULE_EXPORT +FMT_EXPORT template <typename Char> class basic_string_view { private: const Char* data_; @@ -497,11 +497,11 @@ template <typename Char> class basic_string_view { } }; -FMT_MODULE_EXPORT +FMT_EXPORT using string_view = basic_string_view<char>; /** Specifies if ``T`` is a character type. Can be specialized by users. */ -FMT_MODULE_EXPORT +FMT_EXPORT template <typename T> struct is_char : std::false_type {}; template <> struct is_char<char> : std::true_type {}; @@ -639,6 +639,9 @@ struct error_handler { }; } // namespace detail +/** Throws ``format_error`` with a given message. */ +using detail::throw_format_error; + /** String's character type. */ template <typename S> using char_t = typename detail::char_t_impl<S>::type; @@ -649,7 +652,7 @@ template <typename S> using char_t = typename detail::char_t_impl<S>::type; You can use the ``format_parse_context`` type alias for ``char`` instead. \endrst */ -FMT_MODULE_EXPORT +FMT_EXPORT template <typename Char> class basic_format_parse_context { private: basic_string_view<Char> format_str_; @@ -715,7 +718,7 @@ template <typename Char> class basic_format_parse_context { FMT_CONSTEXPR void check_dynamic_spec(int arg_id); }; -FMT_MODULE_EXPORT +FMT_EXPORT using format_parse_context = basic_format_parse_context<char>; namespace detail { @@ -756,72 +759,6 @@ class compile_parse_context : public basic_format_parse_context<Char> { #endif } }; -} // namespace detail - -template <typename Char> -FMT_CONSTEXPR void basic_format_parse_context<Char>::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 || FMT_GCC_VERSION >= 1200)) { - using context = detail::compile_parse_context<Char>; - if (id >= static_cast<context*>(this)->num_args()) - detail::throw_format_error("argument not found"); - } -} - -template <typename Char> -FMT_CONSTEXPR void basic_format_parse_context<Char>::check_dynamic_spec( - int arg_id) { - if (detail::is_constant_evaluated() && - (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) { - using context = detail::compile_parse_context<Char>; - static_cast<context*>(this)->check_dynamic_spec(arg_id); - } -} - -FMT_MODULE_EXPORT template <typename Context> class basic_format_arg; -FMT_MODULE_EXPORT template <typename Context> class basic_format_args; -FMT_MODULE_EXPORT template <typename Context> class dynamic_format_arg_store; - -// A formatter for objects of type T. -FMT_MODULE_EXPORT -template <typename T, typename Char = char, typename Enable = void> -struct formatter { - // A deleted default constructor indicates a disabled formatter. - formatter() = delete; -}; - -// Specifies if T has an enabled formatter specialization. A type can be -// formattable even if it doesn't have a formatter e.g. via a conversion. -template <typename T, typename Context> -using has_formatter = - std::is_constructible<typename Context::template formatter_type<T>>; - -// Checks whether T is a container with contiguous storage. -template <typename T> struct is_contiguous : std::false_type {}; -template <typename Char> -struct is_contiguous<std::basic_string<Char>> : std::true_type {}; - -class appender; - -namespace detail { - -template <typename Context, typename T> -constexpr auto has_const_formatter_impl(T*) - -> decltype(typename Context::template formatter_type<T>().format( - std::declval<const T&>(), std::declval<Context&>()), - true) { - return true; -} -template <typename Context> -constexpr auto has_const_formatter_impl(...) -> bool { - return false; -} -template <typename T, typename Context> -constexpr auto has_const_formatter() -> bool { - return has_const_formatter_impl<Context>(static_cast<T*>(nullptr)); -} // Extracts a reference to the container from back_insert_iterator. template <typename Container> @@ -903,10 +840,8 @@ template <typename T> class buffer { /** Returns the capacity of this buffer. */ constexpr auto capacity() const noexcept -> size_t { return capacity_; } - /** Returns a pointer to the buffer data. */ + /** Returns a pointer to the buffer data (not null-terminated). */ FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; } - - /** Returns a pointer to the buffer data. */ FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; } /** Clears this buffer. */ @@ -1099,6 +1034,79 @@ template <typename T = char> class counting_buffer final : public buffer<T> { auto count() -> size_t { return count_ + this->size(); } }; +} // namespace detail + +template <typename Char> +FMT_CONSTEXPR void basic_format_parse_context<Char>::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 || FMT_GCC_VERSION >= 1200)) { + using context = detail::compile_parse_context<Char>; + if (id >= static_cast<context*>(this)->num_args()) + detail::throw_format_error("argument not found"); + } +} + +template <typename Char> +FMT_CONSTEXPR void basic_format_parse_context<Char>::check_dynamic_spec( + int arg_id) { + if (detail::is_constant_evaluated() && + (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) { + using context = detail::compile_parse_context<Char>; + static_cast<context*>(this)->check_dynamic_spec(arg_id); + } +} + +FMT_EXPORT template <typename Context> class basic_format_arg; +FMT_EXPORT template <typename Context> class basic_format_args; +FMT_EXPORT template <typename Context> class dynamic_format_arg_store; + +// A formatter for objects of type T. +FMT_EXPORT +template <typename T, typename Char = char, typename Enable = void> +struct formatter { + // A deleted default constructor indicates a disabled formatter. + formatter() = delete; +}; + +// Specifies if T has an enabled formatter specialization. A type can be +// formattable even if it doesn't have a formatter e.g. via a conversion. +template <typename T, typename Context> +using has_formatter = + std::is_constructible<typename Context::template formatter_type<T>>; + +// An output iterator that appends to a buffer. +// It is used to reduce symbol sizes for the common case. +class appender : public std::back_insert_iterator<detail::buffer<char>> { + using base = std::back_insert_iterator<detail::buffer<char>>; + + public: + using std::back_insert_iterator<detail::buffer<char>>::back_insert_iterator; + appender(base it) noexcept : base(it) {} + FMT_UNCHECKED_ITERATOR(appender); + + auto operator++() noexcept -> appender& { return *this; } + auto operator++(int) noexcept -> appender { return *this; } +}; + +namespace detail { + +template <typename Context, typename T> +constexpr auto has_const_formatter_impl(T*) + -> decltype(typename Context::template formatter_type<T>().format( + std::declval<const T&>(), std::declval<Context&>()), + true) { + return true; +} +template <typename Context> +constexpr auto has_const_formatter_impl(...) -> bool { + return false; +} +template <typename T, typename Context> +constexpr auto has_const_formatter() -> bool { + return has_const_formatter_impl<Context>(static_cast<T*>(nullptr)); +} template <typename T> using buffer_appender = conditional_t<std::is_same<T, char>::value, appender, @@ -1274,9 +1282,9 @@ template <typename Context> class value { FMT_INLINE value(const named_arg_info<char_type>* args, size_t size) : named_args{args, size} {} - template <typename T> FMT_CONSTEXPR FMT_INLINE value(T& val) { - using value_type = remove_cvref_t<T>; - custom.value = const_cast<value_type*>(&val); + template <typename T> FMT_CONSTEXPR20 FMT_INLINE value(T& val) { + using value_type = remove_const_t<T>; + custom.value = const_cast<value_type*>(std::addressof(val)); // Get the formatter type through the context to allow different contexts // have different extension points, e.g. `formatter<T>` for `format` and // `printf_formatter<T>` for `printf`. @@ -1301,9 +1309,6 @@ template <typename Context> class value { } }; -template <typename Context, typename T> -FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg<Context>; - // 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. enum { long_short = sizeof(long) == sizeof(int) }; @@ -1415,9 +1420,8 @@ template <typename Context> struct arg_mapper { FMT_ENABLE_IF( std::is_pointer<T>::value || std::is_member_pointer<T>::value || std::is_function<typename std::remove_pointer<T>::type>::value || - (std::is_convertible<const T&, const void*>::value && - !std::is_convertible<const T&, const char_type*>::value && - !has_formatter<T, Context>::value))> + (std::is_array<T>::value && + !std::is_convertible<T, const char_type*>::value))> FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer { return {}; } @@ -1435,30 +1439,28 @@ template <typename Context> struct arg_mapper { return map(format_as(val)); } - template <typename T, typename U = remove_cvref_t<T>> - struct formattable - : bool_constant<has_const_formatter<U, Context>() || - (has_formatter<U, Context>::value && - !std::is_const<remove_reference_t<T>>::value)> {}; + template <typename T, typename U = remove_const_t<T>> + struct formattable : bool_constant<has_const_formatter<U, Context>() || + (has_formatter<U, Context>::value && + !std::is_const<T>::value)> {}; template <typename T, FMT_ENABLE_IF(formattable<T>::value)> - FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& { + FMT_CONSTEXPR FMT_INLINE auto do_map(T& val) -> T& { return val; } template <typename T, FMT_ENABLE_IF(!formattable<T>::value)> - FMT_CONSTEXPR FMT_INLINE auto do_map(T&&) -> unformattable { + FMT_CONSTEXPR FMT_INLINE auto do_map(T&) -> unformattable { return {}; } - template <typename T, typename U = remove_cvref_t<T>, + template <typename T, typename U = remove_const_t<T>, FMT_ENABLE_IF((std::is_class<U>::value || std::is_enum<U>::value || std::is_union<U>::value) && !is_string<U>::value && !is_char<U>::value && !is_named_arg<U>::value && !std::is_arithmetic<format_as_t<U>>::value)> - FMT_CONSTEXPR FMT_INLINE auto map(T&& val) - -> decltype(this->do_map(std::forward<T>(val))) { - return do_map(std::forward<T>(val)); + FMT_CONSTEXPR FMT_INLINE auto map(T& val) -> decltype(this->do_map(val)) { + return do_map(val); } template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)> @@ -1481,22 +1483,121 @@ enum { packed_arg_bits = 4 }; enum { max_packed_args = 62 / packed_arg_bits }; enum : unsigned long long { is_unpacked_bit = 1ULL << 63 }; enum : unsigned long long { has_named_args_bit = 1ULL << 62 }; -} // namespace detail -// An output iterator that appends to a buffer. -// It is used to reduce symbol sizes for the common case. -class appender : public std::back_insert_iterator<detail::buffer<char>> { - using base = std::back_insert_iterator<detail::buffer<char>>; +template <typename Char, typename InputIt> +auto copy_str(InputIt begin, InputIt end, appender out) -> appender { + get_container(out).append(begin, end); + return out; +} +template <typename Char, typename InputIt> +auto copy_str(InputIt begin, InputIt end, + std::back_insert_iterator<std::string> out) + -> std::back_insert_iterator<std::string> { + get_container(out).append(begin, end); + return out; +} + +template <typename Char, typename R, typename OutputIt> +FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt { + return detail::copy_str<Char>(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 <typename...> struct void_t_impl { using type = void; }; +template <typename... T> using void_t = typename void_t_impl<T...>::type; +#else +template <typename...> using void_t = void; +#endif + +template <typename It, typename T, typename Enable = void> +struct is_output_iterator : std::false_type {}; + +template <typename It, typename T> +struct is_output_iterator< + It, T, + void_t<typename std::iterator_traits<It>::iterator_category, + decltype(*std::declval<It>() = std::declval<T>())>> + : std::true_type {}; + +template <typename It> struct is_back_insert_iterator : std::false_type {}; +template <typename Container> +struct is_back_insert_iterator<std::back_insert_iterator<Container>> + : std::true_type {}; + +// A type-erased reference to an std::locale to avoid a heavy <locale> include. +class locale_ref { + private: + const void* locale_; // A type-erased pointer to std::locale. public: - using std::back_insert_iterator<detail::buffer<char>>::back_insert_iterator; - appender(base it) noexcept : base(it) {} - FMT_UNCHECKED_ITERATOR(appender); + constexpr FMT_INLINE locale_ref() : locale_(nullptr) {} + template <typename Locale> explicit locale_ref(const Locale& loc); - auto operator++() noexcept -> appender& { return *this; } - auto operator++(int) noexcept -> appender { return *this; } + explicit operator bool() const noexcept { return locale_ != nullptr; } + + template <typename Locale> auto get() const -> Locale; }; +template <typename> constexpr auto encode_types() -> unsigned long long { + return 0; +} + +template <typename Context, typename Arg, typename... Args> +constexpr auto encode_types() -> unsigned long long { + return static_cast<unsigned>(mapped_type_constant<Arg, Context>::value) | + (encode_types<Context, Args...>() << packed_arg_bits); +} + +#if defined(__cpp_if_constexpr) +// This type is intentionally undefined, only used for errors +template <typename T, typename Char> struct type_is_unformattable_for; +#endif + +template <bool PACKED, typename Context, typename T, FMT_ENABLE_IF(PACKED)> +FMT_CONSTEXPR FMT_INLINE auto make_arg(T& val) -> value<Context> { + using arg_type = remove_cvref_t<decltype(arg_mapper<Context>().map(val))>; + + constexpr bool formattable_char = + !std::is_same<arg_type, unformattable_char>::value; + static_assert(formattable_char, "Mixing character types is disallowed."); + + // Formatting of arbitrary pointers is disallowed. If you want to format a + // pointer cast it to `void*` or `const void*`. In particular, this forbids + // formatting of `[const] volatile char*` printed as bool by iostreams. + constexpr bool formattable_pointer = + !std::is_same<arg_type, unformattable_pointer>::value; + static_assert(formattable_pointer, + "Formatting of non-void pointers is disallowed."); + + constexpr bool formattable = !std::is_same<arg_type, unformattable>::value; +#if defined(__cpp_if_constexpr) + if constexpr (!formattable) { + type_is_unformattable_for<T, typename Context::char_type> _; + } +#endif + static_assert( + formattable, + "Cannot format an argument. To make type T formattable provide a " + "formatter<T> specialization: https://fmt.dev/latest/api.html#udt"); + return {arg_mapper<Context>().map(val)}; +} + +template <typename Context, typename T> +FMT_CONSTEXPR auto make_arg(T& val) -> basic_format_arg<Context> { + auto arg = basic_format_arg<Context>(); + arg.type_ = mapped_type_constant<T, Context>::value; + arg.value_ = make_arg<true, Context>(val); + return arg; +} + +template <bool PACKED, typename Context, typename T, FMT_ENABLE_IF(!PACKED)> +FMT_CONSTEXPR inline auto make_arg(T& val) -> basic_format_arg<Context> { + return make_arg<Context>(val); +} +} // namespace detail +FMT_BEGIN_EXPORT + // A formatting argument. It is a trivially copyable/constructible type to // allow storage in basic_memory_buffer. template <typename Context> class basic_format_arg { @@ -1505,7 +1606,7 @@ template <typename Context> class basic_format_arg { detail::type type_; template <typename ContextType, typename T> - friend FMT_CONSTEXPR auto detail::make_arg(T&& value) + friend FMT_CONSTEXPR auto detail::make_arg(T& value) -> basic_format_arg<ContextType>; template <typename Visitor, typename Ctx> @@ -1559,7 +1660,7 @@ template <typename Context> class basic_format_arg { ``vis(value)`` will be called with the value of type ``double``. \endrst */ -FMT_MODULE_EXPORT +// DEPRECATED! template <typename Visitor, typename Context> FMT_CONSTEXPR FMT_INLINE auto visit_format_arg( Visitor&& vis, const basic_format_arg<Context>& arg) -> decltype(vis(0)) { @@ -1601,124 +1702,6 @@ FMT_CONSTEXPR FMT_INLINE auto visit_format_arg( return vis(monostate()); } -namespace detail { - -template <typename Char, typename InputIt> -auto copy_str(InputIt begin, InputIt end, appender out) -> appender { - get_container(out).append(begin, end); - return out; -} - -template <typename Char, typename R, typename OutputIt> -FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt { - return detail::copy_str<Char>(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 <typename...> struct void_t_impl { using type = void; }; -template <typename... T> using void_t = typename void_t_impl<T...>::type; -#else -template <typename...> using void_t = void; -#endif - -template <typename It, typename T, typename Enable = void> -struct is_output_iterator : std::false_type {}; - -template <typename It, typename T> -struct is_output_iterator< - It, T, - void_t<typename std::iterator_traits<It>::iterator_category, - decltype(*std::declval<It>() = std::declval<T>())>> - : std::true_type {}; - -template <typename It> struct is_back_insert_iterator : std::false_type {}; -template <typename Container> -struct is_back_insert_iterator<std::back_insert_iterator<Container>> - : std::true_type {}; - -template <typename It> -struct is_contiguous_back_insert_iterator : std::false_type {}; -template <typename Container> -struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>> - : is_contiguous<Container> {}; -template <> -struct is_contiguous_back_insert_iterator<appender> : std::true_type {}; - -// A type-erased reference to an std::locale to avoid a heavy <locale> include. -class locale_ref { - private: - const void* locale_; // A type-erased pointer to std::locale. - - public: - constexpr FMT_INLINE locale_ref() : locale_(nullptr) {} - template <typename Locale> explicit locale_ref(const Locale& loc); - - explicit operator bool() const noexcept { return locale_ != nullptr; } - - template <typename Locale> auto get() const -> Locale; -}; - -template <typename> constexpr auto encode_types() -> unsigned long long { - return 0; -} - -template <typename Context, typename Arg, typename... Args> -constexpr auto encode_types() -> unsigned long long { - return static_cast<unsigned>(mapped_type_constant<Arg, Context>::value) | - (encode_types<Context, Args...>() << packed_arg_bits); -} - -template <typename Context, typename T> -FMT_CONSTEXPR FMT_INLINE auto make_value(T&& val) -> value<Context> { - auto&& arg = arg_mapper<Context>().map(FMT_FORWARD(val)); - using arg_type = remove_cvref_t<decltype(arg)>; - - constexpr bool formattable_char = - !std::is_same<arg_type, unformattable_char>::value; - static_assert(formattable_char, "Mixing character types is disallowed."); - - // Formatting of arbitrary pointers is disallowed. If you want to format a - // pointer cast it to `void*` or `const void*`. In particular, this forbids - // formatting of `[const] volatile char*` printed as bool by iostreams. - constexpr bool formattable_pointer = - !std::is_same<arg_type, unformattable_pointer>::value; - static_assert(formattable_pointer, - "Formatting of non-void pointers is disallowed."); - - constexpr bool formattable = !std::is_same<arg_type, unformattable>::value; - static_assert( - formattable, - "Cannot format an argument. To make type T formattable provide a " - "formatter<T> specialization: https://fmt.dev/latest/api.html#udt"); - return {arg}; -} - -template <typename Context, typename T> -FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg<Context> { - auto arg = basic_format_arg<Context>(); - arg.type_ = mapped_type_constant<T, Context>::value; - arg.value_ = make_value<Context>(value); - return arg; -} - -// The DEPRECATED 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 <bool IS_PACKED, typename Context, type, typename T, - FMT_ENABLE_IF(IS_PACKED)> -FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value<Context> { - return make_value<Context>(val); -} - -template <bool IS_PACKED, typename Context, type, typename T, - FMT_ENABLE_IF(!IS_PACKED)> -FMT_CONSTEXPR inline auto make_arg(T&& value) -> basic_format_arg<Context> { - return make_arg<Context>(value); -} -} // namespace detail -FMT_BEGIN_EXPORT - // Formatting context. template <typename OutputIt, typename Char> class basic_format_context { private: @@ -1778,7 +1761,7 @@ using format_context = buffer_context<char>; template <typename T, typename Char = char> using is_formattable = bool_constant<!std::is_base_of< detail::unformattable, decltype(detail::arg_mapper<buffer_context<Char>>() - .map(std::declval<T>()))>::value>; + .map(std::declval<T&>()))>::value>; /** \rst @@ -1796,7 +1779,7 @@ class format_arg_store { private: static const size_t num_args = sizeof...(Args); - static const size_t num_named_args = detail::count_named_args<Args...>(); + static constexpr size_t num_named_args = detail::count_named_args<Args...>(); static const bool is_packed = num_args <= detail::max_packed_args; using value_type = conditional_t<is_packed, detail::value<Context>, @@ -1817,16 +1800,14 @@ class format_arg_store public: template <typename... T> - FMT_CONSTEXPR FMT_INLINE format_arg_store(T&&... args) + FMT_CONSTEXPR FMT_INLINE format_arg_store(T&... args) : #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 basic_format_args<Context>(*this), #endif - data_{detail::make_arg< - is_packed, Context, - detail::mapped_type_constant<remove_cvref_t<T>, Context>::value>( - FMT_FORWARD(args))...} { - detail::init_named_args(data_.named_args(), 0, 0, args...); + data_{detail::make_arg<is_packed, Context>(args)...} { + if (detail::const_check(num_named_args != 0)) + detail::init_named_args(data_.named_args(), 0, 0, args...); } }; @@ -1834,14 +1815,15 @@ class format_arg_store \rst Constructs a `~fmt::format_arg_store` object that contains references to arguments and can be implicitly converted to `~fmt::format_args`. `Context` - can be omitted in which case it defaults to `~fmt::context`. + can be omitted in which case it defaults to `~fmt::format_context`. See `~fmt::arg` for lifetime considerations. \endrst */ +// Arguments are taken by lvalue references to avoid some lifetime issues. template <typename Context = format_context, typename... T> -constexpr auto make_format_args(T&&... args) +constexpr auto make_format_args(T&... args) -> format_arg_store<Context, remove_cvref_t<T>...> { - return {FMT_FORWARD(args)...}; + return {args...}; } /** @@ -1869,7 +1851,7 @@ FMT_END_EXPORT ``vformat``:: void vlog(string_view format_str, format_args args); // OK - format_args args = make_format_args(42); // Error: dangling reference + format_args args = make_format_args(); // Error: dangling reference \endrst */ template <typename Context> class basic_format_args { @@ -1986,7 +1968,7 @@ template <typename Context> class basic_format_args { /** An alias to ``basic_format_args<format_context>``. */ // A separate type would result in shorter symbols but break ABI compatibility // between clang and gcc on ARM (#1919). -FMT_MODULE_EXPORT using format_args = basic_format_args<format_context>; +FMT_EXPORT using format_args = basic_format_args<format_context>; // 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). @@ -2558,7 +2540,17 @@ FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx) mapped_type_constant<T, context>::value != type::custom_type, decltype(arg_mapper<context>().map(std::declval<const T&>())), typename strip_named_arg<T>::type>; +#if defined(__cpp_if_constexpr) + if constexpr (std::is_default_constructible_v< + formatter<mapped_type, char_type>>) { + return formatter<mapped_type, char_type>().parse(ctx); + } else { + type_is_unformattable_for<T, char_type> _; + return ctx.begin(); + } +#else return formatter<mapped_type, char_type>().parse(ctx); +#endif } // Checks char specs and returns true iff the presentation type is char-like. @@ -2574,8 +2566,6 @@ FMT_CONSTEXPR auto check_char_specs(const format_specs<Char>& specs) -> bool { return true; } -constexpr FMT_INLINE_VARIABLE int invalid_arg_index = -1; - #if FMT_USE_NONTYPE_TEMPLATE_ARGS template <int N, typename T, typename... Args, typename Char> constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int { @@ -2585,7 +2575,7 @@ constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int { if constexpr (sizeof...(Args) > 0) return get_arg_index_by_name<N + 1, Args...>(name); (void)name; // Workaround an MSVC bug about "unused" parameter. - return invalid_arg_index; + return -1; } #endif @@ -2596,7 +2586,7 @@ FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int { return get_arg_index_by_name<0, Args...>(name); #endif (void)name; - return invalid_arg_index; + return -1; } template <typename Char, typename... Args> class format_string_checker { @@ -2610,15 +2600,15 @@ template <typename Char, typename... Args> class format_string_checker { // needed for compile-time checks: https://godbolt.org/z/GvWzcTjh1. using parse_func = const Char* (*)(parse_context_type&); + type types_[num_args > 0 ? static_cast<size_t>(num_args) : 1]; parse_context_type context_; parse_func parse_funcs_[num_args > 0 ? static_cast<size_t>(num_args) : 1]; - type types_[num_args > 0 ? static_cast<size_t>(num_args) : 1]; public: explicit FMT_CONSTEXPR format_string_checker(basic_string_view<Char> fmt) - : context_(fmt, num_args, types_), - parse_funcs_{&parse_format_specs<Args, parse_context_type>...}, - types_{mapped_type_constant<Args, buffer_context<Char>>::value...} {} + : types_{mapped_type_constant<Args, buffer_context<Char>>::value...}, + context_(fmt, num_args, types_), + parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {} FMT_CONSTEXPR void on_text(const Char*, const Char*) {} @@ -2629,7 +2619,7 @@ template <typename Char, typename... Args> class format_string_checker { FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int { #if FMT_USE_NONTYPE_TEMPLATE_ARGS auto index = get_arg_index_by_name<Args...>(id); - if (index == invalid_arg_index) on_error("named argument is not found"); + if (index < 0) on_error("named argument is not found"); return index; #else (void)id; @@ -2638,7 +2628,9 @@ template <typename Char, typename... Args> class format_string_checker { #endif } - FMT_CONSTEXPR void on_replacement_field(int, const Char*) {} + FMT_CONSTEXPR void on_replacement_field(int id, const Char* begin) { + on_format_specs(id, begin, begin); // Call parse() on empty specs. + } FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char*) -> const Char* { @@ -2721,27 +2713,6 @@ struct formatter<T, Char, -> decltype(ctx.out()); }; -#define FMT_FORMAT_AS(Type, Base) \ - template <typename Char> \ - struct formatter<Type, Char> : formatter<Base, Char> { \ - template <typename FormatContext> \ - auto format(const Type& val, FormatContext& ctx) const \ - -> decltype(ctx.out()) { \ - return formatter<Base, Char>::format(static_cast<Base>(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<Char>, basic_string_view<Char>); -FMT_FORMAT_AS(std::nullptr_t, const void*); -FMT_FORMAT_AS(detail::std_string_view<Char>, basic_string_view<Char>); - template <typename Char = char> struct runtime_format_string { basic_string_view<Char> str; }; |