2 //===----------------------------------------------------------------------===//
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8 //===----------------------------------------------------------------------===//
10 #ifndef _LIBCPP___FORMAT_FORMAT_ARG_STORE_H
11 #define _LIBCPP___FORMAT_FORMAT_ARG_STORE_H
13 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
14 # pragma GCC system_header
17 #include <__concepts/arithmetic.h>
18 #include <__concepts/same_as.h>
20 #include <__format/concepts.h>
21 #include <__format/format_arg.h>
22 #include <__type_traits/conditional.h>
23 #include <__type_traits/extent.h>
24 #include <__type_traits/remove_const.h>
27 #include <string_view>
29 _LIBCPP_BEGIN_NAMESPACE_STD
31 #if _LIBCPP_STD_VER >= 20
35 /// \returns The @c __arg_t based on the type of the formatting argument.
37 /// \pre \c __formattable<_Tp, typename _Context::char_type>
38 template <class _Context
, class _Tp
>
39 consteval __arg_t
__determine_arg_t();
42 template <class, same_as
<bool> _Tp
>
43 consteval __arg_t
__determine_arg_t() {
44 return __arg_t::__boolean
;
48 template <class _Context
, same_as
<typename
_Context::char_type
> _Tp
>
49 consteval __arg_t
__determine_arg_t() {
50 return __arg_t::__char_type
;
52 # if _LIBCPP_HAS_WIDE_CHARACTERS
53 template <class _Context
, class _CharT
>
54 requires(same_as
<typename
_Context::char_type
, wchar_t> && same_as
<_CharT
, char>)
55 consteval __arg_t
__determine_arg_t() {
56 return __arg_t::__char_type
;
61 template <class, __libcpp_signed_integer _Tp
>
62 consteval __arg_t
__determine_arg_t() {
63 if constexpr (sizeof(_Tp
) <= sizeof(int))
64 return __arg_t::__int
;
65 else if constexpr (sizeof(_Tp
) <= sizeof(long long))
66 return __arg_t::__long_long
;
67 # if _LIBCPP_HAS_INT128
68 else if constexpr (sizeof(_Tp
) == sizeof(__int128_t
))
69 return __arg_t::__i128
;
72 static_assert(sizeof(_Tp
) == 0, "an unsupported signed integer was used");
76 template <class, __libcpp_unsigned_integer _Tp
>
77 consteval __arg_t
__determine_arg_t() {
78 if constexpr (sizeof(_Tp
) <= sizeof(unsigned))
79 return __arg_t::__unsigned
;
80 else if constexpr (sizeof(_Tp
) <= sizeof(unsigned long long))
81 return __arg_t::__unsigned_long_long
;
82 # if _LIBCPP_HAS_INT128
83 else if constexpr (sizeof(_Tp
) == sizeof(__uint128_t
))
84 return __arg_t::__u128
;
87 static_assert(sizeof(_Tp
) == 0, "an unsupported unsigned integer was used");
91 template <class, same_as
<float> _Tp
>
92 consteval __arg_t
__determine_arg_t() {
93 return __arg_t::__float
;
95 template <class, same_as
<double> _Tp
>
96 consteval __arg_t
__determine_arg_t() {
97 return __arg_t::__double
;
99 template <class, same_as
<long double> _Tp
>
100 consteval __arg_t
__determine_arg_t() {
101 return __arg_t::__long_double
;
105 template <class _Context
, class _Tp
>
106 requires(same_as
<typename
_Context::char_type
*, _Tp
> || same_as
<const typename
_Context::char_type
*, _Tp
>)
107 consteval __arg_t
__determine_arg_t() {
108 return __arg_t::__const_char_type_ptr
;
112 template <class _Context
, class _Tp
>
113 requires(is_array_v
<_Tp
> && same_as
<_Tp
, typename
_Context::char_type
[extent_v
<_Tp
>]>)
114 consteval __arg_t
__determine_arg_t() {
115 return __arg_t::__string_view
;
119 template <class _Context
, class _Tp
>
120 requires(same_as
<typename
_Context::char_type
, typename
_Tp::value_type
> &&
121 same_as
<_Tp
, basic_string_view
<typename
_Tp::value_type
, typename
_Tp::traits_type
>>)
122 consteval __arg_t
__determine_arg_t() {
123 return __arg_t::__string_view
;
127 template <class _Context
, class _Tp
>
129 same_as
<typename
_Context::char_type
, typename
_Tp::value_type
> &&
130 same_as
<_Tp
, basic_string
<typename
_Tp::value_type
, typename
_Tp::traits_type
, typename
_Tp::allocator_type
>>)
131 consteval __arg_t
__determine_arg_t() {
132 return __arg_t::__string_view
;
136 template <class, class _Ptr
>
137 requires(same_as
<_Ptr
, void*> || same_as
<_Ptr
, const void*> || same_as
<_Ptr
, nullptr_t
>)
138 consteval __arg_t
__determine_arg_t() {
139 return __arg_t::__ptr
;
144 // Note this version can't be constrained avoiding ambiguous overloads.
145 // That means it can be instantiated by disabled formatters. To solve this, a
146 // constrained version for not formattable formatters is added.
147 template <class _Context
, class _Tp
>
148 consteval __arg_t
__determine_arg_t() {
149 return __arg_t::__handle
;
152 // The overload for not formattable types allows triggering the static
154 template <class _Context
, class _Tp
>
155 requires(!__formattable_with
<_Tp
, _Context
>)
156 consteval __arg_t
__determine_arg_t() {
157 return __arg_t::__none
;
160 // Pseudo constuctor for basic_format_arg
162 // Modeled after template<class T> explicit basic_format_arg(T& v) noexcept;
164 template <class _Context
, class _Tp
>
165 _LIBCPP_HIDE_FROM_ABI basic_format_arg
<_Context
> __create_format_arg(_Tp
& __value
) noexcept
{
166 using _Dp
= remove_const_t
<_Tp
>;
167 constexpr __arg_t __arg
= __determine_arg_t
<_Context
, _Dp
>();
168 static_assert(__arg
!= __arg_t::__none
, "the supplied type is not formattable");
169 static_assert(__formattable_with
<_Tp
, _Context
>);
171 // Not all types can be used to directly initialize the
172 // __basic_format_arg_value. First handle all types needing adjustment, the
173 // final else requires no adjustment.
174 if constexpr (__arg
== __arg_t::__char_type
)
176 # if _LIBCPP_HAS_WIDE_CHARACTERS
177 if constexpr (same_as
<typename
_Context::char_type
, wchar_t> && same_as
<_Dp
, char>)
178 return basic_format_arg
<_Context
>{__arg
, static_cast<wchar_t>(static_cast<unsigned char>(__value
))};
181 return basic_format_arg
<_Context
>{__arg
, __value
};
182 else if constexpr (__arg
== __arg_t::__int
)
183 return basic_format_arg
<_Context
>{__arg
, static_cast<int>(__value
)};
184 else if constexpr (__arg
== __arg_t::__long_long
)
185 return basic_format_arg
<_Context
>{__arg
, static_cast<long long>(__value
)};
186 else if constexpr (__arg
== __arg_t::__unsigned
)
187 return basic_format_arg
<_Context
>{__arg
, static_cast<unsigned>(__value
)};
188 else if constexpr (__arg
== __arg_t::__unsigned_long_long
)
189 return basic_format_arg
<_Context
>{__arg
, static_cast<unsigned long long>(__value
)};
190 else if constexpr (__arg
== __arg_t::__string_view
)
191 // Using std::size on a character array will add the NUL-terminator to the size.
192 if constexpr (is_array_v
<_Dp
>)
193 return basic_format_arg
<_Context
>{
194 __arg
, basic_string_view
<typename
_Context::char_type
>{__value
, extent_v
<_Dp
> - 1}};
196 // When the _Traits or _Allocator are different an implicit conversion will
198 return basic_format_arg
<_Context
>{
199 __arg
, basic_string_view
<typename
_Context::char_type
>{__value
.data(), __value
.size()}};
200 else if constexpr (__arg
== __arg_t::__ptr
)
201 return basic_format_arg
<_Context
>{__arg
, static_cast<const void*>(__value
)};
202 else if constexpr (__arg
== __arg_t::__handle
)
203 return basic_format_arg
<_Context
>{__arg
, typename __basic_format_arg_value
<_Context
>::__handle
{__value
}};
205 return basic_format_arg
<_Context
>{__arg
, __value
};
208 template <class _Context
, class... _Args
>
209 _LIBCPP_HIDE_FROM_ABI
void
210 __create_packed_storage(uint64_t& __types
, __basic_format_arg_value
<_Context
>* __values
, _Args
&... __args
) noexcept
{
214 basic_format_arg
<_Context
> __arg
= __format::__create_format_arg
<_Context
>(__args
);
216 __types
|= static_cast<uint64_t>(__arg
.__type_
) << __shift
;
218 // Assigns the initial value.
219 __types
= static_cast<uint64_t>(__arg
.__type_
);
220 __shift
+= __packed_arg_t_bits
;
221 *__values
++ = __arg
.__value_
;
226 template <class _Context
, class... _Args
>
227 _LIBCPP_HIDE_FROM_ABI
void __store_basic_format_arg(basic_format_arg
<_Context
>* __data
, _Args
&... __args
) noexcept
{
228 ([&] { *__data
++ = __format::__create_format_arg
<_Context
>(__args
); }(), ...);
231 template <class _Context
, size_t _Np
>
232 struct __packed_format_arg_store
{
233 __basic_format_arg_value
<_Context
> __values_
[_Np
];
234 uint64_t __types_
= 0;
237 template <class _Context
>
238 struct __packed_format_arg_store
<_Context
, 0> {
239 uint64_t __types_
= 0;
242 template <class _Context
, size_t _Np
>
243 struct __unpacked_format_arg_store
{
244 basic_format_arg
<_Context
> __args_
[_Np
];
247 } // namespace __format
249 template <class _Context
, class... _Args
>
250 struct _LIBCPP_TEMPLATE_VIS __format_arg_store
{
251 _LIBCPP_HIDE_FROM_ABI
__format_arg_store(_Args
&... __args
) noexcept
{
252 if constexpr (sizeof...(_Args
) != 0) {
253 if constexpr (__format::__use_packed_format_arg_store(sizeof...(_Args
)))
254 __format::__create_packed_storage(__storage
.__types_
, __storage
.__values_
, __args
...);
256 __format::__store_basic_format_arg
<_Context
>(__storage
.__args_
, __args
...);
261 conditional_t
<__format::__use_packed_format_arg_store(sizeof...(_Args
)),
262 __format::__packed_format_arg_store
<_Context
, sizeof...(_Args
)>,
263 __format::__unpacked_format_arg_store
<_Context
, sizeof...(_Args
)>>;
268 #endif // _LIBCPP_STD_VER >= 20
270 _LIBCPP_END_NAMESPACE_STD
272 #endif // _LIBCPP___FORMAT_FORMAT_ARG_STORE_H