[libc++][format] Improve format-arg-store.
authorMark de Wever <koraq@xs4all.nl>
Sun, 3 Oct 2021 11:11:53 +0000 (13:11 +0200)
committerMark de Wever <koraq@xs4all.nl>
Wed, 18 May 2022 18:11:36 +0000 (20:11 +0200)
This optimizes the __format_arg_store type to allow a more efficient
storage of the basic_format_args.

It stores the data in two arrays:
- A struct with the tag of the exposition only variant's type and the
  offset of the element in the data array. Since this array only depends
  on the type information it's calculated at compile time and can be
  shared by different instances of this class.
- The arguments converted to the types used in the exposition only
  variant of basic_format_arg. This means the packed data can be
  directly copied to an element of this variant.

The new code uses rvalue reference arguments in preparation for P2418.
The handle class also has some changes to prepare for P2418. The real
changed for P2418 will be done separately, but these parts make it
easier to implement that paper.

Some parts of existing test code are removed since they were no longer
valid after the changes, but new tests have been added.

Implements parts of:
- P2418 Add support for std::generator-like types to std::format

Completes:
- LWG3473 Normative encouragement in non-normative note

Depends on D121138

Reviewed By: #libc, vitaut, Mordante

Differential Revision: https://reviews.llvm.org/D121514

25 files changed:
libcxx/docs/Status/Cxx20Papers.csv
libcxx/docs/Status/Cxx2bIssues.csv
libcxx/include/CMakeLists.txt
libcxx/include/__format/format_arg.h
libcxx/include/__format/format_arg_store.h [new file with mode: 0644]
libcxx/include/__format/format_args.h
libcxx/include/__format/format_fwd.h
libcxx/include/format
libcxx/include/module.modulemap
libcxx/test/libcxx/private_headers.verify.cpp
libcxx/test/libcxx/utilities/format/format.arguments/format.arg/visit_format_arg.pass.cpp
libcxx/test/std/utilities/format/format.arguments/format.arg.store/class.pass.cpp [deleted file]
libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.pass.cpp
libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_wformat_args.pass.cpp
libcxx/test/std/utilities/format/format.arguments/format.arg/operator_bool.pass.cpp
libcxx/test/std/utilities/format/format.formatter/format.context/format.context/arg.pass.cpp
libcxx/test/std/utilities/format/format.formatter/format.context/format.context/ctor.pass.cpp
libcxx/test/std/utilities/format/format.formatter/format.context/format.context/locale.pass.cpp
libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.bool.pass.cpp
libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.c_string.pass.cpp
libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.floating_point.pass.cpp
libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.handle.pass.cpp
libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.pointer.pass.cpp
libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.string.pass.cpp
libcxx/test/std/utilities/format/format.functions/format_tests.h

index a548445..b887d3d 100644 (file)
 "","","","","",""
 "`P2372R3 <https://wg21.link/P2372R3>`__","LWG","Fixing locale handling in chrono formatters","October 2021","",""
 "`P2415R2 <https://wg21.link/P2415R2>`__","LWG","What is a ``view``","October 2021","|Complete|","14.0"
-"`P2418R2 <https://wg21.link/P2418R2>`__","LWG","Add support for ``std::generator``-like types to ``std::format``","October 2021","",""
+"`P2418R2 <https://wg21.link/P2418R2>`__","LWG","Add support for ``std::generator``-like types to ``std::format``","October 2021","|Partial|",""
 "`P2432R1 <https://wg21.link/P2432R1>`__","LWG","Fix ``istream_view``","October 2021","",""
index 0391f64..93fa19d 100644 (file)
@@ -43,7 +43,7 @@
 "`3466 <https://wg21.link/LWG3466>`__","Specify the requirements for ``promise``/``future``/``shared_future`` consistently","November 2020","|Nothing To Do|",""
 "`3467 <https://wg21.link/LWG3467>`__","``bool`` can't be an integer-like type","November 2020","|Complete|","14.0"
 "`3472 <https://wg21.link/LWG3472>`__","``counted_iterator`` is missing preconditions","November 2020","|Complete|","14.0","|ranges|"
-"`3473 <https://wg21.link/LWG3473>`__","Normative encouragement in non-normative note","November 2020","|Nothing To Do|","","|format|"
+"`3473 <https://wg21.link/LWG3473>`__","Normative encouragement in non-normative note","November 2020","|Complete|","15.0","|format|"
 "`3474 <https://wg21.link/LWG3474>`__","Nesting ``join_views`` is broken because of CTAD","November 2020","|Complete|","15.0","|ranges|"
 "`3476 <https://wg21.link/LWG3476>`__","``thread`` and ``jthread`` constructors require that the parameters be move-constructible but never move construct the parameters","November 2020","",""
 "`3477 <https://wg21.link/LWG3477>`__","Simplify constraints for ``semiregular-box``","November 2020","","","|ranges|"
