diff --git a/include/fmt/args.h b/include/fmt/args.h index 7a0f3e07..a24fe3f1 100644 --- a/include/fmt/args.h +++ b/include/fmt/args.h @@ -112,13 +112,6 @@ class dynamic_format_arg_store friend class basic_format_args; - auto get_types() const -> unsigned long long { - return detail::is_unpacked_bit | data_.size() | - (named_info_.empty() - ? 0ULL - : static_cast(detail::has_named_args_bit)); - } - auto data() const -> const basic_format_arg* { return named_info_.empty() ? data_.data() : data_.data() + 1; } @@ -140,13 +133,18 @@ class dynamic_format_arg_store std::unique_ptr>, decltype(pop_one)> guard{&data_, pop_one}; named_info_.push_back({arg.name, static_cast(data_.size() - 2u)}); - data_[0].value_.named_args = {named_info_.data(), named_info_.size()}; + data_[0] = {named_info_.data(), named_info_.size()}; guard.release(); } public: constexpr dynamic_format_arg_store() = default; + operator basic_format_args() const { + return basic_format_args(data(), static_cast(data_.size()), + !named_info_.empty()); + } + /** * Adds an argument into the dynamic store for later passing to a formatting * function. @@ -217,7 +215,7 @@ class dynamic_format_arg_store /// `new_cap_named` named arguments. void reserve(size_t new_cap, size_t new_cap_named) { FMT_ASSERT(new_cap >= new_cap_named, - "Set of arguments includes set of named arguments"); + "set of arguments includes set of named arguments"); data_.reserve(new_cap); named_info_.reserve(new_cap_named); } diff --git a/include/fmt/base.h b/include/fmt/base.h index 50b6c97b..fac45a07 100644 --- a/include/fmt/base.h +++ b/include/fmt/base.h @@ -216,20 +216,6 @@ # define FMT_DEPRECATED /* deprecated */ #endif -#ifdef FMT_NO_UNIQUE_ADDRESS -// Use the provided definition. -#elif FMT_CPLUSPLUS < 202002L -// Not supported. -#elif FMT_HAS_CPP_ATTRIBUTE(no_unique_address) -# define FMT_NO_UNIQUE_ADDRESS [[no_unique_address]] -// VS2019 v16.10 and later except clang-cl (https://reviews.llvm.org/D110485). -#elif FMT_MSC_VERSION >= 1929 && !FMT_CLANG_VERSION -# define FMT_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] -#endif -#ifndef FMT_NO_UNIQUE_ADDRESS -# define FMT_NO_UNIQUE_ADDRESS -#endif - #ifdef FMT_ALWAYS_INLINE // Use the provided definition. #elif FMT_GCC_VERSION || FMT_CLANG_VERSION @@ -652,7 +638,6 @@ using buffered_context = template class basic_format_arg; template class basic_format_args; -template class dynamic_format_arg_store; // A separate type would result in shorter symbols but break ABI compatibility // between clang and gcc on ARM (#1919). @@ -790,18 +775,22 @@ enum { struct view {}; -template struct named_arg : view { - const Char* name; - const T& value; - named_arg(const Char* n, const T& v) : name(n), value(v) {} -}; - +template struct named_arg; template struct is_named_arg : std::false_type {}; template struct is_static_named_arg : std::false_type {}; template struct is_named_arg> : std::true_type {}; +template struct named_arg : view { + const Char* name; + const T& value; + + named_arg(const Char* n, const T& v) : name(n), value(v) {} + + static_assert(!is_named_arg::value, "nested named arguments"); +}; + template auto unwrap_named_arg(const named_arg& arg) -> const T& { return arg.value; @@ -2407,7 +2396,7 @@ FMT_CONSTEXPR auto make_arg(T& val) -> value { enum { formattable_char = !std::is_same::value }; - static_assert(formattable_char, "Mixing character types is disallowed."); + 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 @@ -2416,7 +2405,7 @@ FMT_CONSTEXPR auto make_arg(T& val) -> value { formattable_pointer = !std::is_same::value }; static_assert(formattable_pointer, - "Formatting of non-void pointers is disallowed."); + "formatting of non-void pointers is disallowed"); enum { formattable = !std::is_same::value }; #if defined(__cpp_if_constexpr) @@ -2426,7 +2415,7 @@ FMT_CONSTEXPR auto make_arg(T& val) -> value { #endif static_assert( formattable, - "Cannot format an argument. To make type T formattable provide a " + "cannot format an argument; to make type T formattable provide a " "formatter specialization: https://fmt.dev/latest/api.html#udt"); return {arg_mapper::map(val)}; } @@ -2562,22 +2551,12 @@ template class basic_format_arg { detail::value value_; detail::type type_; - template - friend FMT_CONSTEXPR auto detail::make_arg(T& value) - -> basic_format_arg; - + template + friend FMT_CONSTEXPR auto detail::make_arg(T& value) -> basic_format_arg; friend class basic_format_args; - friend class dynamic_format_arg_store; - friend class loc_value; using char_type = typename Context::char_type; - template - friend struct detail::format_arg_store; - - basic_format_arg(const detail::named_arg_info* args, size_t size) - : value_(args, size) {} - public: class handle { private: @@ -2592,15 +2571,14 @@ template class basic_format_arg { }; constexpr basic_format_arg() : type_(detail::type::none_type) {} + basic_format_arg(const detail::named_arg_info* args, size_t size) + : value_(args, size) {} constexpr explicit operator bool() const noexcept { return type_ != detail::type::none_type; } - auto type() const -> detail::type { return type_; } - auto is_integral() const -> bool { return detail::is_integral_type(type_); } - /** * Visits an argument dispatching to the appropriate visit method based on * the argument type. For example, if the argument type is `double` then @@ -2714,18 +2692,16 @@ template class basic_format_args { store) : desc_(DESC), args_(store.args + (NUM_NAMED_ARGS != 0 ? 1 : 0)) {} - /// Constructs a `basic_format_args` object from `dynamic_format_arg_store`. - constexpr basic_format_args(const dynamic_format_arg_store& store) - : desc_(store.get_types()), args_(store.data()) {} - /// Constructs a `basic_format_args` object from a dynamic list of arguments. - constexpr basic_format_args(const format_arg* args, int count) - : desc_(detail::is_unpacked_bit | detail::to_unsigned(count)), + constexpr basic_format_args(const format_arg* args, int count, + bool has_named = false) + : desc_(detail::is_unpacked_bit | detail::to_unsigned(count) | + (has_named ? +detail::has_named_args_bit : 0ULL)), args_(args) {} /// Returns the argument with the specified id. FMT_CONSTEXPR auto get(int id) const -> format_arg { - format_arg arg; + auto arg = format_arg(); if (!is_packed()) { if (id < max_size()) arg = args_[id]; return arg; @@ -2761,11 +2737,10 @@ template class basic_format_args { }; // A formatting context. -class context { +class context : private detail::locale_ref { private: appender out_; format_args args_; - FMT_NO_UNIQUE_ADDRESS detail::locale_ref loc_; public: /// The character type for the output. @@ -2780,7 +2755,7 @@ class context { /// Constructs a `context` object. References to the arguments are stored /// in the object so make sure they have appropriate lifetimes. FMT_CONSTEXPR context(iterator out, format_args a, detail::locale_ref l = {}) - : out_(out), args_(a), loc_(l) {} + : locale_ref(l), out_(out), args_(a) {} context(context&&) = default; context(const context&) = delete; void operator=(const context&) = delete; @@ -2797,7 +2772,7 @@ class context { // Advances the begin iterator to `it`. void advance_to(iterator) {} - FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; } + FMT_CONSTEXPR auto locale() -> detail::locale_ref { return *this; } }; template struct runtime_format_string { @@ -2844,7 +2819,7 @@ template class basic_format_string { static_assert( FMT_USE_CONSTEVAL && sizeof(S) != 0, "FMT_ENFORCE_COMPILE_STRING requires all format strings to use " - "FMT_STRING."); + "FMT_STRING"); #endif } template inline auto arg(const Char* name, const T& arg) -> detail::named_arg { - static_assert(!detail::is_named_arg(), "nested named arguments"); return {name, arg}; } diff --git a/include/fmt/format.h b/include/fmt/format.h index 471922f3..f24b14df 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -124,6 +124,20 @@ FMT_END_NAMESPACE # endif #endif +#ifdef FMT_NO_UNIQUE_ADDRESS +// Use the provided definition. +#elif FMT_CPLUSPLUS < 202002L +// Not supported. +#elif FMT_HAS_CPP_ATTRIBUTE(no_unique_address) +# define FMT_NO_UNIQUE_ADDRESS [[no_unique_address]] +// VS2019 v16.10 and later except clang-cl (https://reviews.llvm.org/D110485). +#elif FMT_MSC_VERSION >= 1929 && !FMT_CLANG_VERSION +# define FMT_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] +#endif +#ifndef FMT_NO_UNIQUE_ADDRESS +# define FMT_NO_UNIQUE_ADDRESS +#endif + #ifndef FMT_MAYBE_UNUSED # if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused) # define FMT_MAYBE_UNUSED [[maybe_unused]] @@ -1077,14 +1091,11 @@ template class generic_context { class loc_value { private: - basic_format_arg value_; + basic_format_arg value_; public: template ::value)> - loc_value(T value) { - value_.type_ = detail::mapped_type_constant::value; - value_.value_ = detail::arg_mapper::map(value); - } + loc_value(T value) : value_(detail::make_arg(value)) {} template ::value)> loc_value(T) {} diff --git a/include/fmt/printf.h b/include/fmt/printf.h index 4cd5411b..7344416f 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -491,7 +491,7 @@ void vprintf(buffer& buf, basic_string_view format, auto arg = get_arg(arg_index); // For d, i, o, u, x, and X conversion specifiers, if a precision is // specified, the '0' flag is ignored - if (specs.precision >= 0 && arg.is_integral()) { + if (specs.precision >= 0 && is_integral_type(arg.type())) { // Ignore '0' for non-numeric types or if '-' present. specs.set_fill(' '); } @@ -556,7 +556,7 @@ void vprintf(buffer& buf, basic_string_view format, // Parse type. if (it == end) report_error("invalid format string"); char type = static_cast(*it++); - if (arg.is_integral()) { + if (is_integral_type(arg.type())) { // Normalize type. switch (type) { case 'i':