зеркало из https://github.com/microsoft/STL.git
1265 строки
52 KiB
C++
1265 строки
52 KiB
C++
// __msvc_ranges_tuple_formatter.hpp internal header
|
|
|
|
// Copyright (c) Microsoft Corporation.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
// NOTE:
|
|
// The contents of this header are derived in part from libfmt under the following license:
|
|
|
|
// Copyright (c) 2012 - present, Victor Zverovich
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
// --- Optional exception to the license ---
|
|
//
|
|
// As an exception, if, as a result of your compiling your source code, portions
|
|
// of this Software are embedded into a machine-executable object form of such
|
|
// source code, you may redistribute such embedded portions in such object form
|
|
// without including the above copyright and permission notices.
|
|
|
|
#ifndef __MSVC_RANGES_TUPLE_FORMATTER_HPP
|
|
#define __MSVC_RANGES_TUPLE_FORMATTER_HPP
|
|
#include <yvals_core.h>
|
|
#if _STL_COMPILER_PREPROCESSOR
|
|
|
|
#if !_HAS_CXX20
|
|
#error The contents of <format> are only available with C++20. (Also, you should not include this internal header.)
|
|
#endif // !_HAS_CXX20
|
|
|
|
#include <__msvc_formatter.hpp>
|
|
#include <__msvc_string_view.hpp>
|
|
#include <bit>
|
|
#include <iterator>
|
|
#include <stdexcept>
|
|
#include <xlocale>
|
|
|
|
#if _HAS_CXX23
|
|
#include <tuple>
|
|
#endif // _HAS_CXX23
|
|
|
|
#pragma pack(push, _CRT_PACKING)
|
|
#pragma warning(push, _STL_WARNING_LEVEL)
|
|
#pragma warning(disable : _STL_DISABLED_WARNINGS)
|
|
_STL_DISABLE_CLANG_WARNINGS
|
|
#pragma push_macro("new")
|
|
#undef new
|
|
|
|
_STD_BEGIN
|
|
_EXPORT_STD template <class, class>
|
|
class vector;
|
|
|
|
template <class _CharT>
|
|
_NODISCARD constexpr const _CharT* _Choose_literal(const char* const _Str, const wchar_t* const _WStr) noexcept {
|
|
if constexpr (is_same_v<_CharT, char>) {
|
|
return _Str;
|
|
} else {
|
|
return _WStr;
|
|
}
|
|
}
|
|
|
|
// _STATICALLY_WIDEN is used by <chrono> since C++20 and by <format> since C++23.
|
|
// It's defined here, so that both headers can use this definition.
|
|
#define _STATICALLY_WIDEN(_CharT, _Literal) (_Choose_literal<_CharT>(_Literal, L##_Literal))
|
|
|
|
_EXPORT_STD class format_error : public runtime_error {
|
|
using runtime_error::runtime_error;
|
|
};
|
|
|
|
[[noreturn]] inline void _Throw_format_error(const char* const _Message) {
|
|
_THROW(format_error{_Message});
|
|
}
|
|
|
|
inline void _You_see_this_error_because_arg_id_is_out_of_range() noexcept {}
|
|
inline void _Invalid_arg_type_for_dynamic_width_or_precision() noexcept {}
|
|
|
|
template <class _CharT>
|
|
class _Compile_time_parse_context;
|
|
|
|
_EXPORT_STD template <class _CharT>
|
|
class basic_format_parse_context {
|
|
public:
|
|
using char_type = _CharT;
|
|
using const_iterator = basic_string_view<_CharT>::const_iterator;
|
|
using iterator = const_iterator;
|
|
|
|
constexpr explicit basic_format_parse_context(
|
|
const basic_string_view<_CharT> _Fmt, const size_t _Num_args_ = 0) noexcept
|
|
: _Format_string(_Fmt), _Num_args(_Num_args_) {}
|
|
|
|
basic_format_parse_context(const basic_format_parse_context&) = delete;
|
|
basic_format_parse_context& operator=(const basic_format_parse_context&) = delete;
|
|
|
|
_NODISCARD constexpr const_iterator begin() const noexcept {
|
|
return _Format_string.begin();
|
|
}
|
|
_NODISCARD constexpr const_iterator end() const noexcept {
|
|
return _Format_string.end();
|
|
}
|
|
_NODISCARD constexpr const _CharT* _Unchecked_begin() const noexcept {
|
|
return _Format_string._Unchecked_begin();
|
|
}
|
|
_NODISCARD constexpr const _CharT* _Unchecked_end() const noexcept {
|
|
return _Format_string._Unchecked_end();
|
|
}
|
|
|
|
constexpr void advance_to(const const_iterator _It) {
|
|
_Adl_verify_range(_It, _Format_string.end());
|
|
_Adl_verify_range(_Format_string.begin(), _It);
|
|
const auto _Diff = static_cast<size_t>(_It._Unwrapped() - _Format_string._Unchecked_begin());
|
|
_Format_string.remove_prefix(_Diff);
|
|
}
|
|
|
|
// While the standard presents an exposition-only enum value for
|
|
// the indexing mode (manual, automatic, or unknown) we use _Next_arg_id to indicate it.
|
|
// _Next_arg_id > 0 means automatic
|
|
// _Next_arg_id == 0 means unknown
|
|
// _Next_arg_id < 0 means manual
|
|
_NODISCARD constexpr size_t next_arg_id() {
|
|
if (_Next_arg_id < 0) {
|
|
_Throw_format_error("Can not switch from manual to automatic indexing");
|
|
}
|
|
|
|
if (_STD is_constant_evaluated()) {
|
|
if (static_cast<size_t>(_Next_arg_id) >= _Num_args) {
|
|
_You_see_this_error_because_arg_id_is_out_of_range();
|
|
}
|
|
}
|
|
|
|
return static_cast<size_t>(_Next_arg_id++);
|
|
}
|
|
|
|
constexpr void check_arg_id(const size_t _Id) {
|
|
if (_STD is_constant_evaluated()) {
|
|
if (_Id >= _Num_args) {
|
|
_You_see_this_error_because_arg_id_is_out_of_range();
|
|
}
|
|
}
|
|
|
|
if (_Next_arg_id > 0) {
|
|
_Throw_format_error("Can not switch from automatic to manual indexing");
|
|
}
|
|
_Next_arg_id = -1;
|
|
}
|
|
|
|
constexpr void _Check_dynamic_spec_integral(const size_t _Idx) noexcept {
|
|
if (_STD is_constant_evaluated()) {
|
|
// This downcast might seem UB-prone, but since it only happens at compile-time,
|
|
// the compiler will produce an error if it is invalid.
|
|
auto& _Ctx = static_cast<_Compile_time_parse_context<_CharT>&>(*this);
|
|
|
|
_STL_INTERNAL_CHECK(_Ctx._Arg_type[_Idx] != _Basic_format_arg_type::_None);
|
|
if (_Ctx._Arg_type[_Idx] > _Basic_format_arg_type::_ULong_long_type) {
|
|
_Invalid_arg_type_for_dynamic_width_or_precision();
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
basic_string_view<_CharT> _Format_string;
|
|
size_t _Num_args;
|
|
// The standard says this is size_t, however we use ptrdiff_t to save some space
|
|
// by not having to store the indexing mode. Above is a more detailed explanation
|
|
// of how this works.
|
|
ptrdiff_t _Next_arg_id = 0;
|
|
};
|
|
|
|
template <class _CharT>
|
|
class _Compile_time_parse_context : public basic_format_parse_context<_CharT> {
|
|
friend basic_format_parse_context<_CharT>;
|
|
|
|
public:
|
|
constexpr _Compile_time_parse_context(const basic_string_view<_CharT> _Fmt, const size_t _Num_args,
|
|
const _Basic_format_arg_type* const _Arg_type_) noexcept
|
|
: basic_format_parse_context<_CharT>(_Fmt, _Num_args), _Arg_type(_Arg_type_) {}
|
|
|
|
private:
|
|
const _Basic_format_arg_type* const _Arg_type;
|
|
};
|
|
|
|
_EXPORT_STD using format_parse_context = basic_format_parse_context<char>;
|
|
_EXPORT_STD using wformat_parse_context = basic_format_parse_context<wchar_t>;
|
|
|
|
template <class _Ty, class _Context, class _Formatter = _Context::template formatter_type<remove_const_t<_Ty>>>
|
|
concept _Formattable_with = semiregular<_Formatter>
|
|
&& requires(_Formatter& __f, const _Formatter& __cf, _Ty&& __t, _Context __fc,
|
|
basic_format_parse_context<typename _Context::char_type> __pc) {
|
|
{ __f.parse(__pc) } -> same_as<typename decltype(__pc)::iterator>;
|
|
{ __cf.format(__t, __fc) } -> same_as<typename _Context::iterator>;
|
|
};
|
|
|
|
template <class _Context>
|
|
struct _Format_arg_traits {
|
|
using _Char_type = _Context::char_type;
|
|
|
|
// Function template _Type_eraser mirrors the type dispatching mechanism in the construction of basic_format_arg
|
|
// (N4950 [format.arg]). They determine the mapping from "raw" to "erased" argument type for _Format_arg_store.
|
|
template <class _Ty>
|
|
static auto _Type_eraser();
|
|
|
|
template <class _Ty>
|
|
using _Storage_type = decltype(_Type_eraser<remove_reference_t<_Ty>>());
|
|
|
|
template <class _Ty>
|
|
static constexpr size_t _Storage_size = sizeof(_Storage_type<_Ty>);
|
|
};
|
|
|
|
_EXPORT_STD template <class _Context>
|
|
class basic_format_args;
|
|
|
|
_FMT_P2286_BEGIN
|
|
template <class _CharT>
|
|
struct _Format_handler;
|
|
_FMT_P2286_END
|
|
|
|
_EXPORT_STD template <class _Context>
|
|
class basic_format_arg {
|
|
public:
|
|
using _CharType = _Context::char_type;
|
|
|
|
class handle {
|
|
private:
|
|
const void* _Ptr;
|
|
void(__cdecl* _Format)(basic_format_parse_context<_CharType>& _Parse_ctx, _Context& _Format_ctx, const void*);
|
|
|
|
template <class _Ty>
|
|
explicit handle(_Ty& _Val) noexcept
|
|
: _Ptr(_STD addressof(_Val)), _Format([](basic_format_parse_context<_CharType>& _Parse_ctx,
|
|
_Context& _Format_ctx, const void* _Ptr) _STATIC_CALL_OPERATOR {
|
|
using _Td = remove_const_t<_Ty>;
|
|
// doesn't drop const-qualifier per an unnumbered LWG issue
|
|
using _Tq = conditional_t<_Formattable_with<const _Ty, _Context>, const _Ty, _Ty>;
|
|
_STL_INTERNAL_STATIC_ASSERT(_Formattable_with<_Tq, _Context>);
|
|
|
|
typename _Context::template formatter_type<_Td> _Formatter;
|
|
_Parse_ctx.advance_to(_Formatter.parse(_Parse_ctx));
|
|
_Format_ctx.advance_to(
|
|
_Formatter.format(*const_cast<_Tq*>(static_cast<const _Td*>(_Ptr)), _Format_ctx));
|
|
}) {}
|
|
|
|
public:
|
|
void format(basic_format_parse_context<_CharType>& _Parse_ctx, _Context& _Format_ctx) const {
|
|
_Format(_Parse_ctx, _Format_ctx, _Ptr);
|
|
}
|
|
|
|
template <class _Ty>
|
|
_NODISCARD static handle _Make_from(_Ty& _Val) noexcept {
|
|
return handle{_Val};
|
|
}
|
|
};
|
|
|
|
#if defined(__clang__) || defined(__EDG__) // TRANSITION, LLVM-81774 (Clang), VSO-1956558 (EDG)
|
|
basic_format_arg() noexcept : _Active_state(_Basic_format_arg_type::_None), _No_state() {}
|
|
#else // ^^^ workaround / no workaround vvv
|
|
basic_format_arg() noexcept = default;
|
|
#endif // ^^^ no workaround ^^^
|
|
|
|
explicit operator bool() const noexcept {
|
|
return _Active_state != _Basic_format_arg_type::_None;
|
|
}
|
|
|
|
// Function template _Make_from mirrors the exposition-only single-argument constructor template of
|
|
// basic_format_arg (N4950 [format.arg]).
|
|
template <_Formattable_with<_Context> _Ty>
|
|
_NODISCARD static basic_format_arg _Make_from(_Ty& _Val) noexcept {
|
|
using _Erased_type = _Format_arg_traits<_Context>::template _Storage_type<_Ty>;
|
|
if constexpr (is_same_v<remove_const_t<_Ty>, char> && is_same_v<_CharType, wchar_t>) {
|
|
return basic_format_arg(static_cast<_Erased_type>(static_cast<unsigned char>(_Val)));
|
|
}
|
|
#if !_HAS_CXX23
|
|
else if constexpr (is_same_v<_Erased_type, basic_string_view<_CharType>>) {
|
|
return basic_format_arg(_Erased_type{_Val.data(), _Val.size()});
|
|
}
|
|
#endif // !_HAS_CXX23
|
|
else {
|
|
return basic_format_arg(static_cast<_Erased_type>(_Val));
|
|
}
|
|
}
|
|
|
|
template <class _Visitor>
|
|
decltype(auto) _Visit(_Visitor&& _Vis) {
|
|
switch (_Active_state) {
|
|
case _Basic_format_arg_type::_None:
|
|
return _STD forward<_Visitor>(_Vis)(_No_state);
|
|
case _Basic_format_arg_type::_Int_type:
|
|
return _STD forward<_Visitor>(_Vis)(_Int_state);
|
|
case _Basic_format_arg_type::_UInt_type:
|
|
return _STD forward<_Visitor>(_Vis)(_UInt_state);
|
|
case _Basic_format_arg_type::_Long_long_type:
|
|
return _STD forward<_Visitor>(_Vis)(_Long_long_state);
|
|
case _Basic_format_arg_type::_ULong_long_type:
|
|
return _STD forward<_Visitor>(_Vis)(_ULong_long_state);
|
|
case _Basic_format_arg_type::_Bool_type:
|
|
return _STD forward<_Visitor>(_Vis)(_Bool_state);
|
|
case _Basic_format_arg_type::_Char_type:
|
|
return _STD forward<_Visitor>(_Vis)(_Char_state);
|
|
case _Basic_format_arg_type::_Float_type:
|
|
return _STD forward<_Visitor>(_Vis)(_Float_state);
|
|
case _Basic_format_arg_type::_Double_type:
|
|
return _STD forward<_Visitor>(_Vis)(_Double_state);
|
|
case _Basic_format_arg_type::_Long_double_type:
|
|
return _STD forward<_Visitor>(_Vis)(_Long_double_state);
|
|
case _Basic_format_arg_type::_Pointer_type:
|
|
return _STD forward<_Visitor>(_Vis)(_Pointer_state);
|
|
case _Basic_format_arg_type::_CString_type:
|
|
return _STD forward<_Visitor>(_Vis)(_CString_state);
|
|
case _Basic_format_arg_type::_String_type:
|
|
return _STD forward<_Visitor>(_Vis)(_String_state);
|
|
case _Basic_format_arg_type::_Custom_type:
|
|
return _STD forward<_Visitor>(_Vis)(_Custom_state);
|
|
default:
|
|
_STL_VERIFY(false, "basic_format_arg is in impossible state");
|
|
int _Dummy{};
|
|
return _STD forward<_Visitor>(_Vis)(_Dummy);
|
|
}
|
|
}
|
|
|
|
private:
|
|
friend basic_format_args<_Context>;
|
|
friend _Format_handler<_CharType>;
|
|
friend _Format_arg_traits<_Context>;
|
|
|
|
explicit basic_format_arg(const int _Val) noexcept
|
|
: _Active_state(_Basic_format_arg_type::_Int_type), _Int_state(_Val) {}
|
|
explicit basic_format_arg(const unsigned int _Val) noexcept
|
|
: _Active_state(_Basic_format_arg_type::_UInt_type), _UInt_state(_Val) {}
|
|
explicit basic_format_arg(const long long _Val) noexcept
|
|
: _Active_state(_Basic_format_arg_type::_Long_long_type), _Long_long_state(_Val) {}
|
|
explicit basic_format_arg(const unsigned long long _Val) noexcept
|
|
: _Active_state(_Basic_format_arg_type::_ULong_long_type), _ULong_long_state(_Val) {}
|
|
explicit basic_format_arg(const bool _Val) noexcept
|
|
: _Active_state(_Basic_format_arg_type::_Bool_type), _Bool_state(_Val) {}
|
|
explicit basic_format_arg(const _CharType _Val) noexcept
|
|
: _Active_state(_Basic_format_arg_type::_Char_type), _Char_state(_Val) {}
|
|
explicit basic_format_arg(const float _Val) noexcept
|
|
: _Active_state(_Basic_format_arg_type::_Float_type), _Float_state(_Val) {}
|
|
explicit basic_format_arg(const double _Val) noexcept
|
|
: _Active_state(_Basic_format_arg_type::_Double_type), _Double_state(_Val) {}
|
|
explicit basic_format_arg(const long double _Val) noexcept
|
|
: _Active_state(_Basic_format_arg_type::_Long_double_type), _Long_double_state(_Val) {}
|
|
explicit basic_format_arg(const void* _Val) noexcept
|
|
: _Active_state(_Basic_format_arg_type::_Pointer_type), _Pointer_state(_Val) {}
|
|
explicit basic_format_arg(const _CharType* _Val) noexcept
|
|
: _Active_state(_Basic_format_arg_type::_CString_type), _CString_state(_Val) {}
|
|
explicit basic_format_arg(const basic_string_view<_CharType> _Val) noexcept
|
|
: _Active_state(_Basic_format_arg_type::_String_type), _String_state(_Val) {}
|
|
explicit basic_format_arg(const handle _Val) noexcept
|
|
: _Active_state(_Basic_format_arg_type::_Custom_type), _Custom_state(_Val) {}
|
|
|
|
_Basic_format_arg_type _Active_state = _Basic_format_arg_type::_None;
|
|
union {
|
|
monostate _No_state = monostate{};
|
|
int _Int_state;
|
|
unsigned int _UInt_state;
|
|
long long _Long_long_state;
|
|
unsigned long long _ULong_long_state;
|
|
bool _Bool_state;
|
|
_CharType _Char_state;
|
|
float _Float_state;
|
|
double _Double_state;
|
|
long double _Long_double_state;
|
|
const void* _Pointer_state;
|
|
const _CharType* _CString_state;
|
|
basic_string_view<_CharType> _String_state;
|
|
handle _Custom_state;
|
|
};
|
|
};
|
|
|
|
template <class _Ty, class _CharT>
|
|
constexpr bool _Is_basic_string_like_for = false;
|
|
|
|
template <class _CharT, class _Traits, class _Alloc>
|
|
constexpr bool _Is_basic_string_like_for<basic_string<_CharT, _Traits, _Alloc>, _CharT> = true;
|
|
|
|
template <class _CharT, class _Traits>
|
|
constexpr bool _Is_basic_string_like_for<basic_string_view<_CharT, _Traits>, _CharT> = true;
|
|
|
|
template <class _Context>
|
|
template <class _Ty>
|
|
auto _Format_arg_traits<_Context>::_Type_eraser() {
|
|
using _Td = remove_const_t<_Ty>;
|
|
// See N4950 [format.arg]/6
|
|
if constexpr (is_same_v<_Td, bool>) {
|
|
return bool{};
|
|
} else if constexpr (is_same_v<_Td, _Char_type>) {
|
|
return _Char_type{};
|
|
} else if constexpr (is_same_v<_Td, char> && is_same_v<_Char_type, wchar_t>) {
|
|
return _Char_type{};
|
|
} else if constexpr (signed_integral<_Td> && sizeof(_Td) <= sizeof(int)) {
|
|
return int{};
|
|
} else if constexpr (unsigned_integral<_Td> && sizeof(_Td) <= sizeof(unsigned int)) {
|
|
return static_cast<unsigned int>(42);
|
|
} else if constexpr (signed_integral<_Td> && sizeof(_Td) <= sizeof(long long)) {
|
|
return static_cast<long long>(42);
|
|
} else if constexpr (unsigned_integral<_Td> && sizeof(_Td) <= sizeof(unsigned long long)) {
|
|
return static_cast<unsigned long long>(42);
|
|
} else if constexpr (is_same_v<_Td, float>) {
|
|
return float{};
|
|
} else if constexpr (is_same_v<_Td, double>) {
|
|
return double{};
|
|
} else if constexpr (is_same_v<_Td, long double>) {
|
|
return static_cast<long double>(42);
|
|
} else if constexpr (_Is_basic_string_like_for<_Td, _Char_type>) {
|
|
return basic_string_view<_Char_type>{};
|
|
} else if constexpr (_Is_any_of_v<decay_t<_Td>, _Char_type*, const _Char_type*>) {
|
|
return static_cast<const _Char_type*>(nullptr);
|
|
} else if constexpr (is_void_v<remove_pointer_t<_Td>> || is_same_v<_Td, nullptr_t>) {
|
|
return static_cast<const void*>(nullptr);
|
|
} else {
|
|
int _Dummy{};
|
|
return basic_format_arg<_Context>::handle::_Make_from(_Dummy);
|
|
}
|
|
}
|
|
|
|
template <class _Context, class _Ty>
|
|
_NODISCARD consteval _Basic_format_arg_type _Get_format_arg_type() noexcept {
|
|
using _CharType = _Context::char_type;
|
|
using _Erased_type = _Format_arg_traits<_Context>::template _Storage_type<_Ty>;
|
|
|
|
if constexpr (is_same_v<_Erased_type, bool>) {
|
|
return _Basic_format_arg_type::_Bool_type;
|
|
} else if constexpr (is_same_v<_Erased_type, _CharType>) {
|
|
return _Basic_format_arg_type::_Char_type;
|
|
} else if constexpr (is_same_v<_Erased_type, int>) {
|
|
return _Basic_format_arg_type::_Int_type;
|
|
} else if constexpr (is_same_v<_Erased_type, unsigned int>) {
|
|
return _Basic_format_arg_type::_UInt_type;
|
|
} else if constexpr (is_same_v<_Erased_type, long long>) {
|
|
return _Basic_format_arg_type::_Long_long_type;
|
|
} else if constexpr (is_same_v<_Erased_type, unsigned long long>) {
|
|
return _Basic_format_arg_type::_ULong_long_type;
|
|
} else if constexpr (is_same_v<_Erased_type, float>) {
|
|
return _Basic_format_arg_type::_Float_type;
|
|
} else if constexpr (is_same_v<_Erased_type, double>) {
|
|
return _Basic_format_arg_type::_Double_type;
|
|
} else if constexpr (is_same_v<_Erased_type, long double>) {
|
|
return _Basic_format_arg_type::_Long_double_type;
|
|
} else if constexpr (is_same_v<_Erased_type, const void*>) {
|
|
return _Basic_format_arg_type::_Pointer_type;
|
|
} else if constexpr (is_same_v<_Erased_type, const _CharType*>) {
|
|
return _Basic_format_arg_type::_CString_type;
|
|
} else if constexpr (is_same_v<_Erased_type, basic_string_view<_CharType>>) {
|
|
return _Basic_format_arg_type::_String_type;
|
|
} else {
|
|
_STL_INTERNAL_STATIC_ASSERT(is_same_v<_Erased_type, typename basic_format_arg<_Context>::handle>);
|
|
return _Basic_format_arg_type::_Custom_type;
|
|
}
|
|
}
|
|
|
|
_EXPORT_STD template <class _Visitor, class _Context>
|
|
decltype(auto) visit_format_arg(_Visitor&& _Vis, basic_format_arg<_Context> _Arg) {
|
|
return _Arg._Visit(_STD forward<_Visitor>(_Vis));
|
|
}
|
|
|
|
struct _Format_arg_index {
|
|
// TRANSITION, Should be templated on number of arguments for even less storage
|
|
|
|
constexpr _Format_arg_index() = default;
|
|
constexpr explicit _Format_arg_index(const size_t _Index_) noexcept : _Index(_Index_) {
|
|
_Type(_Basic_format_arg_type::_None);
|
|
}
|
|
|
|
_NODISCARD constexpr _Basic_format_arg_type _Type() const noexcept {
|
|
return static_cast<_Basic_format_arg_type>(_Type_);
|
|
}
|
|
|
|
constexpr void _Type(_Basic_format_arg_type _Val) noexcept {
|
|
_Type_ = static_cast<size_t>(_Val);
|
|
}
|
|
|
|
size_t _Index : (sizeof(size_t) * 8 - 4){};
|
|
size_t _Type_ : 4 {};
|
|
};
|
|
|
|
template <class _Context, class... _Args>
|
|
class _Format_arg_store {
|
|
private:
|
|
using _CharType = _Context::char_type;
|
|
using _Traits = _Format_arg_traits<_Context>;
|
|
|
|
friend basic_format_args<_Context>;
|
|
|
|
static constexpr size_t _Num_args = sizeof...(_Args);
|
|
static constexpr size_t _Storage_length = (_Traits::template _Storage_size<_Args> + ...);
|
|
|
|
// The actual storage representation: _Num_args offsets into _Storage, followed immediately by the untyped
|
|
// _Storage which holds copies of the object representations of arguments (with no regard for alignment).
|
|
// These must be allocated consecutively, since basic_format_args thinks it can store a pointer to
|
|
// _Index_array and use arithmetic to access the bytes of _Storage.
|
|
_Format_arg_index _Index_array[_Num_args];
|
|
unsigned char _Storage[_Storage_length];
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable : 6386) // Buffer overrun while writing to '%s' ...
|
|
template <class _Ty>
|
|
void _Store_impl(
|
|
const size_t _Arg_index, const _Basic_format_arg_type _Arg_type, const type_identity_t<_Ty>& _Val) noexcept {
|
|
_STL_INTERNAL_CHECK(_Arg_index < _Num_args);
|
|
|
|
const auto _Store_index = _Index_array[_Arg_index]._Index;
|
|
|
|
_CSTD memcpy(_Storage + _Store_index, _STD addressof(_Val), sizeof(_Ty));
|
|
_Index_array[_Arg_index]._Type(_Arg_type);
|
|
if (_Arg_index + 1 < _Num_args) {
|
|
// Set the starting index of the next arg, as that is dynamic, must be called with increasing index
|
|
_Index_array[_Arg_index + 1] = _Format_arg_index{_Store_index + sizeof(_Ty)};
|
|
}
|
|
}
|
|
#pragma warning(pop)
|
|
|
|
template <class _Ty>
|
|
void _Store(const size_t _Arg_index, _Ty&& _Val) noexcept {
|
|
constexpr _Basic_format_arg_type _Arg_type = _STD _Get_format_arg_type<_Context, _Ty>();
|
|
using _Erased_type = _Traits::template _Storage_type<_Ty>;
|
|
|
|
if constexpr (is_same_v<remove_const_t<remove_reference_t<_Ty>>, char> && is_same_v<_CharType, wchar_t>) {
|
|
_Store_impl<_Erased_type>(
|
|
_Arg_index, _Arg_type, static_cast<_Erased_type>(static_cast<unsigned char>(_Val)));
|
|
} else if constexpr (is_same_v<_Erased_type, typename basic_format_arg<_Context>::handle>) {
|
|
_Store_impl<_Erased_type>(_Arg_index, _Arg_type, _Erased_type::_Make_from(_Val));
|
|
}
|
|
#if !_HAS_CXX23
|
|
// Workaround towards N4950 [format.arg]/6.8 in C++20
|
|
else if constexpr (is_same_v<_Erased_type, basic_string_view<_CharType>>) {
|
|
_Store_impl<_Erased_type>(_Arg_index, _Arg_type, _Erased_type{_Val.data(), _Val.size()});
|
|
}
|
|
#endif // !_HAS_CXX23
|
|
else {
|
|
_Store_impl<_Erased_type>(_Arg_index, _Arg_type, static_cast<_Erased_type>(_Val));
|
|
}
|
|
}
|
|
|
|
public:
|
|
_Format_arg_store(_Args&... _Vals) noexcept {
|
|
_Index_array[0] = {};
|
|
size_t _Arg_index = 0;
|
|
(_Store(_Arg_index++, _Vals), ...);
|
|
}
|
|
};
|
|
|
|
template <class _Context>
|
|
class _Format_arg_store<_Context> {};
|
|
|
|
_EXPORT_STD template <class _Context>
|
|
class basic_format_args {
|
|
public:
|
|
basic_format_args(const _Format_arg_store<_Context>&) noexcept {}
|
|
|
|
template <class... _Args>
|
|
basic_format_args(const _Format_arg_store<_Context, _Args...>& _Store) noexcept
|
|
: _Num_args(sizeof...(_Args)), _Index_array(_Store._Index_array) {}
|
|
|
|
_NODISCARD basic_format_arg<_Context> get(const size_t _Index) const noexcept {
|
|
if (_Index >= _Num_args) {
|
|
return basic_format_arg<_Context>{};
|
|
}
|
|
|
|
using _CharType = _Context::char_type;
|
|
// The explanatory comment in _Format_arg_store explains how the following works.
|
|
const auto _Packed_index = _Index_array[_Index];
|
|
const auto _Arg_storage =
|
|
reinterpret_cast<const unsigned char*>(_Index_array + _Num_args) + _Packed_index._Index;
|
|
|
|
switch (_Packed_index._Type()) {
|
|
case _Basic_format_arg_type::_None:
|
|
default:
|
|
_STL_ASSERT(false, "Invalid basic_format_arg type");
|
|
return basic_format_arg<_Context>{};
|
|
case _Basic_format_arg_type::_Int_type:
|
|
return basic_format_arg<_Context>{_Get_value_from_memory<int>(_Arg_storage)};
|
|
case _Basic_format_arg_type::_UInt_type:
|
|
return basic_format_arg<_Context>{_Get_value_from_memory<unsigned int>(_Arg_storage)};
|
|
case _Basic_format_arg_type::_Long_long_type:
|
|
return basic_format_arg<_Context>{_Get_value_from_memory<long long>(_Arg_storage)};
|
|
case _Basic_format_arg_type::_ULong_long_type:
|
|
return basic_format_arg<_Context>{_Get_value_from_memory<unsigned long long>(_Arg_storage)};
|
|
case _Basic_format_arg_type::_Bool_type:
|
|
return basic_format_arg<_Context>{_Get_value_from_memory<bool>(_Arg_storage)};
|
|
case _Basic_format_arg_type::_Char_type:
|
|
return basic_format_arg<_Context>{_Get_value_from_memory<_CharType>(_Arg_storage)};
|
|
case _Basic_format_arg_type::_Float_type:
|
|
return basic_format_arg<_Context>{_Get_value_from_memory<float>(_Arg_storage)};
|
|
case _Basic_format_arg_type::_Double_type:
|
|
return basic_format_arg<_Context>{_Get_value_from_memory<double>(_Arg_storage)};
|
|
case _Basic_format_arg_type::_Long_double_type:
|
|
return basic_format_arg<_Context>{_Get_value_from_memory<long double>(_Arg_storage)};
|
|
case _Basic_format_arg_type::_Pointer_type:
|
|
return basic_format_arg<_Context>{_Get_value_from_memory<const void*>(_Arg_storage)};
|
|
case _Basic_format_arg_type::_CString_type:
|
|
return basic_format_arg<_Context>{_Get_value_from_memory<const _CharType*>(_Arg_storage)};
|
|
case _Basic_format_arg_type::_String_type:
|
|
return basic_format_arg<_Context>{_Get_value_from_memory<basic_string_view<_CharType>>(_Arg_storage)};
|
|
case _Basic_format_arg_type::_Custom_type:
|
|
return basic_format_arg<_Context>{
|
|
_Get_value_from_memory<typename basic_format_arg<_Context>::handle>(_Arg_storage)};
|
|
}
|
|
}
|
|
|
|
_NODISCARD size_t _Estimate_required_capacity() const noexcept {
|
|
using _CharType = _Context::char_type;
|
|
size_t _Result = 0;
|
|
|
|
for (size_t _Idx = 0; _Idx < _Num_args; ++_Idx) {
|
|
const auto _Packed_index = _Index_array[_Idx];
|
|
const auto _Arg_type = _Packed_index._Type();
|
|
if (_Arg_type == _Basic_format_arg_type::_String_type) {
|
|
const auto _Arg_storage =
|
|
reinterpret_cast<const unsigned char*>(_Index_array + _Num_args) + _Packed_index._Index;
|
|
const auto _View = _Get_value_from_memory<basic_string_view<_CharType>>(_Arg_storage);
|
|
_Result += _View.size();
|
|
} else if (_Arg_type == _Basic_format_arg_type::_CString_type) {
|
|
_Result += 32; // estimate for length of null-terminated strings
|
|
} else {
|
|
_Result += 8; // estimate for length of all other arguments
|
|
}
|
|
}
|
|
return _Result;
|
|
}
|
|
|
|
private:
|
|
template <class _Ty>
|
|
_NODISCARD static auto _Get_value_from_memory(const unsigned char* const _Val) noexcept {
|
|
auto& _Temp = *reinterpret_cast<const unsigned char(*)[sizeof(_Ty)]>(_Val);
|
|
return _STD bit_cast<_Ty>(_Temp);
|
|
}
|
|
|
|
size_t _Num_args = 0;
|
|
const _Format_arg_index* _Index_array = nullptr;
|
|
};
|
|
|
|
template <class _Context, class... _Args>
|
|
basic_format_args(_Format_arg_store<_Context, _Args...>) -> basic_format_args<_Context>;
|
|
|
|
// _Lazy_locale is used instead of a std::locale so that the locale is only
|
|
// constructed when needed, and is never constructed if the format string does not
|
|
// contain locale-sensitive format specifiers. Note that this means that a new locale
|
|
// will be constructed for _every_ locale-sensitive format specifier in the format string,
|
|
// making that case slower than if we had stored a "real" locale in the basic_format_context.
|
|
class _Lazy_locale {
|
|
private:
|
|
const locale* _Loc = nullptr;
|
|
|
|
public:
|
|
_Lazy_locale() = default;
|
|
|
|
explicit _Lazy_locale(const locale& _Loc_) : _Loc(&_Loc_) {}
|
|
|
|
explicit _Lazy_locale(const locale&&) = delete;
|
|
|
|
_NODISCARD locale _Get() const {
|
|
if (_Loc) {
|
|
return *_Loc;
|
|
} else {
|
|
return locale{};
|
|
}
|
|
}
|
|
};
|
|
|
|
_EXPORT_STD template <class _Out, class _CharT>
|
|
requires output_iterator<_Out, const _CharT&>
|
|
class basic_format_context {
|
|
private:
|
|
_Out _OutputIt;
|
|
basic_format_args<basic_format_context> _Args;
|
|
_Lazy_locale _Loc;
|
|
|
|
constexpr basic_format_context(
|
|
_Out&& _OutputIt_, const basic_format_args<basic_format_context>& _Ctx_args, const _Lazy_locale& _Loc_)
|
|
: _OutputIt(_STD move(_OutputIt_)), _Args(_Ctx_args), _Loc(_Loc_) {}
|
|
|
|
basic_format_context(const basic_format_context&) = delete;
|
|
basic_format_context& operator=(const basic_format_context&) = delete;
|
|
|
|
public:
|
|
using iterator = _Out;
|
|
using char_type = _CharT;
|
|
|
|
template <class _Ty>
|
|
using formatter_type = formatter<_Ty, _CharT>;
|
|
|
|
_NODISCARD basic_format_arg<basic_format_context> arg(size_t _Id) const noexcept {
|
|
return _Args.get(_Id);
|
|
}
|
|
_NODISCARD locale locale() {
|
|
return _Loc._Get();
|
|
}
|
|
_NODISCARD iterator out() {
|
|
return _STD move(_OutputIt);
|
|
}
|
|
void advance_to(iterator _It) {
|
|
_OutputIt = _STD move(_It);
|
|
}
|
|
|
|
_NODISCARD const basic_format_args<basic_format_context>& _Get_args() const noexcept {
|
|
return _Args;
|
|
}
|
|
_NODISCARD _Lazy_locale _Get_lazy_locale() const {
|
|
return _Loc;
|
|
}
|
|
|
|
_NODISCARD static constexpr basic_format_context _Make_from(
|
|
_Out _OutputIt_, basic_format_args<basic_format_context> _Ctx_args, const _Lazy_locale& _Loc_) {
|
|
return basic_format_context{_STD move(_OutputIt_), _Ctx_args, _Loc_};
|
|
}
|
|
};
|
|
|
|
template <class _Ty>
|
|
class _Fmt_buffer {
|
|
private:
|
|
_Ty* _Ptr_ = nullptr;
|
|
size_t _Size_ = 0;
|
|
size_t _Capacity_ = 0;
|
|
|
|
protected:
|
|
explicit _Fmt_buffer(const size_t _Size) noexcept : _Size_(_Size), _Capacity_(_Size) {}
|
|
|
|
~_Fmt_buffer() = default;
|
|
|
|
_Fmt_buffer(_Ty* _Data, const size_t _Size, const size_t _Capacity) noexcept
|
|
: _Ptr_(_Data), _Size_(_Size), _Capacity_(_Capacity) {}
|
|
|
|
void _Set(_Ty* _Buf_data, const size_t _Buf_capacity) noexcept {
|
|
_Ptr_ = _Buf_data;
|
|
_Capacity_ = _Buf_capacity;
|
|
}
|
|
|
|
virtual void _Grow(size_t _Capacity) = 0;
|
|
|
|
public:
|
|
using value_type = _Ty;
|
|
|
|
_Fmt_buffer(const _Fmt_buffer&) = delete;
|
|
void operator=(const _Fmt_buffer&) = delete;
|
|
|
|
_NODISCARD _Ty* begin() noexcept {
|
|
return _Ptr_;
|
|
}
|
|
|
|
_NODISCARD _Ty* end() noexcept {
|
|
return _Ptr_ + _Size_;
|
|
}
|
|
|
|
_NODISCARD size_t _Size() const noexcept {
|
|
return _Size_;
|
|
}
|
|
|
|
_NODISCARD size_t _Capacity() const noexcept {
|
|
return _Capacity_;
|
|
}
|
|
|
|
void _Clear() noexcept {
|
|
_Size_ = 0;
|
|
}
|
|
|
|
void _Try_reserve(const size_t _New_capacity) {
|
|
if (_New_capacity > _Capacity_) {
|
|
_Grow(_New_capacity);
|
|
}
|
|
}
|
|
|
|
void push_back(const _Ty _Value) {
|
|
_Try_reserve(_Size_ + 1);
|
|
_Ptr_[_Size_++] = _Value;
|
|
}
|
|
};
|
|
|
|
struct _Fmt_buffer_traits {
|
|
explicit _Fmt_buffer_traits(ptrdiff_t) {}
|
|
|
|
_NODISCARD size_t _Count() const noexcept {
|
|
return 0;
|
|
}
|
|
|
|
_NODISCARD size_t _Limit(const size_t _Size) noexcept {
|
|
return _Size;
|
|
}
|
|
};
|
|
|
|
inline constexpr size_t _Fmt_buffer_size = 256;
|
|
|
|
template <class _OutputIt>
|
|
struct _Fmt_iterator_flush {
|
|
template <class _Ty>
|
|
static _OutputIt _Flush(const _Ty* const _First, const _Ty* const _Last, _OutputIt _Output) {
|
|
return _STD copy(_First, _Last, _STD move(_Output));
|
|
}
|
|
};
|
|
|
|
template <class _Container>
|
|
struct _Back_insert_iterator_container_access : back_insert_iterator<_Container> {
|
|
explicit _Back_insert_iterator_container_access(back_insert_iterator<_Container> _Iter)
|
|
: back_insert_iterator<_Container>(_Iter) {}
|
|
|
|
using back_insert_iterator<_Container>::container;
|
|
};
|
|
|
|
template <class _Container>
|
|
requires (_Is_specialization_v<_Container, basic_string> || _Is_specialization_v<_Container, vector>)
|
|
struct _Fmt_iterator_flush<back_insert_iterator<_Container>> {
|
|
using _OutputIt = back_insert_iterator<_Container>;
|
|
|
|
template <class _Ty>
|
|
static _OutputIt _Flush(const _Ty* const _First, const _Ty* const _Last, _OutputIt _Output) {
|
|
_Container& _Cont = *_Back_insert_iterator_container_access<_Container>{_Output}.container;
|
|
_Cont.insert(_Cont.end(), _First, _Last);
|
|
return _Output;
|
|
}
|
|
};
|
|
|
|
template <class _OutputIt, class _Ty, class _Traits = _Fmt_buffer_traits>
|
|
class _Fmt_iterator_buffer final : public _Traits, public _Fmt_buffer<_Ty> {
|
|
private:
|
|
_OutputIt _Output;
|
|
_Ty _Data[_Fmt_buffer_size];
|
|
|
|
void _Grow(size_t) final {
|
|
if (this->_Size() == _Fmt_buffer_size) {
|
|
_Flush();
|
|
}
|
|
}
|
|
|
|
void _Flush() {
|
|
auto _Size = this->_Size();
|
|
this->_Clear();
|
|
const auto _End = _Data + this->_Limit(_Size);
|
|
|
|
_Output = _Fmt_iterator_flush<_OutputIt>::_Flush(_Data, _End, _STD move(_Output));
|
|
}
|
|
|
|
public:
|
|
explicit _Fmt_iterator_buffer(_OutputIt _Out, ptrdiff_t _Size = _Fmt_buffer_size)
|
|
: _Traits(_Size), _Fmt_buffer<_Ty>(_Data, 0, _Fmt_buffer_size), _Output(_STD move(_Out)) {}
|
|
|
|
~_Fmt_iterator_buffer() {
|
|
if (this->_Size() != 0) {
|
|
_Flush();
|
|
}
|
|
}
|
|
|
|
_NODISCARD _OutputIt _Out() {
|
|
_Flush();
|
|
return _STD move(_Output);
|
|
}
|
|
|
|
_NODISCARD ptrdiff_t _Count() const noexcept {
|
|
return static_cast<ptrdiff_t>(_Traits::_Count() + this->_Size());
|
|
}
|
|
};
|
|
|
|
template <class _Ty>
|
|
class _Fmt_iterator_buffer<_Ty*, _Ty> final : public _Fmt_buffer<_Ty> {
|
|
private:
|
|
void _Grow(size_t) final {}
|
|
|
|
public:
|
|
explicit _Fmt_iterator_buffer(_Ty* _Out, ptrdiff_t = 0) : _Fmt_buffer<_Ty>(_Out, 0, ~size_t{}) {}
|
|
|
|
_NODISCARD _Ty* _Out() noexcept {
|
|
return this->end();
|
|
}
|
|
};
|
|
|
|
#if _HAS_CXX23
|
|
template <class _CharT>
|
|
struct _Phony_fmt_iter_for {
|
|
using difference_type = ptrdiff_t;
|
|
|
|
// These member functions are never defined:
|
|
_CharT& operator*() const;
|
|
_Phony_fmt_iter_for& operator++();
|
|
_Phony_fmt_iter_for operator++(int);
|
|
};
|
|
|
|
_EXPORT_STD template <class _Ty, class _CharT>
|
|
concept formattable =
|
|
_Formattable_with<remove_reference_t<_Ty>, basic_format_context<_Phony_fmt_iter_for<_CharT>, _CharT>>;
|
|
|
|
template <class _CharT>
|
|
struct _Range_specs : _Fill_align_and_width_specs<_CharT> {
|
|
bool _No_brackets = false;
|
|
char _Type = '\0';
|
|
};
|
|
|
|
// TRANSITION, VSO-1236041: Avoid declaring and defining member functions in different headers.
|
|
template <class _Ty, class _CharT, class _ParseContext>
|
|
_NODISCARD constexpr _ParseContext::iterator _Range_formatter_parse(formatter<_Ty, _CharT>& _Underlying,
|
|
basic_string_view<_CharT>& _Separator, basic_string_view<_CharT>& _Opening_bracket,
|
|
basic_string_view<_CharT>& _Closing_bracket, _Range_specs<_CharT>& _Specs, _ParseContext& _Ctx);
|
|
|
|
template <class _Ty, class _CharT, _RANGES input_range _Range, class _FormatContext>
|
|
_NODISCARD _FormatContext::iterator _Range_formatter_format(const formatter<_Ty, _CharT>& _Underlying,
|
|
basic_string_view<_CharT> _Separator, basic_string_view<_CharT> _Opening_bracket,
|
|
basic_string_view<_CharT> _Closing_bracket, const _Range_specs<_CharT>& _Specs, _Range&& _Rng,
|
|
_FormatContext& _Ctx);
|
|
|
|
_EXPORT_STD template <class _Ty, class _CharT = char>
|
|
requires same_as<remove_cvref_t<_Ty>, _Ty> && formattable<_Ty, _CharT>
|
|
class range_formatter {
|
|
private:
|
|
formatter<_Ty, _CharT> _Underlying;
|
|
basic_string_view<_CharT> _Separator = _STATICALLY_WIDEN(_CharT, ", ");
|
|
basic_string_view<_CharT> _Opening_bracket = _STATICALLY_WIDEN(_CharT, "[");
|
|
basic_string_view<_CharT> _Closing_bracket = _STATICALLY_WIDEN(_CharT, "]");
|
|
_Range_specs<_CharT> _Specs;
|
|
|
|
public:
|
|
constexpr void set_separator(basic_string_view<_CharT> _Sep) noexcept {
|
|
_Separator = _Sep;
|
|
}
|
|
|
|
constexpr void set_brackets(basic_string_view<_CharT> _Opening, basic_string_view<_CharT> _Closing) noexcept {
|
|
_Opening_bracket = _Opening;
|
|
_Closing_bracket = _Closing;
|
|
}
|
|
|
|
_NODISCARD constexpr formatter<_Ty, _CharT>& underlying() noexcept {
|
|
return _Underlying;
|
|
}
|
|
|
|
_NODISCARD constexpr const formatter<_Ty, _CharT>& underlying() const noexcept {
|
|
return _Underlying;
|
|
}
|
|
|
|
template <class _ParseContext>
|
|
constexpr _ParseContext::iterator parse(_ParseContext& _Ctx) {
|
|
return _STD _Range_formatter_parse(_Underlying, _Separator, _Opening_bracket, _Closing_bracket, _Specs, _Ctx);
|
|
}
|
|
|
|
template <_RANGES input_range _Range, class _FormatContext>
|
|
requires formattable<_RANGES range_reference_t<_Range>, _CharT>
|
|
&& same_as<remove_cvref_t<_RANGES range_reference_t<_Range>>, _Ty>
|
|
_FormatContext::iterator format(_Range&& _Rng, _FormatContext& _Ctx) const {
|
|
return _Format(_STD forward<_Range>(_Rng), _Ctx);
|
|
}
|
|
|
|
private:
|
|
template <_RANGES input_range _Range, class _FormatContext>
|
|
_FormatContext::iterator _Format(_Range&&, _FormatContext&) const {
|
|
_Throw_format_error("Unsupported 'basic_format_context'.");
|
|
}
|
|
|
|
template <_RANGES input_range _Range, class _FormatContext>
|
|
requires _Is_specialization_v<typename _FormatContext::iterator, back_insert_iterator>
|
|
&& derived_from<typename _FormatContext::iterator::container_type, _Fmt_buffer<_CharT>>
|
|
_FormatContext::iterator _Format(_Range&& _Rng, _FormatContext& _Ctx) const {
|
|
return _STD _Range_formatter_format(
|
|
_Underlying, _Separator, _Opening_bracket, _Closing_bracket, _Specs, _STD forward<_Range>(_Rng), _Ctx);
|
|
}
|
|
};
|
|
|
|
template <class _Rng, class _CharT>
|
|
concept _Const_formattable_range =
|
|
_RANGES input_range<const _Rng> && formattable<_RANGES range_reference_t<const _Rng>, _CharT>;
|
|
|
|
template <class _Rng, class _CharT>
|
|
using _Fmt_maybe_const = conditional_t<_Const_formattable_range<_Rng, _CharT>, const _Rng, _Rng>;
|
|
|
|
template <range_format _Kind, _RANGES input_range _Rng, class _CharT>
|
|
struct _Range_default_formatter;
|
|
|
|
template <_RANGES input_range _Rng, class _CharT>
|
|
struct _Range_default_formatter<range_format::sequence, _Rng, _CharT> {
|
|
private:
|
|
using _Range_type = _Fmt_maybe_const<_Rng, _CharT>;
|
|
|
|
range_formatter<remove_cvref_t<_RANGES range_reference_t<_Range_type>>, _CharT> _Underlying;
|
|
|
|
public:
|
|
constexpr void set_separator(const basic_string_view<_CharT> _Sep) noexcept {
|
|
_Underlying.set_separator(_Sep);
|
|
}
|
|
|
|
constexpr void set_brackets(
|
|
const basic_string_view<_CharT> _Opening, const basic_string_view<_CharT> _Closing) noexcept {
|
|
_Underlying.set_brackets(_Opening, _Closing);
|
|
}
|
|
|
|
template <class _ParseContext>
|
|
constexpr _ParseContext::iterator parse(_ParseContext& _Ctx) {
|
|
return _Underlying.parse(_Ctx);
|
|
}
|
|
|
|
template <class _FormatContext>
|
|
_FormatContext::iterator format(_Range_type& _Elems, _FormatContext& _Ctx) const {
|
|
return _Underlying.format(_Elems, _Ctx);
|
|
}
|
|
};
|
|
|
|
template <_RANGES input_range _Rng, class _CharT>
|
|
struct _Range_default_formatter<range_format::map, _Rng, _CharT> {
|
|
private:
|
|
using _Map_type = _Fmt_maybe_const<_Rng, _CharT>;
|
|
using _Element_type = remove_cvref_t<_RANGES range_reference_t<_Map_type>>;
|
|
|
|
range_formatter<_Element_type, _CharT> _Underlying;
|
|
|
|
public:
|
|
constexpr _Range_default_formatter()
|
|
noexcept(is_nothrow_default_constructible_v<range_formatter<_Element_type, _CharT>>) /* strengthened */ {
|
|
static_assert(_Is_two_tuple<_Element_type>, "the element type of the formatted range must be either pair<T, U> "
|
|
"or tuple<T, U> (N4981 [format.range.fmtmap]/1)");
|
|
|
|
_Underlying.set_brackets(_STATICALLY_WIDEN(_CharT, "{"), _STATICALLY_WIDEN(_CharT, "}"));
|
|
_Underlying.underlying().set_brackets({}, {});
|
|
_Underlying.underlying().set_separator(_STATICALLY_WIDEN(_CharT, ": "));
|
|
}
|
|
|
|
template <class _ParseContext>
|
|
constexpr _ParseContext::iterator parse(_ParseContext& _Ctx) {
|
|
return _Underlying.parse(_Ctx);
|
|
}
|
|
|
|
template <class _FormatContext>
|
|
_FormatContext::iterator format(_Map_type& _Rx, _FormatContext& _Ctx) const {
|
|
return _Underlying.format(_Rx, _Ctx);
|
|
}
|
|
};
|
|
|
|
template <_RANGES input_range _Rng, class _CharT>
|
|
struct _Range_default_formatter<range_format::set, _Rng, _CharT> {
|
|
private:
|
|
using _Set_type = _Fmt_maybe_const<_Rng, _CharT>;
|
|
range_formatter<remove_cvref_t<_RANGES range_reference_t<_Set_type>>, _CharT> _Underlying;
|
|
|
|
public:
|
|
constexpr _Range_default_formatter() noexcept(is_nothrow_default_constructible_v<
|
|
range_formatter<remove_cvref_t<_RANGES range_reference_t<_Set_type>>, _CharT>>) /* strengthened */ {
|
|
_Underlying.set_brackets(_STATICALLY_WIDEN(_CharT, "{"), _STATICALLY_WIDEN(_CharT, "}"));
|
|
}
|
|
|
|
template <class _ParseContext>
|
|
constexpr _ParseContext::iterator parse(_ParseContext& _Ctx) {
|
|
return _Underlying.parse(_Ctx);
|
|
}
|
|
|
|
template <class _FormatContext>
|
|
_FormatContext::iterator format(_Set_type& _Rx, _FormatContext& _Ctx) const {
|
|
return _Underlying.format(_Rx, _Ctx);
|
|
}
|
|
};
|
|
|
|
template <range_format _Kind, _RANGES input_range _Rng, class _CharT>
|
|
requires (_Kind == range_format::string || _Kind == range_format::debug_string)
|
|
struct _Range_default_formatter<_Kind, _Rng, _CharT> {
|
|
private:
|
|
static_assert(is_same_v<remove_cvref_t<_RANGES range_reference_t<_Rng>>, _CharT>,
|
|
"the element type of the formatted range must be the character type used in formatting "
|
|
"(N4981 [format.range.fmtstr]/1)");
|
|
|
|
using _Range_type = _Maybe_const<_RANGES input_range<const _Rng>, _Rng>;
|
|
|
|
formatter<basic_string_view<_CharT>, _CharT> _Underlying; // avoid copying the string if possible
|
|
|
|
public:
|
|
template <class _ParseContext>
|
|
constexpr _ParseContext::iterator parse(_ParseContext& _Ctx) {
|
|
auto _Iter = _Underlying.parse(_Ctx);
|
|
if constexpr (_Kind == range_format::debug_string) {
|
|
_Underlying.set_debug_format();
|
|
}
|
|
return _Iter;
|
|
}
|
|
|
|
template <class _FormatContext>
|
|
_FormatContext::iterator format(_Range_type& _Rx, _FormatContext& _Ctx) const {
|
|
if constexpr (_RANGES contiguous_range<_Range_type>) {
|
|
const auto _Size = _STD _To_unsigned_like(_RANGES distance(_Rx));
|
|
|
|
if (!_STD in_range<size_t>(_Size)) [[unlikely]] {
|
|
_Throw_format_error("Formatted range is too long.");
|
|
}
|
|
|
|
const basic_string_view<_CharT> _Str(_STD to_address(_RANGES begin(_Rx)), static_cast<size_t>(_Size));
|
|
return _Underlying.format(_Str, _Ctx);
|
|
} else {
|
|
return _Underlying.format(basic_string<_CharT>{from_range, _Rx}, _Ctx);
|
|
}
|
|
}
|
|
};
|
|
|
|
// the deleted default constructor makes it "disabled" as per N4981 [format.formatter.spec]/5
|
|
|
|
template <_RANGES input_range _Rng, _Format_supported_charT _CharT>
|
|
requires _Formatting_enabled_range<_Rng>
|
|
struct formatter<_Rng, _CharT> {
|
|
formatter() = delete;
|
|
formatter(const formatter&) = delete;
|
|
formatter& operator=(const formatter&) = delete;
|
|
};
|
|
|
|
template <_RANGES input_range _Rng, _Format_supported_charT _CharT>
|
|
requires _Formatting_enabled_range<_Rng> && formattable<_RANGES range_reference_t<_Rng>, _CharT>
|
|
struct formatter<_Rng, _CharT> : _Range_default_formatter<format_kind<_Rng>, _Rng, _CharT> {};
|
|
|
|
// Per LWG-3997, `_CharT` in library-provided `formatter` specializations is
|
|
// constrained to character types supported by `format`.
|
|
template <class _AdaptorType, _Format_supported_charT _CharT, template <class> class _RefView>
|
|
struct _Adaptor_formatter_base {
|
|
private:
|
|
using _Container = _AdaptorType::container_type;
|
|
using _Maybe_const_container = _Fmt_maybe_const<_Container, _CharT>;
|
|
using _Maybe_const_adaptor = _Maybe_const<is_const_v<_Maybe_const_container>, _AdaptorType>;
|
|
|
|
formatter<_RefView<_Maybe_const_container>, _CharT> _Underlying;
|
|
|
|
public:
|
|
template <class _ParseCtx>
|
|
constexpr _ParseCtx::iterator parse(_ParseCtx& _Ctx) {
|
|
return _Underlying.parse(_Ctx);
|
|
}
|
|
|
|
template <class _FmtCtx>
|
|
_FmtCtx::iterator format(_Maybe_const_adaptor& _Adaptor, _FmtCtx& _Ctx) const {
|
|
struct _Container_exposer : _AdaptorType {
|
|
using _AdaptorType::c;
|
|
|
|
_Container_exposer(const _Container_exposer&) = delete;
|
|
_Container_exposer& operator=(const _Container_exposer&) = delete;
|
|
~_Container_exposer() = delete;
|
|
};
|
|
|
|
constexpr auto _Mem_cont_ptr = &_Container_exposer::c;
|
|
return _Underlying.format(_Adaptor.*_Mem_cont_ptr, _Ctx);
|
|
}
|
|
};
|
|
|
|
// TRANSITION, VSO-1236041: Avoid declaring and defining member functions in different headers.
|
|
template <class... _Types, class _CharT, class _ParseContext>
|
|
_NODISCARD constexpr _ParseContext::iterator _Tuple_formatter_parse(tuple<formatter<_Types, _CharT>...>& _Underlying,
|
|
basic_string_view<_CharT>& _Separator, basic_string_view<_CharT>& _Opening_bracket,
|
|
basic_string_view<_CharT>& _Closing_bracket, _Fill_align_and_width_specs<_CharT>& _Specs, _ParseContext& _Ctx);
|
|
|
|
template <class... _Types, class _CharT, class _FormatContext, class... _ArgTypes>
|
|
_NODISCARD _FormatContext::iterator _Tuple_formatter_format(const tuple<formatter<_Types, _CharT>...>& _Underlying,
|
|
basic_string_view<_CharT> _Separator, basic_string_view<_CharT> _Opening_bracket,
|
|
basic_string_view<_CharT> _Closing_bracket, const _Fill_align_and_width_specs<_CharT>& _Specs,
|
|
_FormatContext& _Fmt_ctx, _ArgTypes&... _Args);
|
|
|
|
template <class _CharT, formattable<_CharT>... _Types>
|
|
class _Tuple_formatter_common_base {
|
|
private:
|
|
tuple<formatter<remove_cvref_t<_Types>, _CharT>...> _Underlying;
|
|
basic_string_view<_CharT> _Separator = _STATICALLY_WIDEN(_CharT, ", ");
|
|
basic_string_view<_CharT> _Opening_bracket = _STATICALLY_WIDEN(_CharT, "(");
|
|
basic_string_view<_CharT> _Closing_bracket = _STATICALLY_WIDEN(_CharT, ")");
|
|
|
|
_Fill_align_and_width_specs<_CharT> _Specs;
|
|
|
|
protected:
|
|
static constexpr bool _Is_const_formattable = (formattable<const _Types, _CharT> && ...);
|
|
|
|
template <class _FormatContext, class... _ArgTypes>
|
|
_FormatContext::iterator _Format(_FormatContext& _Fmt_ctx, _ArgTypes&... _Args) const {
|
|
_STL_INTERNAL_STATIC_ASSERT(
|
|
(is_same_v<_ArgTypes, remove_reference_t<_Maybe_const<_Is_const_formattable, _Types>>> && ...));
|
|
|
|
return _STD _Tuple_formatter_format(
|
|
_Underlying, _Separator, _Opening_bracket, _Closing_bracket, _Specs, _Fmt_ctx, _Args...);
|
|
}
|
|
|
|
public:
|
|
constexpr void set_separator(const basic_string_view<_CharT> _Sep) noexcept {
|
|
_Separator = _Sep;
|
|
}
|
|
|
|
constexpr void set_brackets(
|
|
const basic_string_view<_CharT> _Opening, const basic_string_view<_CharT> _Closing) noexcept {
|
|
_Opening_bracket = _Opening;
|
|
_Closing_bracket = _Closing;
|
|
}
|
|
|
|
template <class _ParseContext>
|
|
constexpr _ParseContext::iterator parse(_ParseContext& _Ctx) {
|
|
return _STD _Tuple_formatter_parse(_Underlying, _Separator, _Opening_bracket, _Closing_bracket, _Specs, _Ctx);
|
|
}
|
|
};
|
|
|
|
// formatter definition for all pairs and tuples, the deleted default constructor
|
|
// makes it "disabled" as per N4971 [format.formatter.spec]/5
|
|
|
|
template <class _TupleOrPair, class _CharT>
|
|
struct _Tuple_formatter_base {
|
|
_Tuple_formatter_base() = delete;
|
|
_Tuple_formatter_base(const _Tuple_formatter_base&) = delete;
|
|
_Tuple_formatter_base& operator=(const _Tuple_formatter_base&) = delete;
|
|
};
|
|
|
|
template <class _CharT, formattable<_CharT> _Ty1, formattable<_CharT> _Ty2>
|
|
struct _Tuple_formatter_base<pair<_Ty1, _Ty2>, _CharT> : _Tuple_formatter_common_base<_CharT, _Ty1, _Ty2> {
|
|
private:
|
|
using _Base = _Tuple_formatter_common_base<_CharT, _Ty1, _Ty2>;
|
|
using _Formatted_type = _Maybe_const<_Base::_Is_const_formattable, pair<_Ty1, _Ty2>>;
|
|
|
|
public:
|
|
template <class _FormatContext>
|
|
_FormatContext::iterator format(_Formatted_type& _Elems, _FormatContext& _Ctx) const {
|
|
return this->_Format(_Ctx, _Elems.first, _Elems.second);
|
|
}
|
|
};
|
|
|
|
template <class _CharT, formattable<_CharT>... _Types>
|
|
struct _Tuple_formatter_base<tuple<_Types...>, _CharT> : _Tuple_formatter_common_base<_CharT, _Types...> {
|
|
private:
|
|
using _Base = _Tuple_formatter_common_base<_CharT, _Types...>;
|
|
using _Formatted_type = _Maybe_const<_Base::_Is_const_formattable, tuple<_Types...>>;
|
|
|
|
public:
|
|
template <class _FormatContext>
|
|
_FormatContext::iterator format(_Formatted_type& _Elems, _FormatContext& _Ctx) const {
|
|
return _STD apply([this, &_Ctx](auto&... _Args) { return this->_Format(_Ctx, _Args...); }, _Elems);
|
|
}
|
|
};
|
|
|
|
// specializations for tuple-like types that are input ranges and not formattable as tuples
|
|
|
|
template <_Format_supported_charT _CharT, class _Ty1, class _Ty2>
|
|
requires (!formattable<_Ty1, _CharT> || !formattable<_Ty2, _CharT>)
|
|
// TRANSITION, clang-format, () should be redundant
|
|
&& (_RANGES input_range<pair<_Ty1, _Ty2>>) && _Formatting_enabled_range<pair<_Ty1, _Ty2>>
|
|
&& formattable<_RANGES range_reference_t<pair<_Ty1, _Ty2>>, _CharT>
|
|
struct _Tuple_formatter_base<pair<_Ty1, _Ty2>, _CharT>
|
|
: _Range_default_formatter<format_kind<pair<_Ty1, _Ty2>>, pair<_Ty1, _Ty2>, _CharT> {};
|
|
|
|
template <_Format_supported_charT _CharT, class... _Types>
|
|
requires ((!formattable<_Types, _CharT>) || ...)
|
|
// TRANSITION, clang-format, () should be redundant
|
|
&& (_RANGES input_range<tuple<_Types...>>) && _Formatting_enabled_range<tuple<_Types...>>
|
|
&& formattable<_RANGES range_reference_t<tuple<_Types...>>, _CharT>
|
|
struct _Tuple_formatter_base<tuple<_Types...>, _CharT>
|
|
: _Range_default_formatter<format_kind<tuple<_Types...>>, tuple<_Types...>, _CharT> {};
|
|
|
|
// Per LWG-3997, `_CharT` in library-provided `formatter` specializations is
|
|
// constrained to character types supported by `format`.
|
|
|
|
template <_Format_supported_charT _CharT, class _Ty1, class _Ty2>
|
|
struct formatter<pair<_Ty1, _Ty2>, _CharT> : _Tuple_formatter_base<pair<_Ty1, _Ty2>, _CharT> {};
|
|
|
|
template <_Format_supported_charT _CharT, class... _Types>
|
|
struct formatter<tuple<_Types...>, _CharT> : _Tuple_formatter_base<tuple<_Types...>, _CharT> {};
|
|
#endif // _HAS_CXX23
|
|
_STD_END
|
|
|
|
#pragma pop_macro("new")
|
|
_STL_RESTORE_CLANG_WARNINGS
|
|
#pragma warning(pop)
|
|
#pragma pack(pop)
|
|
|
|
#endif // _STL_COMPILER_PREPROCESSOR
|
|
#endif // __MSVC_RANGES_TUPLE_FORMATTER_HPP
|