index 2da8b98..d7aa09e 100644 (file)
@@ -212,6 +212,7 @@ set(files
   __format/concepts.h
   __format/enable_insertable.h
   __format/format_arg.h
+  __format/format_arg_store.h
   __format/format_args.h
   __format/format_context.h
   __format/format_error.h
index 45356b2..3c61446 100644 (file)
@@ -35,6 +35,14 @@ namespace __format {
 ///
 /// @note The 128-bit types are unconditionally in the list to avoid the values
 /// of the enums to depend on the availability of 128-bit integers.
+///
+/// @note The value is stored as a 5-bit value in the __packed_arg_t_bits. This
+/// limits the maximum number of elements to 32.
+/// When modifying update the test
+/// test/libcxx/utilities/format/format.arguments/format.arg/arg_t.compile.pass.cpp
+/// It could be packed in 4-bits but that means a new type directly becomes an
+/// ABI break. The packed type is 64-bit so this reduces the maximum number of
+/// packed elements from 16 to 12.
 enum class _LIBCPP_ENUM_VIS __arg_t : uint8_t {
   __none,
   __boolean,
@@ -53,58 +61,151 @@ enum class _LIBCPP_ENUM_VIS __arg_t : uint8_t {
   __ptr,
   __handle
 };
+
+inline constexpr unsigned __packed_arg_t_bits = 5;
+inline constexpr uint8_t __packed_arg_t_mask = 0x1f;
+
+inline constexpr unsigned __packed_types_storage_bits = 64;
+inline constexpr unsigned __packed_types_max = __packed_types_storage_bits / __packed_arg_t_bits;
+
+_LIBCPP_HIDE_FROM_ABI
+constexpr bool __use_packed_format_arg_store(size_t __size) { return __size <= __packed_types_max; }
+
+_LIBCPP_HIDE_FROM_ABI
+constexpr __arg_t __get_packed_type(uint64_t __types, size_t __id) {
+  _LIBCPP_ASSERT(__id <= __packed_types_max, "");
+
+  if (__id > 0)
+    __types >>= __id * __packed_arg_t_bits;
+
+  return static_cast<__format::__arg_t>(__types & __packed_arg_t_mask);
+}
+
 } // namespace __format
 
 template <class _Visitor, class _Context>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT decltype(auto)
-visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT decltype(auto) visit_format_arg(_Visitor&& __vis,
+                                                                                  basic_format_arg<_Context> __arg) {
   switch (__arg.__type_) {
   case __format::__arg_t::__none:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), monostate{});
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__monostate_);
   case __format::__arg_t::__boolean:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__boolean);
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__boolean_);
   case __format::__arg_t::__char_type:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__char_type);
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__char_type_);
   case __format::__arg_t::__int:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__int);
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__int_);
   case __format::__arg_t::__long_long:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__long_long);
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__long_long_);
   case __format::__arg_t::__i128:
-#ifndef _LIBCPP_HAS_NO_INT128
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__i128);
-#else
+#  ifndef _LIBCPP_HAS_NO_INT128
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__i128_);
+#  else
     __libcpp_unreachable();
-#endif
+#  endif
   case __format::__arg_t::__unsigned:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__unsigned);
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__unsigned_);
   case __format::__arg_t::__unsigned_long_long:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis),
-                         __arg.__unsigned_long_long);
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__unsigned_long_long_);
   case __format::__arg_t::__u128:
-#ifndef _LIBCPP_HAS_NO_INT128
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__u128);
-#else
-   __libcpp_unreachable();
-#endif
+#  ifndef _LIBCPP_HAS_NO_INT128
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__u128_);
+#  else
+    __libcpp_unreachable();
+#  endif
   case __format::__arg_t::__float:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__float);
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__float_);
   case __format::__arg_t::__double:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__double);
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__double_);
   case __format::__arg_t::__long_double:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__long_double);
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__long_double_);
   case __format::__arg_t::__const_char_type_ptr:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis),
-                         __arg.__const_char_type_ptr);
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__const_char_type_ptr_);
   case __format::__arg_t::__string_view:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__string_view);
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__string_view_);
   case __format::__arg_t::__ptr:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__ptr);
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__value_.__ptr_);
   case __format::__arg_t::__handle:
-    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__handle);
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis),
+                         typename basic_format_arg<_Context>::handle{__arg.__value_.__handle_});
   }
+
   __libcpp_unreachable();
 }
 
