• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // -*- C++ -*-
2 //===----------------------------------------------------------------------===//
3 //
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
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #ifndef _LIBCPP___FORMAT_FORMAT_ARG_STORE_H
11 #define _LIBCPP___FORMAT_FORMAT_ARG_STORE_H
12 
13 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
14 #  pragma GCC system_header
15 #endif
16 
17 #include <__concepts/arithmetic.h>
18 #include <__concepts/same_as.h>
19 #include <__config>
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/is_same.h>
25 #include <__utility/forward.h>
26 #include <string>
27 #include <string_view>
28 
29 _LIBCPP_BEGIN_NAMESPACE_STD
30 
31 #if _LIBCPP_STD_VER >= 20
32 
33 namespace __format {
34 
35 /// \returns The @c __arg_t based on the type of the formatting argument.
36 ///
37 /// \pre \c __formattable<_Tp, typename _Context::char_type>
38 template <class _Context, class _Tp>
39 consteval __arg_t __determine_arg_t();
40 
41 // Boolean
42 template <class, same_as<bool> _Tp>
__determine_arg_t()43 consteval __arg_t __determine_arg_t() {
44   return __arg_t::__boolean;
45 }
46 
47 // Char
48 template <class _Context, same_as<typename _Context::char_type> _Tp>
__determine_arg_t()49 consteval __arg_t __determine_arg_t() {
50   return __arg_t::__char_type;
51 }
52 #  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
53 template <class _Context, class _CharT>
requires(same_as<typename _Context::char_type,wchar_t> && same_as<_CharT,char>)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;
57 }
58 #  endif
59 
60 // Signed integers
61 template <class, __libcpp_signed_integer _Tp>
__determine_arg_t()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 #  ifndef _LIBCPP_HAS_NO_INT128
68   else if constexpr (sizeof(_Tp) == sizeof(__int128_t))
69     return __arg_t::__i128;
70 #  endif
71   else
72     static_assert(sizeof(_Tp) == 0, "an unsupported signed integer was used");
73 }
74 
75 // Unsigned integers
76 template <class, __libcpp_unsigned_integer _Tp>
__determine_arg_t()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 #  ifndef _LIBCPP_HAS_NO_INT128
83   else if constexpr (sizeof(_Tp) == sizeof(__uint128_t))
84     return __arg_t::__u128;
85 #  endif
86   else
87     static_assert(sizeof(_Tp) == 0, "an unsupported unsigned integer was used");
88 }
89 
90 // Floating-point
91 template <class, same_as<float> _Tp>
__determine_arg_t()92 consteval __arg_t __determine_arg_t() {
93   return __arg_t::__float;
94 }
95 template <class, same_as<double> _Tp>
__determine_arg_t()96 consteval __arg_t __determine_arg_t() {
97   return __arg_t::__double;
98 }
99 template <class, same_as<long double> _Tp>
__determine_arg_t()100 consteval __arg_t __determine_arg_t() {
101   return __arg_t::__long_double;
102 }
103 
104 // Char pointer
105 template <class _Context, class _Tp>
106   requires(same_as<typename _Context::char_type*, _Tp> || same_as<const typename _Context::char_type*, _Tp>)
__determine_arg_t()107 consteval __arg_t __determine_arg_t() {
108   return __arg_t::__const_char_type_ptr;
109 }
110 
111 // Char array
112 template <class _Context, class _Tp>
requires(is_array_v<_Tp> && same_as<_Tp,typename _Context::char_type[extent_v<_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;
116 }
117 
118 // String view
119 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>>)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;
124 }
125 
126 // String
127 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>>)128   requires(
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;
133 }
134 
135 // Pointers
136 template <class, class _Ptr>
137   requires(same_as<_Ptr, void*> || same_as<_Ptr, const void*> || same_as<_Ptr, nullptr_t>)
__determine_arg_t()138 consteval __arg_t __determine_arg_t() {
139   return __arg_t::__ptr;
140 }
141 
142 // Handle
143 //
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>
__determine_arg_t()148 consteval __arg_t __determine_arg_t() {
149   return __arg_t::__handle;
150 }
151 
152 // The overload for not formattable types allows triggering the static
153 // assertion below.
154 template <class _Context, class _Tp>
155   requires(!__formattable<_Tp, typename _Context::char_type>)
__determine_arg_t()156 consteval __arg_t __determine_arg_t() {
157   return __arg_t::__none;
158 }
159 
160 template <class _Context, class _Tp>
__create_format_arg(_Tp && __value)161 _LIBCPP_HIDE_FROM_ABI basic_format_arg<_Context> __create_format_arg(_Tp&& __value) noexcept {
162   constexpr __arg_t __arg = __determine_arg_t<_Context, remove_cvref_t<_Tp>>();
163   static_assert(__arg != __arg_t::__none, "the supplied type is not formattable");
164 
165   // Not all types can be used to directly initialize the
166   // __basic_format_arg_value.  First handle all types needing adjustment, the
167   // final else requires no adjustment.
168   if constexpr (__arg == __arg_t::__char_type)
169     // On some platforms initializing a wchar_t from a char is a narrowing
170     // conversion.
171     return basic_format_arg<_Context>{__arg, static_cast<typename _Context::char_type>(__value)};
172   else if constexpr (__arg == __arg_t::__int)
173     return basic_format_arg<_Context>{__arg, static_cast<int>(__value)};
174   else if constexpr (__arg == __arg_t::__long_long)
175     return basic_format_arg<_Context>{__arg, static_cast<long long>(__value)};
176   else if constexpr (__arg == __arg_t::__unsigned)
177     return basic_format_arg<_Context>{__arg, static_cast<unsigned>(__value)};
178   else if constexpr (__arg == __arg_t::__unsigned_long_long)
179     return basic_format_arg<_Context>{__arg, static_cast<unsigned long long>(__value)};
180   else if constexpr (__arg == __arg_t::__string_view)
181     // Using std::size on a character array will add the NUL-terminator to the size.
182     if constexpr (is_array_v<remove_cvref_t<_Tp>>)
183       return basic_format_arg<_Context>{
184           __arg, basic_string_view<typename _Context::char_type>{__value, extent_v<remove_cvref_t<_Tp>> - 1}};
185     else
186       // When the _Traits or _Allocator are different an implicit conversion will
187       // fail.
188       return basic_format_arg<_Context>{
189           __arg, basic_string_view<typename _Context::char_type>{__value.data(), __value.size()}};
190   else if constexpr (__arg == __arg_t::__ptr)
191     return basic_format_arg<_Context>{__arg, static_cast<const void*>(__value)};
192   else if constexpr (__arg == __arg_t::__handle)
193     return basic_format_arg<_Context>{
194         __arg, typename __basic_format_arg_value<_Context>::__handle{_VSTD::forward<_Tp>(__value)}};
195   else
196     return basic_format_arg<_Context>{__arg, __value};
197 }
198 
199 template <class _Context, class... _Args>
__create_packed_storage(uint64_t & __types,__basic_format_arg_value<_Context> * __values,_Args &&...__args)200 _LIBCPP_HIDE_FROM_ABI void __create_packed_storage(uint64_t& __types, __basic_format_arg_value<_Context>* __values,
201                                                    _Args&&... __args) noexcept {
202   int __shift = 0;
203   (
204       [&] {
205         basic_format_arg<_Context> __arg = __format::__create_format_arg<_Context>(__args);
206         if (__shift != 0)
207           __types |= static_cast<uint64_t>(__arg.__type_) << __shift;
208         else
209           // Assigns the initial value.
210           __types = static_cast<uint64_t>(__arg.__type_);
211         __shift += __packed_arg_t_bits;
212         *__values++ = __arg.__value_;
213       }(),
214       ...);
215 }
216 
217 template <class _Context, class... _Args>
__store_basic_format_arg(basic_format_arg<_Context> * __data,_Args &&...__args)218 _LIBCPP_HIDE_FROM_ABI void __store_basic_format_arg(basic_format_arg<_Context>* __data, _Args&&... __args) noexcept {
219   ([&] { *__data++ = __format::__create_format_arg<_Context>(__args); }(), ...);
220 }
221 
222 template <class _Context, size_t N>
223 struct __packed_format_arg_store {
224   __basic_format_arg_value<_Context> __values_[N];
225   uint64_t __types_;
226 };
227 
228 template <class _Context, size_t N>
229 struct __unpacked_format_arg_store {
230   basic_format_arg<_Context> __args_[N];
231 };
232 
233 } // namespace __format
234 
235 template <class _Context, class... _Args>
236 struct _LIBCPP_TEMPLATE_VIS __format_arg_store {
237   _LIBCPP_HIDE_FROM_ABI
__format_arg_store__format_arg_store238   __format_arg_store(_Args&... __args) noexcept {
239     if constexpr (sizeof...(_Args) != 0) {
240       if constexpr (__format::__use_packed_format_arg_store(sizeof...(_Args)))
241         __format::__create_packed_storage(__storage.__types_, __storage.__values_, __args...);
242       else
243         __format::__store_basic_format_arg<_Context>(__storage.__args_, __args...);
244     }
245   }
246 
247   using _Storage = conditional_t<__format::__use_packed_format_arg_store(sizeof...(_Args)),
248                                  __format::__packed_format_arg_store<_Context, sizeof...(_Args)>,
249                                  __format::__unpacked_format_arg_store<_Context, sizeof...(_Args)>>;
250 
251   _Storage __storage;
252 };
253 
254 #endif //_LIBCPP_STD_VER >= 20
255 
256 _LIBCPP_END_NAMESPACE_STD
257 
258 #endif // _LIBCPP___FORMAT_FORMAT_ARG_STORE_H
259