283 lines · c
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-exception7//8//===----------------------------------------------------------------------===//9 10#ifndef _LIBCPP___FORMAT_FORMAT_ARG_STORE_H11#define _LIBCPP___FORMAT_FORMAT_ARG_STORE_H12 13#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)14# pragma GCC system_header15#endif16 17#include <__concepts/same_as.h>18#include <__config>19#include <__cstddef/size_t.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/integer_traits.h>25#include <__type_traits/remove_const.h>26#include <cstdint>27#include <string>28#include <string_view>29 30_LIBCPP_BEGIN_NAMESPACE_STD31 32#if _LIBCPP_STD_VER >= 2033 34namespace __format {35 36template <class _Arr, class _Elem>37inline constexpr bool __is_bounded_array_of = false;38 39template <class _Elem, size_t _Len>40inline constexpr bool __is_bounded_array_of<_Elem[_Len], _Elem> = true;41 42/// \returns The @c __arg_t based on the type of the formatting argument.43///44/// \pre \c __formattable<_Tp, typename _Context::char_type>45template <class _Context, class _Tp>46consteval __arg_t __determine_arg_t();47 48// Boolean49template <class, same_as<bool> _Tp>50consteval __arg_t __determine_arg_t() {51 return __arg_t::__boolean;52}53 54// Char55template <class _Context, same_as<typename _Context::char_type> _Tp>56consteval __arg_t __determine_arg_t() {57 return __arg_t::__char_type;58}59# if _LIBCPP_HAS_WIDE_CHARACTERS60template <class _Context, class _CharT>61 requires(same_as<typename _Context::char_type, wchar_t> && same_as<_CharT, char>)62consteval __arg_t __determine_arg_t() {63 return __arg_t::__char_type;64}65# endif66 67// Signed integers68template <class, __signed_integer _Tp>69consteval __arg_t __determine_arg_t() {70 if constexpr (sizeof(_Tp) <= sizeof(int))71 return __arg_t::__int;72 else if constexpr (sizeof(_Tp) <= sizeof(long long))73 return __arg_t::__long_long;74# if _LIBCPP_HAS_INT12875 else if constexpr (sizeof(_Tp) == sizeof(__int128_t))76 return __arg_t::__i128;77# endif78 else79 static_assert(sizeof(_Tp) == 0, "an unsupported signed integer was used");80}81 82// Unsigned integers83template <class, __unsigned_integer _Tp>84consteval __arg_t __determine_arg_t() {85 if constexpr (sizeof(_Tp) <= sizeof(unsigned))86 return __arg_t::__unsigned;87 else if constexpr (sizeof(_Tp) <= sizeof(unsigned long long))88 return __arg_t::__unsigned_long_long;89# if _LIBCPP_HAS_INT12890 else if constexpr (sizeof(_Tp) == sizeof(__uint128_t))91 return __arg_t::__u128;92# endif93 else94 static_assert(sizeof(_Tp) == 0, "an unsupported unsigned integer was used");95}96 97// Floating-point98template <class, same_as<float> _Tp>99consteval __arg_t __determine_arg_t() {100 return __arg_t::__float;101}102template <class, same_as<double> _Tp>103consteval __arg_t __determine_arg_t() {104 return __arg_t::__double;105}106template <class, same_as<long double> _Tp>107consteval __arg_t __determine_arg_t() {108 return __arg_t::__long_double;109}110 111// Char pointer112template <class _Context, class _Tp>113 requires(same_as<typename _Context::char_type*, _Tp> || same_as<const typename _Context::char_type*, _Tp>)114consteval __arg_t __determine_arg_t() {115 return __arg_t::__const_char_type_ptr;116}117 118// Char array119template <class _Context, class _Tp>120 requires __is_bounded_array_of<_Tp, typename _Context::char_type>121consteval __arg_t __determine_arg_t() {122 return __arg_t::__string_view;123}124 125// String view126template <class _Context, class _Tp>127 requires(same_as<typename _Context::char_type, typename _Tp::value_type> &&128 same_as<_Tp, basic_string_view<typename _Tp::value_type, typename _Tp::traits_type>>)129consteval __arg_t __determine_arg_t() {130 return __arg_t::__string_view;131}132 133// String134template <class _Context, class _Tp>135 requires(136 same_as<typename _Context::char_type, typename _Tp::value_type> &&137 same_as<_Tp, basic_string<typename _Tp::value_type, typename _Tp::traits_type, typename _Tp::allocator_type>>)138consteval __arg_t __determine_arg_t() {139 return __arg_t::__string_view;140}141 142// Pointers143template <class, class _Ptr>144 requires(same_as<_Ptr, void*> || same_as<_Ptr, const void*> || same_as<_Ptr, nullptr_t>)145consteval __arg_t __determine_arg_t() {146 return __arg_t::__ptr;147}148 149// Handle150//151// Note this version can't be constrained avoiding ambiguous overloads.152// That means it can be instantiated by disabled formatters. To solve this, a153// constrained version for not formattable formatters is added.154template <class _Context, class _Tp>155consteval __arg_t __determine_arg_t() {156 return __arg_t::__handle;157}158 159// The overload for not formattable types allows triggering the static160// assertion below.161template <class _Context, class _Tp>162 requires(!__formattable_with<_Tp, _Context>)163consteval __arg_t __determine_arg_t() {164 return __arg_t::__none;165}166 167// Pseudo constuctor for basic_format_arg168//169// Modeled after template<class T> explicit basic_format_arg(T& v) noexcept;170// [format.arg]/4-6171template <class _Context, class _Tp>172_LIBCPP_HIDE_FROM_ABI basic_format_arg<_Context> __create_format_arg(_Tp& __value) noexcept {173 using _Dp = remove_const_t<_Tp>;174 constexpr __arg_t __arg = __format::__determine_arg_t<_Context, _Dp>();175 static_assert(__arg != __arg_t::__none, "the supplied type is not formattable");176 static_assert(__formattable_with<_Tp, _Context>);177 178 using __context_char_type = _Context::char_type;179 // Not all types can be used to directly initialize the180 // __basic_format_arg_value. First handle all types needing adjustment, the181 // final else requires no adjustment.182 if constexpr (__arg == __arg_t::__char_type)183 184# if _LIBCPP_HAS_WIDE_CHARACTERS185 if constexpr (same_as<__context_char_type, wchar_t> && same_as<_Dp, char>)186 return basic_format_arg<_Context>{__arg, static_cast<wchar_t>(static_cast<unsigned char>(__value))};187 else188# endif189 return basic_format_arg<_Context>{__arg, __value};190 else if constexpr (__arg == __arg_t::__int)191 return basic_format_arg<_Context>{__arg, static_cast<int>(__value)};192 else if constexpr (__arg == __arg_t::__long_long)193 return basic_format_arg<_Context>{__arg, static_cast<long long>(__value)};194 else if constexpr (__arg == __arg_t::__unsigned)195 return basic_format_arg<_Context>{__arg, static_cast<unsigned>(__value)};196 else if constexpr (__arg == __arg_t::__unsigned_long_long)197 return basic_format_arg<_Context>{__arg, static_cast<unsigned long long>(__value)};198 else if constexpr (__arg == __arg_t::__string_view)199 // Using std::size on a character array will add the NUL-terminator to the size.200 if constexpr (__is_bounded_array_of<_Dp, __context_char_type>) {201 const __context_char_type* const __pbegin = std::begin(__value);202 const __context_char_type* const __pzero =203 char_traits<__context_char_type>::find(__pbegin, extent_v<_Dp>, __context_char_type{});204 _LIBCPP_ASSERT_VALID_INPUT_RANGE(__pzero != nullptr, "formatting a non-null-terminated array");205 return basic_format_arg<_Context>{206 __arg, basic_string_view<__context_char_type>{__pbegin, static_cast<size_t>(__pzero - __pbegin)}};207 } else208 // When the _Traits or _Allocator are different an implicit conversion will fail.209 return basic_format_arg<_Context>{__arg, basic_string_view<__context_char_type>{__value.data(), __value.size()}};210 else if constexpr (__arg == __arg_t::__ptr)211 return basic_format_arg<_Context>{__arg, static_cast<const void*>(__value)};212 else if constexpr (__arg == __arg_t::__handle)213 return basic_format_arg<_Context>{__arg, typename __basic_format_arg_value<_Context>::__handle{__value}};214 else215 return basic_format_arg<_Context>{__arg, __value};216}217 218template <class _Context, class... _Args>219_LIBCPP_HIDE_FROM_ABI void220__create_packed_storage(uint64_t& __types, __basic_format_arg_value<_Context>* __values, _Args&... __args) noexcept {221 int __shift = 0;222 (223 [&] {224 basic_format_arg<_Context> __arg = __format::__create_format_arg<_Context>(__args);225 if (__shift != 0)226 __types |= static_cast<uint64_t>(__arg.__type_) << __shift;227 else228 // Assigns the initial value.229 __types = static_cast<uint64_t>(__arg.__type_);230 __shift += __packed_arg_t_bits;231 *__values++ = __arg.__value_;232 }(),233 ...);234}235 236template <class _Context, class... _Args>237_LIBCPP_HIDE_FROM_ABI void __store_basic_format_arg(basic_format_arg<_Context>* __data, _Args&... __args) noexcept {238 ([&] { *__data++ = __format::__create_format_arg<_Context>(__args); }(), ...);239}240 241template <class _Context, size_t _Np>242struct __packed_format_arg_store {243 __basic_format_arg_value<_Context> __values_[_Np];244 uint64_t __types_ = 0;245};246 247template <class _Context>248struct __packed_format_arg_store<_Context, 0> {249 uint64_t __types_ = 0;250};251 252template <class _Context, size_t _Np>253struct __unpacked_format_arg_store {254 basic_format_arg<_Context> __args_[_Np];255};256 257} // namespace __format258 259template <class _Context, class... _Args>260struct __format_arg_store {261 _LIBCPP_HIDE_FROM_ABI __format_arg_store(_Args&... __args) noexcept {262 if constexpr (sizeof...(_Args) != 0) {263 if constexpr (__format::__use_packed_format_arg_store(sizeof...(_Args)))264 __format::__create_packed_storage(__storage.__types_, __storage.__values_, __args...);265 else266 __format::__store_basic_format_arg<_Context>(__storage.__args_, __args...);267 }268 }269 270 using _Storage _LIBCPP_NODEBUG =271 conditional_t<__format::__use_packed_format_arg_store(sizeof...(_Args)),272 __format::__packed_format_arg_store<_Context, sizeof...(_Args)>,273 __format::__unpacked_format_arg_store<_Context, sizeof...(_Args)>>;274 275 _Storage __storage;276};277 278#endif // _LIBCPP_STD_VER >= 20279 280_LIBCPP_END_NAMESPACE_STD281 282#endif // _LIBCPP___FORMAT_FORMAT_ARG_STORE_H283