+/// Contains the values used in basic_format_arg.
+///
+/// This is a separate type so it's possible to store the values and types in
+/// separate arrays.
+template <class _Context>
+class __basic_format_arg_value {
+  using _CharT = typename _Context::char_type;
+
+public:
+  /// Contains the implementation for basic_format_arg::handle.
+  struct __handle {
+    template <class _Tp>
+    _LIBCPP_HIDE_FROM_ABI explicit __handle(const _Tp& __v) noexcept
+        : __ptr_(_VSTD::addressof(__v)),
+          __format_([](basic_format_parse_context<_CharT>& __parse_ctx, _Context& __ctx, const void* __ptr) {
+            using _Formatter = typename _Context::template formatter_type<_Tp>;
+            using _Qp = conditional_t<requires { _Formatter().format(declval<const _Tp&>(), declval<_Context&>()); },
+                                      const _Tp, _Tp>;
+            _Formatter __f;
+            __parse_ctx.advance_to(__f.parse(__parse_ctx));
+            __ctx.advance_to(__f.format(*const_cast<_Qp*>(static_cast<const _Tp*>(__ptr)), __ctx));
+          }) {}
+
+    const void* __ptr_;
+    void (*__format_)(basic_format_parse_context<_CharT>&, _Context&, const void*);
+  };
+
+  union {
+    monostate __monostate_;
+    bool __boolean_;
+    _CharT __char_type_;
+    int __int_;
+    unsigned __unsigned_;
+    long long __long_long_;
+    unsigned long long __unsigned_long_long_;
+#  ifndef _LIBCPP_HAS_NO_INT128
+    __int128_t __i128_;
+    __uint128_t __u128_;
+#  endif
+    float __float_;
+    double __double_;
+    long double __long_double_;
+    const _CharT* __const_char_type_ptr_;
+    basic_string_view<_CharT> __string_view_;
+    const void* __ptr_;
+    __handle __handle_;
+  };
+
+  // These constructors contain the exact storage type used. If adjustments are
+  // required, these will be done in __create_format_arg.
+
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value() noexcept : __monostate_() {}
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(bool __value) noexcept : __boolean_(__value) {}
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(_CharT __value) noexcept : __char_type_(__value) {}
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(int __value) noexcept : __int_(__value) {}
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(unsigned __value) noexcept : __unsigned_(__value) {}
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(long long __value) noexcept : __long_long_(__value) {}
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(unsigned long long __value) noexcept
+      : __unsigned_long_long_(__value) {}
+#  ifndef _LIBCPP_HAS_NO_INT128
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(__int128_t __value) noexcept : __i128_(__value) {}
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(__uint128_t __value) noexcept : __u128_(__value) {}
+#  endif
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(float __value) noexcept : __float_(__value) {}
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(double __value) noexcept : __double_(__value) {}
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(long double __value) noexcept : __long_double_(__value) {}
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(const _CharT* __value) noexcept : __const_char_type_ptr_(__value) {}
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(basic_string_view<_CharT> __value) noexcept
+      : __string_view_(__value) {}
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(const void* __value) noexcept : __ptr_(__value) {}
+  _LIBCPP_HIDE_FROM_ABI __basic_format_arg_value(__handle __value) noexcept : __handle_(__value) {}
+};
+
 template <class _Context>
 class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT basic_format_arg {
 public:
@@ -131,146 +232,28 @@ private:
   //    .format(declval<const T&>(), declval<Context&>())
   // shall be well-formed when treated as an unevaluated operand.
 
-  template <class _Ctx, class... _Args>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT friend __format_arg_store<_Ctx, _Args...>
-  make_format_args(const _Args&...);
-
-  template <class _Visitor, class _Ctx>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT friend decltype(auto)
-  visit_format_arg(_Visitor&& __vis, basic_format_arg<_Ctx> __arg);
-
-  union {
-    bool __boolean;
-    char_type __char_type;
-    int __int;
-    unsigned __unsigned;
-    long long __long_long;
-    unsigned long long __unsigned_long_long;
-#ifndef _LIBCPP_HAS_NO_INT128
-    __int128_t __i128;
-    __uint128_t __u128;
-#endif
-    float __float;
-    double __double;
-    long double __long_double;
-    const char_type* __const_char_type_ptr;
-    basic_string_view<char_type> __string_view;
-    const void* __ptr;
-    handle __handle;
-  };
+public:
+  __basic_format_arg_value<_Context> __value_;
   __format::__arg_t __type_;
 
-  _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(const bool& __v) noexcept
-      : __boolean(__v), __type_(__format::__arg_t::__boolean) {}
-
-  template <class _Tp>
-  _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(const _Tp& __v) noexcept
-      requires(same_as<_Tp, char_type> ||
-               (same_as<_Tp, char> && same_as<char_type, wchar_t>))
-      : __char_type(__v), __type_(__format::__arg_t::__char_type) {}
-
-  template <__libcpp_signed_integer _Tp>
-  _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(const _Tp& __v) noexcept {
-    if constexpr (sizeof(_Tp) <= sizeof(int)) {
-      __int = static_cast<int>(__v);
-      __type_ = __format::__arg_t::__int;
-    } else if constexpr (sizeof(_Tp) <= sizeof(long long)) {
-      __long_long = static_cast<long long>(__v);
-      __type_ = __format::__arg_t::__long_long;
-    }
-#ifndef _LIBCPP_HAS_NO_INT128
-    else if constexpr (sizeof(_Tp) == sizeof(__int128_t)) {
-      __i128 = __v;
-      __type_ = __format::__arg_t::__i128;
-    }
-#endif
-    else
-      static_assert(sizeof(_Tp) == 0, "An unsupported signed integer was used");
-  }
-
-  template <__libcpp_unsigned_integer _Tp>
-  _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(const _Tp& __v) noexcept {
-    if constexpr (sizeof(_Tp) <= sizeof(unsigned)) {
-      __unsigned = static_cast<unsigned>(__v);
-      __type_ = __format::__arg_t::__unsigned;
-    } else if constexpr (sizeof(_Tp) <= sizeof(unsigned long long)) {
-      __unsigned_long_long = static_cast<unsigned long long>(__v);
-      __type_ = __format::__arg_t::__unsigned_long_long;
-    }
-#ifndef _LIBCPP_HAS_NO_INT128
-    else if constexpr (sizeof(_Tp) == sizeof(__int128_t)) {
-      __u128 = __v;
-      __type_ = __format::__arg_t::__u128;
-    }
-#endif
-    else
-      static_assert(sizeof(_Tp) == 0,
-                    "An unsupported unsigned integer was used");
-  }
-
-  _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(float __v) noexcept
-      : __float(__v), __type_(__format::__arg_t::__float) {}
-
-  _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(double __v) noexcept
-      : __double(__v), __type_(__format::__arg_t::__double) {}
-
-  _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(long double __v) noexcept
-      : __long_double(__v), __type_(__format::__arg_t::__long_double) {}
-
-  // Note not a 'noexcept' function.
-  _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(const char_type* __s)
-      : __const_char_type_ptr(__s),
-        __type_(__format::__arg_t::__const_char_type_ptr) {
-    _LIBCPP_ASSERT(__s, "Used a nullptr argument to initialize a C-string");
-  }
-
-  template <class _Traits>
-  _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(
-      basic_string_view<char_type, _Traits> __s) noexcept
-      : __string_view{__s.data(), __s.size()},
-        __type_(__format::__arg_t::__string_view) {}
-
-  template <class _Traits, class _Allocator>
-  _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(
-      const basic_string<char_type, _Traits, _Allocator>& __s) noexcept
-      : __string_view{__s.data(), __s.size()},
-        __type_(__format::__arg_t::__string_view) {}
-
-  _LIBCPP_HIDE_FROM_ABI
-  explicit basic_format_arg(nullptr_t) noexcept
-      : __ptr(nullptr), __type_(__format::__arg_t::__ptr) {}
-
-  template <class _Tp>
-  requires is_void_v<_Tp> _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(_Tp* __p) noexcept
-      : __ptr(__p), __type_(__format::__arg_t::__ptr) {}
-
-  template <class _Tp>
-  _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(const _Tp& __v) noexcept
-      : __handle(__v), __type_(__format::__arg_t::__handle) {}
+  _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(__format::__arg_t __type,
+                                                  __basic_format_arg_value<_Context> __value) noexcept
+      : __value_(__value), __type_(__type) {}
 };
 
 template <class _Context>
 class _LIBCPP_TEMPLATE_VIS basic_format_arg<_Context>::handle {
-  friend class basic_format_arg<_Context>;
-
 public:
   _LIBCPP_HIDE_FROM_ABI
   void format(basic_format_parse_context<char_type>& __parse_ctx, _Context& __ctx) const {
-    __format_(__parse_ctx, __ctx, __ptr_);
+    __handle_.__format_(__parse_ctx, __ctx, __handle_.__ptr_);
   }
 
+  _LIBCPP_HIDE_FROM_ABI explicit handle(typename __basic_format_arg_value<_Context>::__handle __handle) noexcept
+      : __handle_(__handle) {}
+
 private:
-  const void* __ptr_;
-  void (*__format_)(basic_format_parse_context<char_type>&, _Context&, const void*);
-
-  template <class _Tp>
-  _LIBCPP_HIDE_FROM_ABI explicit handle(const _Tp& __v) noexcept
-      : __ptr_(_VSTD::addressof(__v)),
-        __format_([](basic_format_parse_context<char_type>& __parse_ctx, _Context& __ctx, const void* __ptr) {
-          typename _Context::template formatter_type<_Tp> __f;
-          __parse_ctx.advance_to(__f.parse(__parse_ctx));
-          __ctx.advance_to(__f.format(*static_cast<const _Tp*>(__ptr), __ctx));
-        }) {}
+  typename __basic_format_arg_value<_Context>::__handle __handle_;
 };
 
 #endif //_LIBCPP_STD_VER > 17
