// Copyright 2020 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_ #define ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include "absl/base/config.h" #include "absl/base/optimization.h" #include "absl/meta/type_traits.h" #include "absl/numeric/int128.h" #include "absl/strings/has_absl_stringify.h" #include "absl/strings/internal/str_format/extension.h" #include "absl/strings/string_view.h" #if defined(ABSL_HAVE_STD_STRING_VIEW) #include #endif namespace absl { ABSL_NAMESPACE_BEGIN class Cord; class FormatCountCapture; class FormatSink; template struct FormatConvertResult; class FormatConversionSpec; namespace str_format_internal { template struct ArgConvertResult { bool value; }; using IntegralConvertResult = ArgConvertResult; using FloatingConvertResult = ArgConvertResult; using CharConvertResult = ArgConvertResult; template struct HasUserDefinedConvert : std::false_type {}; template struct HasUserDefinedConvert(), std::declval(), std::declval()))>> : std::true_type {}; // These declarations prevent ADL lookup from continuing in absl namespaces, // we are deliberately using these as ADL hooks and want them to consider // non-absl namespaces only. void AbslFormatConvert(); void AbslStringify(); template bool ConvertIntArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); // Forward declarations of internal `ConvertIntArg` function template // instantiations are here to avoid including the template body in the headers // and instantiating it in large numbers of translation units. Explicit // instantiations can be found in "absl/strings/internal/str_format/arg.cc" extern template bool ConvertIntArg(char v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); extern template bool ConvertIntArg(signed char v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); extern template bool ConvertIntArg(unsigned char v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); extern template bool ConvertIntArg(wchar_t v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); extern template bool ConvertIntArg(short v, // NOLINT FormatConversionSpecImpl conv, FormatSinkImpl* sink); extern template bool ConvertIntArg( // NOLINT unsigned short v, FormatConversionSpecImpl conv, // NOLINT FormatSinkImpl* sink); extern template bool ConvertIntArg(int v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); extern template bool ConvertIntArg(unsigned int v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); extern template bool ConvertIntArg( // NOLINT long v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); // NOLINT extern template bool ConvertIntArg(unsigned long v, // NOLINT FormatConversionSpecImpl conv, FormatSinkImpl* sink); extern template bool ConvertIntArg(long long v, // NOLINT FormatConversionSpecImpl conv, FormatSinkImpl* sink); extern template bool ConvertIntArg( // NOLINT unsigned long long v, FormatConversionSpecImpl conv, // NOLINT FormatSinkImpl* sink); template auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv, FormatSinkImpl* sink) -> decltype(AbslFormatConvert(v, std::declval(), std::declval())) { using FormatConversionSpecT = absl::enable_if_t; using FormatSinkT = absl::enable_if_t; auto fcs = conv.Wrap(); auto fs = sink->Wrap(); return AbslFormatConvert(v, fcs, &fs); } template auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv, FormatSinkImpl* sink) -> std::enable_if_t::value && std::is_void(), v))>::value, IntegralConvertResult> { if (conv.conversion_char() == FormatConversionCharInternal::v) { using FormatSinkT = absl::enable_if_t; auto fs = sink->Wrap(); AbslStringify(fs, v); return {true}; } else { return {ConvertIntArg( static_cast::type>(v), conv, sink)}; } } template auto FormatConvertImpl(const T& v, FormatConversionSpecImpl, FormatSinkImpl* sink) -> std::enable_if_t::value && !std::is_same::value && std::is_void(), v))>::value, ArgConvertResult> { using FormatSinkT = absl::enable_if_t; auto fs = sink->Wrap(); AbslStringify(fs, v); return {true}; } template class StreamedWrapper; // If 'v' can be converted (in the printf sense) according to 'conv', // then convert it, appending to `sink` and return `true`. // Otherwise fail and return `false`. // AbslFormatConvert(v, conv, sink) is intended to be found by ADL on 'v' // as an extension mechanism. These FormatConvertImpl functions are the default // implementations. // The ADL search is augmented via the 'Sink*' parameter, which also // serves as a disambiguator to reject possible unintended 'AbslFormatConvert' // functions in the namespaces associated with 'v'. // Raw pointers. struct VoidPtr { VoidPtr() = default; template (std::declval())) = 0> VoidPtr(T* ptr) // NOLINT : value(ptr ? reinterpret_cast(ptr) : 0) {} uintptr_t value; }; template constexpr FormatConversionCharSet ExtractCharSet(FormatConvertResult) { return C; } template constexpr FormatConversionCharSet ExtractCharSet(ArgConvertResult) { return C; } ArgConvertResult FormatConvertImpl( VoidPtr v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); // Strings. using StringConvertResult = ArgConvertResult; StringConvertResult FormatConvertImpl(const std::string& v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); StringConvertResult FormatConvertImpl(const std::wstring& v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); StringConvertResult FormatConvertImpl(string_view v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); #if defined(ABSL_HAVE_STD_STRING_VIEW) StringConvertResult FormatConvertImpl(std::wstring_view v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); #if !defined(ABSL_USES_STD_STRING_VIEW) inline StringConvertResult FormatConvertImpl(std::string_view v, FormatConversionSpecImpl conv, FormatSinkImpl* sink) { return FormatConvertImpl(absl::string_view(v.data(), v.size()), conv, sink); } #endif // !ABSL_USES_STD_STRING_VIEW #endif // ABSL_HAVE_STD_STRING_VIEW using StringPtrConvertResult = ArgConvertResult; StringPtrConvertResult FormatConvertImpl(const char* v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); StringPtrConvertResult FormatConvertImpl(const wchar_t* v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); // This overload is needed to disambiguate, since `nullptr` could match either // of the other overloads equally well. StringPtrConvertResult FormatConvertImpl(std::nullptr_t, FormatConversionSpecImpl conv, FormatSinkImpl* sink); template ::value>::type* = nullptr> StringConvertResult FormatConvertImpl(const AbslCord& value, FormatConversionSpecImpl conv, FormatSinkImpl* sink) { bool is_left = conv.has_left_flag(); size_t space_remaining = 0; int width = conv.width(); if (width >= 0) space_remaining = static_cast(width); size_t to_write = value.size(); int precision = conv.precision(); if (precision >= 0) to_write = (std::min)(to_write, static_cast(precision)); space_remaining = Excess(to_write, space_remaining); if (space_remaining > 0 && !is_left) sink->Append(space_remaining, ' '); for (string_view piece : value.Chunks()) { if (piece.size() > to_write) { piece.remove_suffix(piece.size() - to_write); to_write = 0; } else { to_write -= piece.size(); } sink->Append(piece); if (to_write == 0) { break; } } if (space_remaining > 0 && is_left) sink->Append(space_remaining, ' '); return {true}; } bool ConvertBoolArg(bool v, FormatSinkImpl* sink); // Floats. FloatingConvertResult FormatConvertImpl(float v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); FloatingConvertResult FormatConvertImpl(double v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); FloatingConvertResult FormatConvertImpl(long double v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); // Chars. CharConvertResult FormatConvertImpl(char v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); CharConvertResult FormatConvertImpl(wchar_t v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); // Ints. IntegralConvertResult FormatConvertImpl(signed char v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(unsigned char v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(short v, // NOLINT FormatConversionSpecImpl conv, FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(unsigned short v, // NOLINT FormatConversionSpecImpl conv, FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(int v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(unsigned v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(long v, // NOLINT FormatConversionSpecImpl conv, FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(unsigned long v, // NOLINT FormatConversionSpecImpl conv, FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(long long v, // NOLINT FormatConversionSpecImpl conv, FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(unsigned long long v, // NOLINT FormatConversionSpecImpl conv, FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(int128 v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(uint128 v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); // This function needs to be a template due to ambiguity regarding type // conversions. template ::value, int> = 0> IntegralConvertResult FormatConvertImpl(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink) { if (conv.conversion_char() == FormatConversionCharInternal::v) { return {ConvertBoolArg(v, sink)}; } return FormatConvertImpl(static_cast(v), conv, sink); } // We provide this function to help the checker, but it is never defined. // FormatArgImpl will use the underlying Convert functions instead. template typename std::enable_if::value && !HasUserDefinedConvert::value && !HasAbslStringify::value, IntegralConvertResult>::type FormatConvertImpl(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); template StringConvertResult FormatConvertImpl(const StreamedWrapper& v, FormatConversionSpecImpl conv, FormatSinkImpl* out) { std::ostringstream oss; oss << v.v_; if (!oss) return {false}; return str_format_internal::FormatConvertImpl(oss.str(), conv, out); } // Use templates and dependent types to delay evaluation of the function // until after FormatCountCapture is fully defined. struct FormatCountCaptureHelper { template static ArgConvertResult ConvertHelper( const FormatCountCapture& v, FormatConversionSpecImpl conv, FormatSinkImpl* sink) { const absl::enable_if_t& v2 = v; if (conv.conversion_char() != str_format_internal::FormatConversionCharInternal::n) { return {false}; } *v2.p_ = static_cast(sink->size()); return {true}; } }; template ArgConvertResult FormatConvertImpl( const FormatCountCapture& v, FormatConversionSpecImpl conv, FormatSinkImpl* sink) { return FormatCountCaptureHelper::ConvertHelper(v, conv, sink); } // Helper friend struct to hide implementation details from the public API of // FormatArgImpl. struct FormatArgImplFriend { template static bool ToInt(Arg arg, int* out) { // A value initialized FormatConversionSpecImpl has a `none` conv, which // tells the dispatcher to run the `int` conversion. return arg.dispatcher_(arg.data_, {}, out); } template static bool Convert(Arg arg, FormatConversionSpecImpl conv, FormatSinkImpl* out) { return arg.dispatcher_(arg.data_, conv, out); } template static typename Arg::Dispatcher GetVTablePtrForTest(Arg arg) { return arg.dispatcher_; } }; template constexpr FormatConversionCharSet ArgumentToConv() { using ConvResult = decltype(str_format_internal::FormatConvertImpl( std::declval(), std::declval(), std::declval())); return absl::str_format_internal::ExtractCharSet(ConvResult{}); } // A type-erased handle to a format argument. class FormatArgImpl { private: enum { kInlinedSpace = 8 }; using VoidPtr = str_format_internal::VoidPtr; union Data { const void* ptr; const volatile void* volatile_ptr; char buf[kInlinedSpace]; }; using Dispatcher = bool (*)(Data, FormatConversionSpecImpl, void* out); template struct store_by_value : std::integral_constant::value || std::is_floating_point::value || std::is_pointer::value || std::is_same::value)> {}; enum StoragePolicy { ByPointer, ByVolatilePointer, ByValue }; template struct storage_policy : std::integral_constant::value ? ByVolatilePointer : (store_by_value::value ? ByValue : ByPointer))> { }; // To reduce the number of vtables we will decay values before hand. // Anything with a user-defined Convert will get its own vtable. // For everything else: // - Decay char* and char arrays into `const char*` // - Decay wchar_t* and wchar_t arrays into `const wchar_t*` // - Decay any other pointer to `const void*` // - Decay all enums to the integral promotion of their underlying type. // - Decay function pointers to void*. template struct DecayType { static constexpr bool kHasUserDefined = str_format_internal::HasUserDefinedConvert::value || HasAbslStringify::value; using type = typename std::conditional< !kHasUserDefined && std::is_convertible::value, const char*, typename std::conditional< !kHasUserDefined && std::is_convertible::value, const wchar_t*, typename std::conditional< !kHasUserDefined && std::is_convertible::value, VoidPtr, const T&>::type>::type>::type; }; template struct DecayType< T, typename std::enable_if< !str_format_internal::HasUserDefinedConvert::value && !HasAbslStringify::value && std::is_enum::value>::type> { using type = decltype(+typename std::underlying_type::type()); }; public: template explicit FormatArgImpl(const T& value) { using D = typename DecayType::type; static_assert( std::is_same::value || storage_policy::value == ByValue, "Decayed types must be stored by value"); Init(static_cast(value)); } private: friend struct str_format_internal::FormatArgImplFriend; template ::value> struct Manager; template struct Manager { static Data SetValue(const T& value) { Data data; data.ptr = std::addressof(value); return data; } static const T& Value(Data arg) { return *static_cast(arg.ptr); } }; template struct Manager { static Data SetValue(const T& value) { Data data; data.volatile_ptr = &value; return data; } static const T& Value(Data arg) { return *static_cast(arg.volatile_ptr); } }; template struct Manager { static Data SetValue(const T& value) { Data data; memcpy(data.buf, &value, sizeof(value)); return data; } static T Value(Data arg) { T value; memcpy(&value, arg.buf, sizeof(T)); return value; } }; template void Init(const T& value) { data_ = Manager::SetValue(value); dispatcher_ = &Dispatch; } template static int ToIntVal(const T& val) { using CommonType = typename std::conditional::value, int64_t, uint64_t>::type; if (static_cast(val) > static_cast((std::numeric_limits::max)())) { return (std::numeric_limits::max)(); } else if (std::is_signed::value && static_cast(val) < static_cast((std::numeric_limits::min)())) { return (std::numeric_limits::min)(); } return static_cast(val); } template static bool ToInt(Data arg, int* out, std::true_type /* is_integral */, std::false_type) { *out = ToIntVal(Manager::Value(arg)); return true; } template static bool ToInt(Data arg, int* out, std::false_type, std::true_type /* is_enum */) { *out = ToIntVal(static_cast::type>( Manager::Value(arg))); return true; } template static bool ToInt(Data, int*, std::false_type, std::false_type) { return false; } template static bool Dispatch(Data arg, FormatConversionSpecImpl spec, void* out) { // A `none` conv indicates that we want the `int` conversion. if (ABSL_PREDICT_FALSE(spec.conversion_char() == FormatConversionCharInternal::kNone)) { return ToInt(arg, static_cast(out), std::is_integral(), std::is_enum()); } if (ABSL_PREDICT_FALSE(!Contains(ArgumentToConv(), spec.conversion_char()))) { return false; } return str_format_internal::FormatConvertImpl( Manager::Value(arg), spec, static_cast(out)) .value; } Data data_; Dispatcher dispatcher_; }; #define ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(T, E) \ E template bool FormatArgImpl::Dispatch(Data, FormatConversionSpecImpl, \ void*) #define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_NO_WSTRING_VIEW_(...) \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(str_format_internal::VoidPtr, \ __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(bool, __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(char, __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(signed char, __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned char, __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(short, __VA_ARGS__); /* NOLINT */ \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned short, /* NOLINT */ \ __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(int, __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned int, __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long, __VA_ARGS__); /* NOLINT */ \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned long, /* NOLINT */ \ __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long long, /* NOLINT */ \ __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned long long, /* NOLINT */ \ __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(int128, __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(uint128, __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(float, __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(double, __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long double, __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(const char*, __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(std::string, __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(string_view, __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(const wchar_t*, __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(std::wstring, __VA_ARGS__) #if defined(ABSL_HAVE_STD_STRING_VIEW) #define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(...) \ ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_NO_WSTRING_VIEW_( \ __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(std::wstring_view, __VA_ARGS__) #else #define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(...) \ ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_NO_WSTRING_VIEW_(__VA_ARGS__) #endif ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(extern); } // namespace str_format_internal ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_