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 <cstring> 23 #include <string> 24 #include <string_view> 25 #include <type_traits> 26 27 _LIBCPP_BEGIN_NAMESPACE_STD 28 29 #if _LIBCPP_STD_VER > 17 30 31 namespace __format { 32 33 /// \returns The @c __arg_t based on the type of the formatting argument. 34 /// 35 /// \pre \c __formattable<_Tp, typename _Context::char_type> 36 template <class _Context, class _Tp> 37 consteval __arg_t __determine_arg_t(); 38 39 // Boolean 40 template <class, same_as<bool> _Tp> 41 consteval __arg_t __determine_arg_t() { 42 return __arg_t::__boolean; 43 } 44 45 // Char 46 template <class _Context, same_as<typename _Context::char_type> _Tp> 47 consteval __arg_t __determine_arg_t() { 48 return __arg_t::__char_type; 49 } 50 # ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS 51 template <class _Context, class _CharT> 52 requires(same_as<typename _Context::char_type, wchar_t> && same_as<_CharT, char>) 53 consteval __arg_t __determine_arg_t() { 54 return __arg_t::__char_type; 55 } 56 # endif 57 58 // Signed integers 59 template <class, __libcpp_signed_integer _Tp> 60 consteval __arg_t __determine_arg_t() { 61 if constexpr (sizeof(_Tp) <= sizeof(int)) 62 return __arg_t::__int; 63 else if constexpr (sizeof(_Tp) <= sizeof(long long)) 64 return __arg_t::__long_long; 65 # ifndef _LIBCPP_HAS_NO_INT128 66 else if constexpr (sizeof(_Tp) == sizeof(__int128_t)) 67 return __arg_t::__i128; 68 # endif 69 else 70 static_assert(sizeof(_Tp) == 0, "an unsupported signed integer was used"); 71 } 72 73 // Unsigned integers 74 template <class, __libcpp_unsigned_integer _Tp> 75 consteval __arg_t __determine_arg_t() { 76 if constexpr (sizeof(_Tp) <= sizeof(unsigned)) 77 return __arg_t::__unsigned; 78 else if constexpr (sizeof(_Tp) <= sizeof(unsigned long long)) 79 return __arg_t::__unsigned_long_long; 80 # ifndef _LIBCPP_HAS_NO_INT128 81 else if constexpr (sizeof(_Tp) == sizeof(__uint128_t)) 82 return __arg_t::__u128; 83 # endif 84 else 85 static_assert(sizeof(_Tp) == 0, "an unsupported unsigned integer was used"); 86 } 87 88 // Floating-point 89 template <class, same_as<float> _Tp> 90 consteval __arg_t __determine_arg_t() { 91 return __arg_t::__float; 92 } 93 template <class, same_as<double> _Tp> 94 consteval __arg_t __determine_arg_t() { 95 return __arg_t::__double; 96 } 97 template <class, same_as<long double> _Tp> 98 consteval __arg_t __determine_arg_t() { 99 return __arg_t::__long_double; 100 } 101 102 // Char pointer 103 template <class _Context, class _Tp> 104 requires(same_as<typename _Context::char_type*, _Tp> || same_as<const typename _Context::char_type*, _Tp>) 105 consteval __arg_t __determine_arg_t() { 106 return __arg_t::__const_char_type_ptr; 107 } 108 109 // Char array 110 template <class _Context, class _Tp> 111 requires(is_array_v<_Tp> && same_as<_Tp, typename _Context::char_type[extent_v<_Tp>]>) 112 consteval __arg_t __determine_arg_t() { 113 return __arg_t::__string_view; 114 } 115 116 // String view 117 template <class _Context, class _Tp> 118 requires(same_as<typename _Context::char_type, typename _Tp::value_type> && 119 same_as<_Tp, basic_string_view<typename _Tp::value_type, typename _Tp::traits_type>>) 120 consteval __arg_t __determine_arg_t() { 121 return __arg_t::__string_view; 122 } 123 124 // String 125 template <class _Context, class _Tp> 126 requires( 127 same_as<typename _Context::char_type, typename _Tp::value_type> && 128 same_as<_Tp, basic_string<typename _Tp::value_type, typename _Tp::traits_type, typename _Tp::allocator_type>>) 129 consteval __arg_t __determine_arg_t() { 130 return __arg_t::__string_view; 131 } 132 133 // Pointers 134 template <class, class _Ptr> 135 requires(same_as<_Ptr, void*> || same_as<_Ptr, const void*> || same_as<_Ptr, nullptr_t>) 136 consteval __arg_t __determine_arg_t() { 137 return __arg_t::__ptr; 138 } 139 140 // Handle 141 // 142 // Note this version can't be constrained avoiding ambiguous overloads. 143 // That means it can be instantiated by disabled formatters. To solve this, a 144 // constrained version for not formattable formatters is added. That overload 145 // is marked as deleted to fail creating a storage type for disabled formatters. 146 template <class _Context, class _Tp> 147 consteval __arg_t __determine_arg_t() { 148 return __arg_t::__handle; 149 } 150 151 template <class _Context, class _Tp> 152 requires(!__formattable<_Tp, typename _Context::char_type>) 153 consteval __arg_t __determine_arg_t() = delete; 154 155 template <class _Context, class _Tp> 156 _LIBCPP_HIDE_FROM_ABI basic_format_arg<_Context> __create_format_arg(_Tp&& __value) noexcept { 157 constexpr __arg_t __arg = __determine_arg_t<_Context, remove_cvref_t<_Tp>>(); 158 static_assert(__arg != __arg_t::__none); 159 160 // Not all types can be used to directly initialize the 161 // __basic_format_arg_value. First handle all types needing adjustment, the 162 // final else requires no adjustment. 163 if constexpr (__arg == __arg_t::__char_type) 164 // On some platforms initializing a wchar_t from a char is a narrowing 165 // conversion. 166 return basic_format_arg<_Context>{__arg, static_cast<typename _Context::char_type>(__value)}; 167 else if constexpr (__arg == __arg_t::__int) 168 return basic_format_arg<_Context>{__arg, static_cast<int>(__value)}; 169 else if constexpr (__arg == __arg_t::__long_long) 170 return basic_format_arg<_Context>{__arg, static_cast<long long>(__value)}; 171 else if constexpr (__arg == __arg_t::__unsigned) 172 return basic_format_arg<_Context>{__arg, static_cast<unsigned>(__value)}; 173 else if constexpr (__arg == __arg_t::__unsigned_long_long) 174 return basic_format_arg<_Context>{__arg, static_cast<unsigned long long>(__value)}; 175 else if constexpr (__arg == __arg_t::__string_view) 176 // Using std::size on a character array will add the NUL-terminator to the size. 177 if constexpr (is_array_v<remove_cvref_t<_Tp>>) 178 return basic_format_arg<_Context>{ 179 __arg, basic_string_view<typename _Context::char_type>{__value, extent_v<remove_cvref_t<_Tp>> - 1}}; 180 else 181 // When the _Traits or _Allocator are different an implicit conversion will 182 // fail. 183 return basic_format_arg<_Context>{ 184 __arg, basic_string_view<typename _Context::char_type>{__value.data(), __value.size()}}; 185 else if constexpr (__arg == __arg_t::__ptr) 186 return basic_format_arg<_Context>{__arg, static_cast<const void*>(__value)}; 187 else if constexpr (__arg == __arg_t::__handle) 188 return basic_format_arg<_Context>{ 189 __arg, typename __basic_format_arg_value<_Context>::__handle{_VSTD::forward<_Tp>(__value)}}; 190 else 191 return basic_format_arg<_Context>{__arg, __value}; 192 } 193 194 template <class _Context, class... _Args> 195 _LIBCPP_HIDE_FROM_ABI void __create_packed_storage(uint64_t& __types, __basic_format_arg_value<_Context>* __values, 196 _Args&&... __args) noexcept { 197 int __shift = 0; 198 ( 199 [&] { 200 basic_format_arg<_Context> __arg = __create_format_arg<_Context>(__args); 201 if (__shift != 0) 202 __types |= static_cast<uint64_t>(__arg.__type_) << __shift; 203 else 204 // Assigns the initial value. 205 __types = static_cast<uint64_t>(__arg.__type_); 206 __shift += __packed_arg_t_bits; 207 *__values++ = __arg.__value_; 208 }(), 209 ...); 210 } 211 212 template <class _Context, class... _Args> 213 _LIBCPP_HIDE_FROM_ABI void __store_basic_format_arg(basic_format_arg<_Context>* __data, _Args&&... __args) noexcept { 214 ([&] { *__data++ = __create_format_arg<_Context>(__args); }(), ...); 215 } 216 217 template <class _Context, size_t N> 218 struct __packed_format_arg_store { 219 __basic_format_arg_value<_Context> __values_[N]; 220 uint64_t __types_; 221 }; 222 223 template <class _Context, size_t N> 224 struct __unpacked_format_arg_store { 225 basic_format_arg<_Context> __args_[N]; 226 }; 227 228 } // namespace __format 229 230 template <class _Context, class... _Args> 231 struct _LIBCPP_TEMPLATE_VIS __format_arg_store { 232 _LIBCPP_HIDE_FROM_ABI 233 __format_arg_store(_Args&... __args) noexcept { 234 if constexpr (sizeof...(_Args) != 0) { 235 if constexpr (__format::__use_packed_format_arg_store(sizeof...(_Args))) 236 __format::__create_packed_storage(__storage.__types_, __storage.__values_, __args...); 237 else 238 __format::__store_basic_format_arg<_Context>(__storage.__args_, __args...); 239 } 240 } 241 242 using _Storage = conditional_t<__format::__use_packed_format_arg_store(sizeof...(_Args)), 243 __format::__packed_format_arg_store<_Context, sizeof...(_Args)>, 244 __format::__unpacked_format_arg_store<_Context, sizeof...(_Args)>>; 245 246 _Storage __storage; 247 }; 248 249 #endif //_LIBCPP_STD_VER > 17 250 251 _LIBCPP_END_NAMESPACE_STD 252 253 #endif // _LIBCPP___FORMAT_FORMAT_ARG_STORE_H 254