diff --git a/libcxx/include/__format/format_arg_store.h b/libcxx/include/__format/format_arg_store.h
new file mode 100644 (file)
index 0000000..4a3fcd4
--- /dev/null
@@ -0,0 +1,251 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___FORMAT_FORMAT_ARG_STORE_H
+#define _LIBCPP___FORMAT_FORMAT_ARG_STORE_H
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#include <__config>
+#include <__format/concepts.h>
+#include <__format/format_arg.h>
+#include <__iterator/data.h>
+#include <__iterator/size.h>
+#include <cstring>
+#include <string>
+#include <string_view>
+#include <type_traits>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER > 17
+
+namespace __format {
+
+/// \returns The @c __arg_t based on the type of the formatting argument.
+///
+/// \pre \c __formattable<_Tp, typename _Context::char_type>
+template <class _Context, class _Tp>
+consteval __arg_t __determine_arg_t();
+
+// Boolean
+template <class, same_as<bool> _Tp>
+consteval __arg_t __determine_arg_t() {
+  return __arg_t::__boolean;
+}
+
+// Char
+template <class _Context, same_as<typename _Context::char_type> _Tp>
+consteval __arg_t __determine_arg_t() {
+  return __arg_t::__char_type;
+}
+#  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+template <class _Context, class _CharT>
+  requires(same_as<typename _Context::char_type, wchar_t> && same_as<_CharT, char>)
+consteval __arg_t __determine_arg_t() {
+  return __arg_t::__char_type;
+}
+#  endif
+
+// Signed integers
+template <class, __libcpp_signed_integer _Tp>
+consteval __arg_t __determine_arg_t() {
+  if constexpr (sizeof(_Tp) <= sizeof(int))
+    return __arg_t::__int;
+  else if constexpr (sizeof(_Tp) <= sizeof(long long))
+    return __arg_t::__long_long;
+#  ifndef _LIBCPP_HAS_NO_INT128
+  else if constexpr (sizeof(_Tp) == sizeof(__int128_t))
+    return __arg_t::__i128;
+#  endif
+  else
+    static_assert(sizeof(_Tp) == 0, "an unsupported signed integer was used");
+}
+
+// Unsigned integers
+template <class, __libcpp_unsigned_integer _Tp>
+consteval __arg_t __determine_arg_t() {
+  if constexpr (sizeof(_Tp) <= sizeof(unsigned))
+    return __arg_t::__unsigned;
+  else if constexpr (sizeof(_Tp) <= sizeof(unsigned long long))
+    return __arg_t::__unsigned_long_long;
+#  ifndef _LIBCPP_HAS_NO_INT128
+  else if constexpr (sizeof(_Tp) == sizeof(__uint128_t))
+    return __arg_t::__u128;
+#  endif
+  else
+    static_assert(sizeof(_Tp) == 0, "an unsupported unsigned integer was used");
+}
+
+// Floating-point
+template <class, same_as<float> _Tp>
+consteval __arg_t __determine_arg_t() {
+  return __arg_t::__float;
+}
+template <class, same_as<double> _Tp>
+consteval __arg_t __determine_arg_t() {
+  return __arg_t::__double;
+}
+template <class, same_as<long double> _Tp>
+consteval __arg_t __determine_arg_t() {
+  return __arg_t::__long_double;
+}
+
+// Char pointer
+template <class _Context, class _Tp>
+  requires(same_as<typename _Context::char_type*, _Tp> || same_as<const typename _Context::char_type*, _Tp>)
+consteval __arg_t __determine_arg_t() {
+  return __arg_t::__const_char_type_ptr;
+}
+
+// Char array
+template <class _Context, class _Tp>
+  requires(is_array_v<_Tp> && same_as<_Tp, typename _Context::char_type[extent_v<_Tp>]>)
+consteval __arg_t __determine_arg_t() {
+  return __arg_t::__string_view;
+}
+
+// String view
+template <class _Context, class _Tp>
+  requires(same_as<typename _Context::char_type, typename _Tp::value_type> &&
+           same_as<_Tp, basic_string_view<typename _Tp::value_type, typename _Tp::traits_type>>)
+consteval __arg_t __determine_arg_t() {
+  return __arg_t::__string_view;
+}
+
+// String
+template <class _Context, class _Tp>
+  requires(
+      same_as<typename _Context::char_type, typename _Tp::value_type> &&
+      same_as<_Tp, basic_string<typename _Tp::value_type, typename _Tp::traits_type, typename _Tp::allocator_type>>)
+consteval __arg_t __determine_arg_t() {
+  return __arg_t::__string_view;
+}
+
+// Pointers
+template <class, class _Ptr>
+  requires(same_as<_Ptr, void*> || same_as<_Ptr, const void*> || same_as<_Ptr, nullptr_t>)
+consteval __arg_t __determine_arg_t() {
+  return __arg_t::__ptr;
+}
+
+// Handle
+//
+// Note this version can't be constrained avoiding ambiguous overloads.
+// That means it can be instantiated by disabled formatters. To solve this, a
+// constrained version for not formattable formatters is added. That overload
+// is marked as deleted to fail creating a storage type for disabled formatters.
+template <class _Context, class _Tp>
+consteval __arg_t __determine_arg_t() {
+  return __arg_t::__handle;
+}
+
+template <class _Context, class _Tp>
+  requires(!__formattable<_Tp, typename _Context::char_type>)
+consteval __arg_t __determine_arg_t() = delete;
+
+template <class _Context, class _Tp>
+_LIBCPP_HIDE_FROM_ABI basic_format_arg<_Context> __create_format_arg(_Tp&& __value) noexcept {
+  constexpr __arg_t __arg = __determine_arg_t<_Context, remove_cvref_t<_Tp>>();
+  static_assert(__arg != __arg_t::__none);
+
+  // Not all types can be used to directly initialize the
+  // __basic_format_arg_value.  First handle all types needing adjustment, the
+  // final else requires no adjustment.
+  if constexpr (__arg == __arg_t::__char_type)
+    // On some platforms initializing a wchar_t from a char is a narrowing
+    // conversion.
+    return basic_format_arg<_Context>{__arg, static_cast<typename _Context::char_type>(__value)};
+  else if constexpr (__arg == __arg_t::__int)
+    return basic_format_arg<_Context>{__arg, static_cast<int>(__value)};
+  else if constexpr (__arg == __arg_t::__long_long)
+    return basic_format_arg<_Context>{__arg, static_cast<long long>(__value)};
+  else if constexpr (__arg == __arg_t::__unsigned)
+    return basic_format_arg<_Context>{__arg, static_cast<unsigned>(__value)};
+  else if constexpr (__arg == __arg_t::__unsigned_long_long)
+    return basic_format_arg<_Context>{__arg, static_cast<unsigned long long>(__value)};
+  else if constexpr (__arg == __arg_t::__string_view)
+    // When the _Traits or _Allocator are different an implicit conversion will
+    // fail.
+    //
+    // Note since the input can be an array use the non-member functions to
+    // extract the constructor arguments.
+    return basic_format_arg<_Context>{
+        __arg, basic_string_view<typename _Context::char_type>{_VSTD::data(__value), _VSTD::size(__value)}};
+  else if constexpr (__arg == __arg_t::__ptr)
+    return basic_format_arg<_Context>{__arg, static_cast<const void*>(__value)};
+  else if constexpr (__arg == __arg_t::__handle)
+    return basic_format_arg<_Context>{
+        __arg, typename __basic_format_arg_value<_Context>::__handle{_VSTD::forward<_Tp>(__value)}};
+  else
+    return basic_format_arg<_Context>{__arg, __value};
+}
+
+template <class _Context, class... _Args>
+_LIBCPP_HIDE_FROM_ABI void __create_packed_storage(uint64_t& __types, __basic_format_arg_value<_Context>* __values,
+                                                   _Args&&... __args) noexcept {
+  int __shift = 0;
+  (
+      [&] {
+        basic_format_arg<_Context> __arg = __create_format_arg<_Context>(_VSTD::forward<_Args>(__args));
+        if (__shift != 0)
+          __types |= static_cast<uint64_t>(__arg.__type_) << __shift;
+        else
+          // Assigns the initial value.
+          __types = static_cast<uint64_t>(__arg.__type_);
+        __shift += __packed_arg_t_bits;
+        *__values++ = __arg.__value_;
+      }(),
+      ...);
+}
+
+template <class _Context, class... _Args>
+_LIBCPP_HIDE_FROM_ABI void __store_basic_format_arg(basic_format_arg<_Context>* __data, _Args&&... __args) noexcept {
+  ([&] { *__data++ = __create_format_arg<_Context>(_VSTD::forward<_Args>(__args)); }(), ...);
+}
+
+template <class _Context, size_t N>
+struct __packed_format_arg_store {
+  __basic_format_arg_value<_Context> __values_[N];
+  uint64_t __types_;
+};
+
+template <class _Context, size_t N>
+struct __unpacked_format_arg_store {
+  basic_format_arg<_Context> __args_[N];
+};
+
+} // namespace __format
+
+template <class _Context, class... _Args>
+struct _LIBCPP_TEMPLATE_VIS __format_arg_store {
+  _LIBCPP_HIDE_FROM_ABI
+  __format_arg_store(_Args&&... __args) noexcept {
+    if constexpr (sizeof...(_Args) != 0) {
+      if constexpr (__format::__use_packed_format_arg_store(sizeof...(_Args)))
+        __format::__create_packed_storage(__storage.__types_, __storage.__values_, _VSTD::forward<_Args>(__args)...);
+      else
+        __format::__store_basic_format_arg<_Context>(__storage.__args_, _VSTD::forward<_Args>(__args)...);
+    }
+  }
+
+  using _Storage = conditional_t<__format::__use_packed_format_arg_store(sizeof...(_Args)),
+                                 __format::__packed_format_arg_store<_Context, sizeof...(_Args)>,
+                                 __format::__unpacked_format_arg_store<_Context, sizeof...(_Args)>>;
+
+  _Storage __storage;
+};
+
+#endif //_LIBCPP_STD_VER > 17
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___FORMAT_FORMAT_ARG_STORE_H
index 40dde98..d90dc50 100644 (file)
 
 #include <__availability>
 #include <__config>
+#include <__format/format_arg.h>
+#include <__format/format_arg_store.h>
 #include <__format/format_fwd.h>
 #include <cstddef>
+#include <cstdint>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -26,29 +29,47 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class _Context>
 class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT basic_format_args {
 public:
-  // TODO FMT Implement [format.args]/5
-  // [Note 1: Implementations are encouraged to optimize the representation of
-  // basic_format_args for small number of formatting arguments by storing
-  // indices of type alternatives separately from values and packing the
-  // former. - end note]
-  // Note: Change  __format_arg_store to use a built-in array.
   _LIBCPP_HIDE_FROM_ABI basic_format_args() noexcept = default;
 
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI basic_format_args(
-      const __format_arg_store<_Context, _Args...>& __store) noexcept
-      : __size_(sizeof...(_Args)), __data_(__store.__args.data()) {}
+  _LIBCPP_HIDE_FROM_ABI basic_format_args(const __format_arg_store<_Context, _Args...>& __store) noexcept
+      : __size_(sizeof...(_Args)) {
+    if constexpr (sizeof...(_Args) != 0) {
+      if constexpr (__format::__use_packed_format_arg_store(sizeof...(_Args))) {
+        __values_ = __store.__storage.__values_;
+        __types_ = __store.__storage.__types_;
+      } else
+        __args_ = __store.__storage.__args_;
+    }
+  }
 
   _LIBCPP_HIDE_FROM_ABI
   basic_format_arg<_Context> get(size_t __id) const noexcept {
-    return __id < __size_ ? __data_[__id] : basic_format_arg<_Context>{};
+    if (__id >= __size_)
+      return basic_format_arg<_Context>{};
+
+    if (__format::__use_packed_format_arg_store(__size_))
+      return basic_format_arg<_Context>{__format::__get_packed_type(__types_, __id), __values_[__id]};
+
+    return __args_[__id];
   }
 
   _LIBCPP_HIDE_FROM_ABI size_t __size() const noexcept { return __size_; }
 
 private:
   size_t __size_{0};
-  const basic_format_arg<_Context>* __data_{nullptr};
+  // [format.args]/5
+  // [Note 1: Implementations are encouraged to optimize the representation of
+  // basic_format_args for small number of formatting arguments by storing
+  // indices of type alternatives separately from values and packing the
+  // former. - end note]
+  union {
+    struct {
+      const __basic_format_arg_value<_Context>* __values_;
+      uint64_t __types_;
+    };
+    const basic_format_arg<_Context>* __args_;
+  };
 };
 
 #endif //_LIBCPP_STD_VER > 17
index 908d10d..f7c72e2 100644 (file)
@@ -25,9 +25,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class _Context>
 class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT basic_format_arg;
 
-template <class _Context, class... _Args>
-struct _LIBCPP_TEMPLATE_VIS __format_arg_store;
-
 template <class _OutIt, class _CharT>
   requires output_iterator<_OutIt, const _CharT&>
 class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT basic_format_context;
index 070bfe2..55ce2b1 100644 (file)
@@ -130,6 +130,7 @@ namespace std {
 #include <__format/concepts.h>
 #include <__format/enable_insertable.h>
 #include <__format/format_arg.h>
+#include <__format/format_arg_store.h>
 #include <__format/format_args.h>
 #include <__format/format_context.h>
 #include <__format/format_error.h>
@@ -176,22 +177,25 @@ using format_args = basic_format_args<format_context>;
 using wformat_args = basic_format_args<wformat_context>;
 #endif
 
+// TODO FMT This helper wrapper can probably be removed after P2418 has been
+// implemented.
 template <class _Context, class... _Args>
-struct _LIBCPP_TEMPLATE_VIS __format_arg_store {
-  // TODO FMT Use a built-in array.
-  array<basic_format_arg<_Context>, sizeof...(_Args)> __args;
-};
+_LIBCPP_HIDE_FROM_ABI __format_arg_store<_Context, _Args...>
+__make_format_args(_Args&&... __args) {
+  return _VSTD::__format_arg_store<_Context, _Args...>(
+      _VSTD::forward<_Args>(__args)...);
+}
 
+// TODO FMT After P2418 specify the return type instead of using auto.
 template <class _Context = format_context, class... _Args>
-_LIBCPP_HIDE_FROM_ABI __format_arg_store<_Context, _Args...>
-make_format_args(const _Args&... __args) {
-  return {basic_format_arg<_Context>(__args)...};
+_LIBCPP_HIDE_FROM_ABI auto make_format_args(const _Args&... __args) {
+  return _VSTD::__make_format_args<_Context>(__args...);
 }
 
 #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+// TODO FMT After P2418 specify the return type instead of using auto.
 template <class... _Args>
-_LIBCPP_HIDE_FROM_ABI __format_arg_store<wformat_context, _Args...>
-make_wformat_args(const _Args&... __args) {
+_LIBCPP_HIDE_FROM_ABI auto make_wformat_args(const _Args&... __args) {
   return _VSTD::make_format_args<wformat_context>(__args...);
 }
 #endif
index ef97b81..8796955 100644 (file)
@@ -545,6 +545,7 @@ module std [system] {
       module concepts                 { private header "__format/concepts.h" }
       module enable_insertable        { private header "__format/enable_insertable.h" }
       module format_arg               { private header "__format/format_arg.h" }
+      module format_arg_store         { private header "__format/format_arg_store.h" }
       module format_args              { private header "__format/format_args.h" }
       module format_context {
         private header "__format/format_context.h"
index cfeddfa..057aaa4 100644 (file)
@@ -244,6 +244,7 @@ END-SCRIPT
 #include <__format/concepts.h> // expected-error@*:* {{use of private header from outside its module: '__format/concepts.h'}}
 #include <__format/enable_insertable.h> // expected-error@*:* {{use of private header from outside its module: '__format/enable_insertable.h'}}
 #include <__format/format_arg.h> // expected-error@*:* {{use of private header from outside its module: '__format/format_arg.h'}}
+#include <__format/format_arg_store.h> // expected-error@*:* {{use of private header from outside its module: '__format/format_arg_store.h'}}
 #include <__format/format_args.h> // expected-error@*:* {{use of private header from outside its module: '__format/format_args.h'}}
 #include <__format/format_context.h> // expected-error@*:* {{use of private header from outside its module: '__format/format_context.h'}}
 #include <__format/format_error.h> // expected-error@*:* {{use of private header from outside its module: '__format/format_error.h'}}
index 4a671e9..0f460b6 100644 (file)
 
 template <class Context, class To, class From>
 void test(From value) {
-  auto format_args = std::make_format_args<Context>(value);
-  assert(format_args.__args.size() == 1);
-  assert(format_args.__args[0]);
+  auto store = std::make_format_args<Context>(value);
+  std::basic_format_args<Context> format_args{store};
+
+  assert(format_args.__size() == 1);
+  assert(format_args.get(0));
 
   auto result = std::visit_format_arg(
       [v = To(value)](auto a) -> To {
@@ -41,7 +43,7 @@ void test(From value) {
           return {};
         }
       },
-      format_args.__args[0]);
+      format_args.get(0));
 
   using ct = std::common_type_t<From, To>;
   assert(static_cast<ct>(result) == static_cast<ct>(value));
@@ -53,9 +55,11 @@ void test(From value) {
 // template argument.
 template <class Context, class From>
 void test_string_view(From value) {
-  auto format_args = std::make_format_args<Context>(value);
-  assert(format_args.__args.size() == 1);
-  assert(format_args.__args[0]);
+  auto store = std::make_format_args<Context>(value);
+  std::basic_format_args<Context> format_args{store};
+
+  assert(format_args.__size() == 1);
+  assert(format_args.get(0));
 
   using CharT = typename Context::char_type;
   using To = std::basic_string_view<CharT>;
@@ -70,7 +74,7 @@ void test_string_view(From value) {
           return {};
         }
       },
-      format_args.__args[0]);
+      format_args.get(0));
 
   assert(std::equal(value.begin(), value.end(), result.begin(), result.end()));
 }
@@ -351,15 +355,11 @@ void test() {
   test<Context, const void*>(static_cast<const void*>(&ci));
 }
 
-void test() {
+int main(int, char**) {
   test<char>();
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
   test<wchar_t>();
 #endif
-}
-
-int main(int, char**) {
-  test();
 
   return 0;
 }
diff --git a/libcxx/test/std/utilities/format/format.arguments/format.arg.store/class.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/class.pass.cpp
deleted file mode 100644 (file)
index 3054625..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-//===----------------------------------------------------------------------===//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: libcpp-has-no-incomplete-format
-
-// <format>
-
-// template<class Context, class... Args>
-// struct format-arg-store {      // exposition only
-//   array<basic_format_arg<Context>, sizeof...(Args)> args;
-// };
-//
-// Note more testing is done in the unit test for:
-// template<class Visitor, class Context>
-//   see below visit_format_arg(Visitor&& vis, basic_format_arg<Context> arg);
-
-#include <format>
-#include <cassert>
-#include <cstddef>
-#include <type_traits>
-
-#include "test_macros.h"
-
-template <class CharT>
-void test() {
-  using Context = std::basic_format_context<CharT*, CharT>;
-  {
-    [[maybe_unused]] auto store = std::make_format_args<Context>();
-    LIBCPP_STATIC_ASSERT(
-        std::is_same_v<decltype(store), std::__format_arg_store<Context>>);
-    LIBCPP_STATIC_ASSERT(
-        std::is_same_v<decltype(store.__args),
-                       std::array<std::basic_format_arg<Context>, 0>>);
-    LIBCPP_ASSERT(store.__args.size() == 0);
-  }
-  {
-    [[maybe_unused]] auto store = std::make_format_args<Context>(1);
-    LIBCPP_STATIC_ASSERT(
-        std::is_same_v<decltype(store), std::__format_arg_store<Context, int>>);
-    LIBCPP_STATIC_ASSERT(
-        std::is_same_v<decltype(store.__args),
-                       std::array<std::basic_format_arg<Context>, 1>>);
-    LIBCPP_ASSERT(store.__args.size() == 1);
-  }
-  {
-    [[maybe_unused]] auto store = std::make_format_args<Context>(1, 'c');
-    LIBCPP_STATIC_ASSERT(
-        std::is_same_v<decltype(store),
-                       std::__format_arg_store<Context, int, char>>);
-    LIBCPP_STATIC_ASSERT(
-        std::is_same_v<decltype(store.__args),
-                       std::array<std::basic_format_arg<Context>, 2>>);
-    LIBCPP_ASSERT(store.__args.size() == 2);
-  }
-  {
-    [[maybe_unused]] auto store = std::make_format_args<Context>(1, 'c', nullptr);
-    LIBCPP_STATIC_ASSERT(
-        std::is_same_v<decltype(store),
-                       std::__format_arg_store<Context, int, char, std::nullptr_t>>);
-    LIBCPP_STATIC_ASSERT(
-        std::is_same_v<decltype(store.__args),
-                       std::array<std::basic_format_arg<Context>, 3>>);
-    LIBCPP_ASSERT(store.__args.size() == 3);
-  }
-}
-
-void test() {
-  test<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test<wchar_t>();
-#endif
-}
-
-int main(int, char**) {
-  test();
-
-  return 0;
-}
index 8ce3622..753b27f 100644 (file)
 #include "test_macros.h"
 
 int main(int, char**) {
-  using Context [[maybe_unused]] = std::basic_format_context<
-      std::back_insert_iterator<std::basic_string<char>>, char>;
-
-  [[maybe_unused]] auto value = std::make_format_args(42, nullptr, false, 1.0);
-
-  LIBCPP_ASSERT(value.__args.size() == 4);
-  LIBCPP_ASSERT(test_basic_format_arg(value.__args[0], 42));
-  // Note [format.arg]/11 specifies a nullptr is stored as a const void*.
-  LIBCPP_ASSERT(test_basic_format_arg(value.__args[1],
-                                      static_cast<const void*>(nullptr)));
-  LIBCPP_ASSERT(test_basic_format_arg(value.__args[2], false));
-  LIBCPP_ASSERT(test_basic_format_arg(value.__args[3], 1.0));
+  using Context [[maybe_unused]] = std::basic_format_context< std::back_insert_iterator<std::basic_string<char>>, char>;
+
+  std::make_format_args(42, nullptr, false, 1.0);
 
   return 0;
 }
index 51869be..f3740e2 100644 (file)
 #include "test_macros.h"
 
 int main(int, char**) {
-  using Context [[maybe_unused]] = std::basic_format_context<
-      std::back_insert_iterator<std::basic_string<wchar_t>>, wchar_t>;
-
-  [[maybe_unused]] auto value = std::make_wformat_args(42, nullptr, false, 1.0);
-
-  LIBCPP_ASSERT(value.__args.size() == 4);
-  LIBCPP_ASSERT(test_basic_format_arg(value.__args[0], 42));
-  // Note [format.arg]/11 specifies a nullptr is stored as a const void*.
-  LIBCPP_ASSERT(test_basic_format_arg(value.__args[1],
-                                      static_cast<const void*>(nullptr)));
-  LIBCPP_ASSERT(test_basic_format_arg(value.__args[2], false));
-  LIBCPP_ASSERT(test_basic_format_arg(value.__args[3], 1.0));
+  using Context [[maybe_unused]] =
+      std::basic_format_context<std::back_insert_iterator<std::basic_string<wchar_t>>, wchar_t>;
+
+  std::make_wformat_args(42, nullptr, false, 1.0);
 
   return 0;
 }
index 4731c66..ecda3d1 100644 (file)
 
 #include "test_macros.h"
 
-void test(const auto& store) {
-#ifdef _LIBCPP_VERSION
-  for (const auto& arg : store.__args) {
-    assert(arg);
-    assert(static_cast<bool>(arg));
-  }
-#else
-  (void)store;
-#endif
-}
-
 template <class CharT>
 void test() {
   using Context = std::basic_format_context<CharT*, CharT>;
@@ -43,19 +32,13 @@ void test() {
     ASSERT_NOEXCEPT(static_cast<bool>(format_arg));
     assert(!static_cast<bool>(format_arg));
   }
-  test(std::make_format_args<Context>());
-  test(std::make_format_args<Context>(1));
-  test(std::make_format_args<Context>(1, 'c'));
-  test(std::make_format_args<Context>(1, 'c', nullptr));
 }
 
-void test() {
+int main(int, char**) {
   test<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
   test<wchar_t>();
-}
-
-int main(int, char**) {
-  test();
+#endif
 
   return 0;
 }
index f1117bd..17d8908 100644 (file)
@@ -49,13 +49,6 @@ int main(int, char**) {
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
   test<std::back_insert_iterator<std::basic_string<wchar_t>>, wchar_t>();
 #endif
-#ifndef TEST_HAS_NO_CHAR8_T
-  test<std::back_insert_iterator<std::basic_string<char8_t>>, char8_t>();
-#endif
-#ifndef TEST_HAS_NO_UNICODE_CHARS
-  test<std::back_insert_iterator<std::basic_string<char16_t>>, char16_t>();
-  test<std::back_insert_iterator<std::basic_string<char32_t>>, char32_t>();
-#endif
 
   return 0;
 }
index 07a1be5..65938ea 100644 (file)
@@ -121,22 +121,11 @@ void test() {
 #endif
 }
 
-void test() {
+int main(int, char**) {
   test<std::back_insert_iterator<std::basic_string<char>>, char>();
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
   test<std::back_insert_iterator<std::basic_string<wchar_t>>, wchar_t>();
 #endif
-#ifndef TEST_HAS_NO_CHAR8_T
-  test<std::back_insert_iterator<std::basic_string<char8_t>>, char8_t>();
-#endif
-#ifndef TEST_HAS_NO_UNICODE_CHARS
-  test<std::back_insert_iterator<std::basic_string<char16_t>>, char16_t>();
-  test<std::back_insert_iterator<std::basic_string<char32_t>>, char32_t>();
-#endif
-}
-
-int main(int, char**) {
-  test();
 
   return 0;
 }
index c802a48..a8c3535 100644 (file)
@@ -77,21 +77,11 @@ void test() {
   }
 }
 
-void test() {
+int main(int, char**) {
   test<std::back_insert_iterator<std::basic_string<char>>, char>();
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
   test<std::back_insert_iterator<std::basic_string<wchar_t>>, wchar_t>();
 #endif
-#ifndef TEST_HAS_NO_CHAR8_T
-  test<std::back_insert_iterator<std::basic_string<char8_t>>, char8_t>();
-#endif
-#ifndef TEST_HAS_NO_UNICODE_CHARS
-  test<std::back_insert_iterator<std::basic_string<char16_t>>, char16_t>();
-  test<std::back_insert_iterator<std::basic_string<char32_t>>, char32_t>();
-#endif
-}
-int main(int, char**) {
-  test();
 
   return 0;
 }
index 102bc4c..0c69935 100644 (file)
@@ -2464,6 +2464,24 @@ void format_tests(TestFunction check, ExceptionTest check_exception) {
   check.template operator()<"hello {0:} {1:}">(SV("hello false true"), false, true);
   check.template operator()<"hello {1:} {0:}">(SV("hello true false"), false, true);
 
+  // *** Test many arguments ***
+
+  // [format.args]/1
+  // An instance of basic_format_args provides access to formatting arguments.
+  // Implementations should optimize the representation of basic_format_args
+  // for a small number of formatting arguments.
+  //
+  // These's no guidances what "a small number of formatting arguments" is.
+  // - fmtlib uses a 15 elements
+  // - libc++ uses 12 elements
+  // - MSVC STL uses a different approach regardless of the number of arguments
+  // - libstdc++ has no implementation yet
+  // fmtlib and libc++ use a similar approach, this approach can support 16
+  // elements (based on design choices both support less elements). This test
+  // makes sure "the large number of formatting arguments" code path is tested.
+  check.template operator()<"{}{}{}{}{}{}{}{}{}{}\t{}{}{}{}{}{}{}{}{}{}">(SV("1234567890\t1234567890"), 1, 2, 3, 4, 5,
+                                                                          6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+
   // ** Test invalid format strings ***
   check_exception("The format string terminates at a '{'", SV("{"));
   check_exception("The replacement field misses a terminating '}'", SV("{:"), 42);