зеркало из https://github.com/microsoft/STL.git
Implement `formatter` specializations for `pair` and `tuple` (#4438)
Co-authored-by: Stephan T. Lavavej <stl@nuwen.net>
This commit is contained in:
Родитель
4d088fc77c
Коммит
79e79a2e07
|
@ -15,7 +15,7 @@
|
|||
"type": "git",
|
||||
"git": {
|
||||
"repositoryUrl": "https://github.com/llvm/llvm-project.git",
|
||||
"commitHash": "b8d38e8b4fcab071c5c4cb698e154023d06de69e"
|
||||
"commitHash": "2e2b6b53f5f63179b52168ee156df7c76b90bc71"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -127,9 +127,9 @@ concept _Format_supported_charT = _Is_any_of_v<_CharT, char, wchar_t>;
|
|||
// makes it "disabled" as per N4950 [format.formatter.spec]/5
|
||||
_EXPORT_STD template <class _Ty, class _CharT = char>
|
||||
struct formatter {
|
||||
formatter() = delete;
|
||||
formatter(const formatter&) = delete;
|
||||
formatter operator=(const formatter&) = delete;
|
||||
formatter() = delete;
|
||||
formatter(const formatter&) = delete;
|
||||
formatter& operator=(const formatter&) = delete;
|
||||
};
|
||||
|
||||
_FMT_P2286_BEGIN
|
||||
|
@ -266,6 +266,25 @@ struct formatter<basic_string_view<_CharT, _Traits>, _CharT>
|
|||
}
|
||||
#endif // _HAS_CXX23
|
||||
};
|
||||
|
||||
#if _HAS_CXX23
|
||||
_EXPORT_STD template <class, class>
|
||||
struct pair;
|
||||
|
||||
_EXPORT_STD template <class...>
|
||||
class tuple;
|
||||
|
||||
// Specializations for pairs and tuples are forward-declared to avoid any risk of using the disabled primary template.
|
||||
|
||||
// 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>;
|
||||
|
||||
template <_Format_supported_charT _CharT, class... _Types>
|
||||
struct formatter<tuple<_Types...>, _CharT>;
|
||||
#endif // _HAS_CXX23
|
||||
_STD_END
|
||||
|
||||
#pragma pop_macro("new")
|
||||
|
|
267
stl/inc/format
267
stl/inc/format
|
@ -58,6 +58,10 @@ _EMIT_STL_WARNING(STL4038, "The contents of <format> are available only with C++
|
|||
#include <xstring>
|
||||
#include <xutility>
|
||||
|
||||
#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)
|
||||
|
@ -3934,6 +3938,259 @@ _NODISCARD size_t formatted_size(const locale& _Loc, const wformat_string<_Types
|
|||
_FMT_P2286_END
|
||||
|
||||
#if _HAS_CXX23
|
||||
enum class _Fmt_tuple_type : uint8_t { _None, _Key_value, _No_brackets };
|
||||
|
||||
template <class _CharT>
|
||||
struct _Fill_align_and_width_specs { // used by pair, tuple, thread::id, and stacktrace_entry formatters
|
||||
int _Width = -1;
|
||||
int _Dynamic_width_index = -1;
|
||||
_Fmt_align _Alignment = _Fmt_align::_None;
|
||||
uint8_t _Fill_length = 1;
|
||||
// At most one codepoint (so one char32_t or four utf-8 char8_t).
|
||||
_CharT _Fill[4 / sizeof(_CharT)]{' '};
|
||||
};
|
||||
|
||||
template <class _CharT, bool _IsTwoTuple>
|
||||
class _Tuple_format_specs_setter {
|
||||
public:
|
||||
constexpr explicit _Tuple_format_specs_setter(_Fill_align_and_width_specs<_CharT>& _Specs_,
|
||||
_Fmt_tuple_type& _Fmt_type_, basic_format_parse_context<_CharT>& _Parse_ctx_)
|
||||
: _Specs(_Specs_), _Fmt_type(_Fmt_type_), _Parse_ctx(_Parse_ctx_) {}
|
||||
|
||||
constexpr void _On_align(const _Fmt_align _Aln) {
|
||||
_Specs._Alignment = _Aln;
|
||||
}
|
||||
|
||||
constexpr void _On_fill(const basic_string_view<_CharT> _Sv) {
|
||||
if (_Sv.size() > _STD size(_Specs._Fill)) {
|
||||
_Throw_format_error("Invalid fill (too long).");
|
||||
}
|
||||
|
||||
if (_Sv == _STATICALLY_WIDEN(_CharT, ":")) {
|
||||
_Throw_format_error(R"(Invalid fill ":" for tuples and pairs.)");
|
||||
}
|
||||
|
||||
const auto _Pos = _STD _Copy_unchecked(_Sv._Unchecked_begin(), _Sv._Unchecked_end(), _Specs._Fill);
|
||||
_STD fill(_Pos, _STD end(_Specs._Fill), _CharT{});
|
||||
_Specs._Fill_length = static_cast<uint8_t>(_Sv.size());
|
||||
}
|
||||
|
||||
constexpr void _On_width(const int _Width) {
|
||||
_Specs._Width = _Width;
|
||||
}
|
||||
|
||||
constexpr void _On_dynamic_width(const size_t _Arg_id) {
|
||||
_Parse_ctx.check_arg_id(_Arg_id);
|
||||
_Parse_ctx._Check_dynamic_spec_integral(_Arg_id);
|
||||
_Specs._Dynamic_width_index = _Verify_dynamic_arg_index_in_range(_Arg_id);
|
||||
}
|
||||
|
||||
constexpr void _On_dynamic_width(_Auto_id_tag) {
|
||||
const size_t _Arg_id = _Parse_ctx.next_arg_id();
|
||||
_Parse_ctx._Check_dynamic_spec_integral(_Arg_id);
|
||||
_Specs._Dynamic_width_index = _Verify_dynamic_arg_index_in_range(_Arg_id);
|
||||
}
|
||||
|
||||
constexpr void _On_type(const _CharT _Type) {
|
||||
if (_Type == _CharT{}) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_Type == static_cast<_CharT>('n')) {
|
||||
_Fmt_type = _Fmt_tuple_type::_No_brackets;
|
||||
return;
|
||||
}
|
||||
|
||||
if constexpr (_IsTwoTuple) {
|
||||
if (_Type == static_cast<_CharT>('m')) {
|
||||
_Fmt_type = _Fmt_tuple_type::_Key_value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_Throw_format_error("Invalid type specification.");
|
||||
}
|
||||
|
||||
private:
|
||||
_Fill_align_and_width_specs<_CharT>& _Specs;
|
||||
_Fmt_tuple_type& _Fmt_type;
|
||||
basic_format_parse_context<_CharT>& _Parse_ctx;
|
||||
|
||||
_NODISCARD static constexpr int _Verify_dynamic_arg_index_in_range(const size_t _Idx) {
|
||||
if (!_STD in_range<int>(_Idx)) {
|
||||
_Throw_format_error("Dynamic width index is too large.");
|
||||
}
|
||||
|
||||
return static_cast<int>(_Idx);
|
||||
}
|
||||
};
|
||||
|
||||
template <class _CharT, class _Callbacks_type>
|
||||
_NODISCARD constexpr const _CharT* _Parse_tuple_format_specs(
|
||||
const _CharT* _Begin, const _CharT* _End, _Callbacks_type&& _Callbacks) {
|
||||
if (_Begin == _End || *_Begin == '}') {
|
||||
return _Begin;
|
||||
}
|
||||
|
||||
_Begin = _STD _Parse_align(_Begin, _End, _Callbacks);
|
||||
if (_Begin == _End) {
|
||||
return _Begin;
|
||||
}
|
||||
|
||||
_Begin = _STD _Parse_width(_Begin, _End, _Callbacks);
|
||||
if (_Begin == _End) {
|
||||
return _Begin;
|
||||
}
|
||||
|
||||
// If there's anything remaining we assume it's a type.
|
||||
if (*_Begin != '}') {
|
||||
_Callbacks._On_type(*_Begin);
|
||||
++_Begin;
|
||||
} else {
|
||||
// call the type callback so it gets a default type, this is required
|
||||
// since _Specs_checker needs to be able to tell that it got a default type
|
||||
// to raise an error for default formatted bools with a sign modifier
|
||||
_Callbacks._On_type(_CharT{});
|
||||
}
|
||||
|
||||
return _Begin;
|
||||
}
|
||||
|
||||
template <class _FormatterType, class _ParseContext>
|
||||
constexpr void _Set_tuple_debug_format(_FormatterType& _Formatter, _ParseContext& _Parse_ctx) {
|
||||
_Formatter.parse(_Parse_ctx);
|
||||
if constexpr (requires { _Formatter.set_debug_format(); }) {
|
||||
_Formatter.set_debug_format();
|
||||
}
|
||||
}
|
||||
|
||||
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>>> && ...));
|
||||
|
||||
auto _Format_specs = _Specs;
|
||||
if (_Specs._Dynamic_width_index >= 0) {
|
||||
_Format_specs._Width =
|
||||
_STD _Get_dynamic_specs<_Width_checker>(_Fmt_ctx.arg(static_cast<size_t>(_Specs._Dynamic_width_index)));
|
||||
}
|
||||
|
||||
basic_string<_CharT> _Tmp_buf;
|
||||
basic_format_context<back_insert_iterator<basic_string<_CharT>>, _CharT> _Tmp_ctx{
|
||||
_STD back_inserter(_Tmp_buf), {}, _Fmt_ctx._Get_lazy_locale()};
|
||||
|
||||
_STD _Copy_unchecked(_Opening_bracket._Unchecked_begin(), _Opening_bracket._Unchecked_end(), _Tmp_ctx.out());
|
||||
[&]<size_t... _Indices>(index_sequence<_Indices...>) {
|
||||
auto _Single_writer = [&]<size_t _Idx>(auto& _Arg) {
|
||||
if constexpr (_Idx != 0) {
|
||||
_STD _Copy_unchecked(_Separator._Unchecked_begin(), _Separator._Unchecked_end(), _Tmp_ctx.out());
|
||||
}
|
||||
_STD get<_Idx>(_Underlying).format(_Arg, _Tmp_ctx);
|
||||
};
|
||||
(_Single_writer.template operator()<_Indices>(_Args), ...);
|
||||
}(index_sequence_for<_ArgTypes...>{});
|
||||
_STD _Copy_unchecked(_Closing_bracket._Unchecked_begin(), _Closing_bracket._Unchecked_end(), _Tmp_ctx.out());
|
||||
|
||||
return _STD _Write_aligned(_Fmt_ctx.out(), static_cast<int>(_Tmp_buf.size()), _Format_specs, _Fmt_align::_Left,
|
||||
[&](typename _FormatContext::iterator _Out) {
|
||||
return _STD _Fmt_write(_STD move(_Out), basic_string_view<_CharT>{_Tmp_buf});
|
||||
});
|
||||
}
|
||||
|
||||
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) {
|
||||
_Fmt_tuple_type _Fmt_type = _Fmt_tuple_type::_None;
|
||||
_Tuple_format_specs_setter<_CharT, sizeof...(_Types) == 2> _Callback{_Specs, _Fmt_type, _Ctx};
|
||||
const auto _It = _STD _Parse_tuple_format_specs(_Ctx._Unchecked_begin(), _Ctx._Unchecked_end(), _Callback);
|
||||
if (_It != _Ctx._Unchecked_end() && *_It != '}') {
|
||||
_STD _Throw_format_error("Missing '}' in format string.");
|
||||
}
|
||||
|
||||
if (_Fmt_type == _Fmt_tuple_type::_No_brackets) {
|
||||
set_brackets({}, {});
|
||||
} else if constexpr (sizeof...(_Types) == 2) {
|
||||
if (_Fmt_type == _Fmt_tuple_type::_Key_value) {
|
||||
set_separator(_STATICALLY_WIDEN(_CharT, ": "));
|
||||
set_brackets({}, {});
|
||||
}
|
||||
}
|
||||
|
||||
_Ctx.advance_to(_Ctx.begin() + (_It - _Ctx._Unchecked_begin()));
|
||||
_STD apply([&_Ctx](auto&... _Elems) { (_Set_tuple_debug_format(_Elems, _Ctx), ...); }, _Underlying);
|
||||
|
||||
return _Ctx.begin();
|
||||
}
|
||||
};
|
||||
|
||||
// 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);
|
||||
}
|
||||
};
|
||||
|
||||
// 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> {};
|
||||
|
||||
enum class _Add_newline : bool { _Nope, _Yes };
|
||||
|
||||
_NODISCARD inline string _Unescape_braces(const _Add_newline _Add_nl, const string_view _Old_str) {
|
||||
|
@ -3980,16 +4237,6 @@ _NODISCARD inline string _Unescape_braces(const _Add_newline _Add_nl, const stri
|
|||
return _Unescaped_str;
|
||||
}
|
||||
|
||||
template <class _CharT>
|
||||
struct _Fill_align_and_width_specs { // used by thread::id and stacktrace_entry formatters
|
||||
int _Width = -1;
|
||||
int _Dynamic_width_index = -1;
|
||||
_Fmt_align _Alignment = _Fmt_align::_None;
|
||||
uint8_t _Fill_length = 1;
|
||||
// At most one codepoint (so one char32_t or four utf-8 char8_t).
|
||||
_CharT _Fill[4 / sizeof(_CharT)] = {' '};
|
||||
};
|
||||
|
||||
template <class _CharT>
|
||||
class _Fill_align_and_width_specs_setter {
|
||||
public:
|
||||
|
|
|
@ -3578,7 +3578,9 @@ namespace pmr {
|
|||
#endif // _HAS_CXX17
|
||||
|
||||
#if _HAS_CXX23
|
||||
template <class _Ty, class _CharT>
|
||||
// Per LWG-3997, `_CharT` in library-provided `formatter` specializations is
|
||||
// constrained to character types supported by `format`.
|
||||
template <class _Ty, _Format_supported_charT _CharT>
|
||||
requires _Is_specialization_v<_Ty, _Vb_reference>
|
||||
struct formatter<_Ty, _CharT> {
|
||||
private:
|
||||
|
|
|
@ -33,6 +33,11 @@ std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_mov
|
|||
# LLVM-79783: [libc++][test] array/size_and_alignment.compile.pass.cpp includes non-Standard <__type_traits/datasizeof.h>
|
||||
std/containers/sequences/array/size_and_alignment.compile.pass.cpp FAIL
|
||||
|
||||
# LLVM-83734: [libc++][test] Don't include test_format_context.h in parse.pass.cpp
|
||||
std/containers/sequences/vector.bool/vector.bool.fmt/parse.pass.cpp FAIL
|
||||
std/thread/thread.threads/thread.thread.class/thread.thread.id/parse.pass.cpp FAIL
|
||||
std/utilities/format/format.tuple/parse.pass.cpp FAIL
|
||||
|
||||
# Non-Standard regex behavior.
|
||||
# "It seems likely that the test is still non-conforming due to how libc++ handles the 'w' character class."
|
||||
std/re/re.traits/lookup_classname.pass.cpp FAIL
|
||||
|
@ -259,7 +264,6 @@ std/containers/container.adaptors/container.adaptors.format/types.compile.pass.c
|
|||
std/containers/sequences/vector.bool/vector.bool.fmt/format.functions.format.pass.cpp FAIL
|
||||
std/containers/sequences/vector.bool/vector.bool.fmt/format.functions.vformat.pass.cpp FAIL
|
||||
std/containers/sequences/vector.bool/vector.bool.fmt/format.pass.cpp FAIL
|
||||
std/containers/sequences/vector.bool/vector.bool.fmt/parse.pass.cpp FAIL
|
||||
std/input.output/iostream.format/print.fun/includes.compile.pass.cpp FAIL
|
||||
std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp FAIL
|
||||
std/utilities/format/format.range/format.range.fmtdef/format.pass.cpp FAIL
|
||||
|
@ -285,12 +289,6 @@ std/utilities/format/format.range/format.range.formatter/parse.pass.cpp FAIL
|
|||
std/utilities/format/format.range/format.range.formatter/set_brackets.pass.cpp FAIL
|
||||
std/utilities/format/format.range/format.range.formatter/set_separator.pass.cpp FAIL
|
||||
std/utilities/format/format.range/format.range.formatter/underlying.pass.cpp FAIL
|
||||
std/utilities/format/format.tuple/format.functions.format.pass.cpp FAIL
|
||||
std/utilities/format/format.tuple/format.functions.vformat.pass.cpp FAIL
|
||||
std/utilities/format/format.tuple/format.pass.cpp FAIL
|
||||
std/utilities/format/format.tuple/parse.pass.cpp FAIL
|
||||
std/utilities/format/format.tuple/set_brackets.pass.cpp FAIL
|
||||
std/utilities/format/format.tuple/set_separator.pass.cpp FAIL
|
||||
std/utilities/format/types.compile.pass.cpp FAIL
|
||||
|
||||
# P2363R5 Extending Associative Containers With The Remaining Heterogeneous Overloads
|
||||
|
@ -1112,7 +1110,6 @@ std/input.output/string.streams/stringstream/stringstream.members/str.allocator_
|
|||
# says "Please create a vendor specific version of the test functions".
|
||||
# If we do, some of these tests should be able to work.
|
||||
std/thread/thread.threads/thread.thread.class/thread.thread.id/format.pass.cpp FAIL
|
||||
std/thread/thread.threads/thread.thread.class/thread.thread.id/parse.pass.cpp FAIL
|
||||
std/utilities/format/format.formatter/format.context/format.context/advance_to.pass.cpp FAIL
|
||||
std/utilities/format/format.formatter/format.context/format.context/arg.pass.cpp FAIL
|
||||
std/utilities/format/format.formatter/format.context/format.context/ctor.pass.cpp FAIL
|
||||
|
@ -1130,6 +1127,7 @@ std/utilities/format/format.formatter/format.formatter.spec/formatter.pointer.pa
|
|||
std/utilities/format/format.formatter/format.formatter.spec/formatter.signed_integral.pass.cpp FAIL
|
||||
std/utilities/format/format.formatter/format.formatter.spec/formatter.string.pass.cpp FAIL
|
||||
std/utilities/format/format.formatter/format.formatter.spec/formatter.unsigned_integral.pass.cpp FAIL
|
||||
std/utilities/format/format.tuple/format.pass.cpp FAIL
|
||||
|
||||
# Not analyzed. Apparent false positives from static analysis where it thinks that array indexing is out of bounds.
|
||||
# warning C28020: The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call.
|
||||
|
|
|
@ -610,6 +610,7 @@ tests\P2286R8_text_formatting_escaping
|
|||
tests\P2286R8_text_formatting_escaping_legacy_text_encoding
|
||||
tests\P2286R8_text_formatting_escaping_utf8
|
||||
tests\P2286R8_text_formatting_formattable
|
||||
tests\P2286R8_text_formatting_tuple
|
||||
tests\P2286R8_text_formatting_vector_bool_reference
|
||||
tests\P2302R4_ranges_alg_contains
|
||||
tests\P2302R4_ranges_alg_contains_subrange
|
||||
|
|
|
@ -213,6 +213,9 @@ void test_P2286_vector_bool() {
|
|||
// Tests for P2286 Formatting ranges
|
||||
template <class CharT>
|
||||
void test_P2286() {
|
||||
assert_is_formattable<pair<int, int>, CharT>();
|
||||
assert_is_formattable<tuple<int>, CharT>();
|
||||
|
||||
test_P2286_vector_bool<CharT, vector<bool>>();
|
||||
test_P2286_vector_bool<CharT, pmr::vector<bool>>();
|
||||
test_P2286_vector_bool<CharT, vector<bool, alternative_allocator<bool>>>();
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# Copyright (c) Microsoft Corporation.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
RUNALL_INCLUDE ..\usual_latest_matrix.lst
|
|
@ -0,0 +1,437 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// derived from libc++'s test files:
|
||||
// * std/utilities/format/format.tuple/format.functions.tests.h
|
||||
// * std/utilities/format/format.tuple/format.functions.format.pass.cpp
|
||||
// * std/utilities/format/format.tuple/format.functions.vformat.pass.cpp
|
||||
|
||||
#include <cassert>
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <format>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <test_format_support.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define CSTR(Str) TYPED_LITERAL(CharT, Str)
|
||||
#define STR(Str) basic_string(CSTR(Str))
|
||||
#define SV(Str) basic_string_view(CSTR(Str))
|
||||
|
||||
enum class color { black, red, gold };
|
||||
|
||||
template <class CharT>
|
||||
struct std::formatter<color, CharT> : std::formatter<basic_string_view<CharT>, CharT> {
|
||||
static constexpr basic_string_view<CharT> color_names[] = {SV("black"), SV("red"), SV("gold")};
|
||||
auto format(color c, auto& ctx) const {
|
||||
return formatter<basic_string_view<CharT>, CharT>::format(color_names[static_cast<int>(c)], ctx);
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Generic tests for a tuple and pair with two elements.
|
||||
//
|
||||
template <class CharT, class TestFunction, class ExceptionTest, class TupleOrPair>
|
||||
void test_tuple_or_pair_int_int(TestFunction check, ExceptionTest check_exception, TupleOrPair&& input) {
|
||||
check(SV("(42, 99)"), SV("{}"), input);
|
||||
check(SV("(42, 99)^42"), SV("{}^42"), input);
|
||||
check(SV("(42, 99)^42"), SV("{:}^42"), input);
|
||||
|
||||
// *** align-fill & width ***
|
||||
check(SV("(42, 99) "), SV("{:13}"), input);
|
||||
check(SV("(42, 99)*****"), SV("{:*<13}"), input);
|
||||
check(SV("__(42, 99)___"), SV("{:_^13}"), input);
|
||||
check(SV("#####(42, 99)"), SV("{:#>13}"), input);
|
||||
|
||||
check(SV("(42, 99) "), SV("{:{}}"), input, 13);
|
||||
check(SV("(42, 99)*****"), SV("{:*<{}}"), input, 13);
|
||||
check(SV("__(42, 99)___"), SV("{:_^{}}"), input, 13);
|
||||
check(SV("#####(42, 99)"), SV("{:#>{}}"), input, 13);
|
||||
|
||||
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
|
||||
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
|
||||
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
|
||||
|
||||
// *** sign ***
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{:+}"), input);
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{: }"), input);
|
||||
|
||||
// *** alternate form ***
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{:#}"), input);
|
||||
|
||||
// *** zero-padding ***
|
||||
check_exception("The width option should not have a leading zero", SV("{:0}"), input);
|
||||
|
||||
// *** precision ***
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{:.}"), input);
|
||||
|
||||
// *** locale-specific form ***
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{:L}"), input);
|
||||
|
||||
// *** type ***
|
||||
check(SV("__42: 99___"), SV("{:_^11m}"), input);
|
||||
check(SV("__42, 99___"), SV("{:_^11n}"), input);
|
||||
|
||||
for (CharT c : SV("aAbBcdeEfFgGopPsxX?")) {
|
||||
check_exception("The format specifier should consume the input or end with a '}'",
|
||||
basic_string_view{STR("{:") + c + STR("}")}, input);
|
||||
}
|
||||
}
|
||||
|
||||
template <class CharT, class TestFunction, class ExceptionTest, class TupleOrPair>
|
||||
void test_tuple_or_pair_int_string(TestFunction check, ExceptionTest check_exception, TupleOrPair&& input) {
|
||||
check(SV("(42, \"hello\")"), SV("{}"), input);
|
||||
check(SV("(42, \"hello\")^42"), SV("{}^42"), input);
|
||||
check(SV("(42, \"hello\")^42"), SV("{:}^42"), input);
|
||||
|
||||
// *** align-fill & width ***
|
||||
check(SV("(42, \"hello\") "), SV("{:18}"), input);
|
||||
check(SV("(42, \"hello\")*****"), SV("{:*<18}"), input);
|
||||
check(SV("__(42, \"hello\")___"), SV("{:_^18}"), input);
|
||||
check(SV("#####(42, \"hello\")"), SV("{:#>18}"), input);
|
||||
|
||||
check(SV("(42, \"hello\") "), SV("{:{}}"), input, 18);
|
||||
check(SV("(42, \"hello\")*****"), SV("{:*<{}}"), input, 18);
|
||||
check(SV("__(42, \"hello\")___"), SV("{:_^{}}"), input, 18);
|
||||
check(SV("#####(42, \"hello\")"), SV("{:#>{}}"), input, 18);
|
||||
|
||||
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
|
||||
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
|
||||
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
|
||||
|
||||
// *** sign ***
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{:+}"), input);
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{: }"), input);
|
||||
|
||||
// *** alternate form ***
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{:#}"), input);
|
||||
|
||||
// *** zero-padding ***
|
||||
check_exception("The width option should not have a leading zero", SV("{:0}"), input);
|
||||
|
||||
// *** precision ***
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{:.}"), input);
|
||||
|
||||
// *** locale-specific form ***
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{:L}"), input);
|
||||
|
||||
// *** type ***
|
||||
check(SV("__42: \"hello\"___"), SV("{:_^16m}"), input);
|
||||
check(SV("__42, \"hello\"___"), SV("{:_^16n}"), input);
|
||||
|
||||
for (CharT c : SV("aAbBcdeEfFgGopPsxX?")) {
|
||||
check_exception("The format specifier should consume the input or end with a '}'",
|
||||
basic_string_view{STR("{:") + c + STR("}")}, input);
|
||||
}
|
||||
}
|
||||
|
||||
template <class CharT, class TestFunction, class TupleOrPair>
|
||||
void test_escaping(TestFunction check, TupleOrPair&& input) {
|
||||
static_assert(same_as<remove_cvref_t<decltype(get<0>(input))>, CharT>);
|
||||
static_assert(same_as<remove_cvref_t<decltype(get<1>(input))>, basic_string<CharT>>);
|
||||
|
||||
check(SV(R"(('*', ""))"), SV("{}"), input);
|
||||
|
||||
// Char
|
||||
get<0>(input) = CharT('\t');
|
||||
check(SV(R"(('\t', ""))"), SV("{}"), input);
|
||||
get<0>(input) = CharT('\n');
|
||||
check(SV(R"(('\n', ""))"), SV("{}"), input);
|
||||
get<0>(input) = CharT('\0');
|
||||
check(SV(R"(('\u{0}', ""))"), SV("{}"), input);
|
||||
|
||||
// String
|
||||
get<0>(input) = CharT('*');
|
||||
get<1>(input) = SV("hell\u00d6");
|
||||
check(SV("('*', \"hell\u00d6\")"), SV("{}"), input);
|
||||
}
|
||||
|
||||
//
|
||||
// pair tests
|
||||
//
|
||||
|
||||
template <class CharT, class TestFunction, class ExceptionTest>
|
||||
void test_pair_int_int(TestFunction check, ExceptionTest check_exception) {
|
||||
test_tuple_or_pair_int_int<CharT>(check, check_exception, make_pair(42, 99));
|
||||
}
|
||||
|
||||
template <class CharT, class TestFunction, class ExceptionTest>
|
||||
void test_pair_int_string(TestFunction check, ExceptionTest check_exception) {
|
||||
test_tuple_or_pair_int_string<CharT>(check, check_exception, make_pair(42, SV("hello")));
|
||||
test_tuple_or_pair_int_string<CharT>(check, check_exception, make_pair(42, STR("hello")));
|
||||
test_tuple_or_pair_int_string<CharT>(check, check_exception, make_pair(42, CSTR("hello")));
|
||||
}
|
||||
|
||||
//
|
||||
// tuple tests
|
||||
//
|
||||
|
||||
template <class CharT, class TestFunction, class ExceptionTest>
|
||||
void test_tuple_int(TestFunction check, ExceptionTest check_exception) {
|
||||
auto input = make_tuple(42);
|
||||
|
||||
check(SV("(42)"), SV("{}"), input);
|
||||
check(SV("(42)^42"), SV("{}^42"), input);
|
||||
check(SV("(42)^42"), SV("{:}^42"), input);
|
||||
|
||||
// *** align-fill & width ***
|
||||
check(SV("(42) "), SV("{:9}"), input);
|
||||
check(SV("(42)*****"), SV("{:*<9}"), input);
|
||||
check(SV("__(42)___"), SV("{:_^9}"), input);
|
||||
check(SV("#####(42)"), SV("{:#>9}"), input);
|
||||
|
||||
check(SV("(42) "), SV("{:{}}"), input, 9);
|
||||
check(SV("(42)*****"), SV("{:*<{}}"), input, 9);
|
||||
check(SV("__(42)___"), SV("{:_^{}}"), input, 9);
|
||||
check(SV("#####(42)"), SV("{:#>{}}"), input, 9);
|
||||
|
||||
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
|
||||
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
|
||||
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
|
||||
|
||||
// *** sign ***
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{:+}"), input);
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{: }"), input);
|
||||
|
||||
// *** alternate form ***
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{:#}"), input);
|
||||
|
||||
// *** zero-padding ***
|
||||
check_exception("The width option should not have a leading zero", SV("{:0}"), input);
|
||||
|
||||
// *** precision ***
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{:.}"), input);
|
||||
|
||||
// *** locale-specific form ***
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{:L}"), input);
|
||||
|
||||
// *** type ***
|
||||
check_exception("Type m requires a pair or a tuple with two elements", SV("{:m}"), input);
|
||||
check(SV("__42___"), SV("{:_^7n}"), input);
|
||||
|
||||
for (CharT c : SV("aAbBcdeEfFgGopPsxX?")) {
|
||||
check_exception("The format specifier should consume the input or end with a '}'",
|
||||
basic_string_view{STR("{:") + c + STR("}")}, input);
|
||||
}
|
||||
}
|
||||
|
||||
template <class CharT, class TestFunction, class ExceptionTest>
|
||||
void test_tuple_int_string_color(TestFunction check, ExceptionTest check_exception) {
|
||||
const auto input = make_tuple(42, SV("hello"), color::red);
|
||||
|
||||
check(SV("(42, \"hello\", \"red\")"), SV("{}"), input);
|
||||
check(SV("(42, \"hello\", \"red\")^42"), SV("{}^42"), input);
|
||||
check(SV("(42, \"hello\", \"red\")^42"), SV("{:}^42"), input);
|
||||
|
||||
// *** align-fill & width ***
|
||||
check(SV("(42, \"hello\", \"red\") "), SV("{:25}"), input);
|
||||
check(SV("(42, \"hello\", \"red\")*****"), SV("{:*<25}"), input);
|
||||
check(SV("__(42, \"hello\", \"red\")___"), SV("{:_^25}"), input);
|
||||
check(SV("#####(42, \"hello\", \"red\")"), SV("{:#>25}"), input);
|
||||
|
||||
check(SV("(42, \"hello\", \"red\") "), SV("{:{}}"), input, 25);
|
||||
check(SV("(42, \"hello\", \"red\")*****"), SV("{:*<{}}"), input, 25);
|
||||
check(SV("__(42, \"hello\", \"red\")___"), SV("{:_^{}}"), input, 25);
|
||||
check(SV("#####(42, \"hello\", \"red\")"), SV("{:#>{}}"), input, 25);
|
||||
|
||||
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
|
||||
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
|
||||
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
|
||||
|
||||
// *** sign ***
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{:+}"), input);
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{: }"), input);
|
||||
|
||||
// *** alternate form ***
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{:#}"), input);
|
||||
|
||||
// *** zero-padding ***
|
||||
check_exception("The width option should not have a leading zero", SV("{:0}"), input);
|
||||
|
||||
// *** precision ***
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{:.}"), input);
|
||||
|
||||
// *** locale-specific form ***
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{:L}"), input);
|
||||
|
||||
// *** type ***
|
||||
check_exception("Type m requires a pair or a tuple with two elements", SV("{:m}"), input);
|
||||
check(SV("__42, \"hello\", \"red\"___"), SV("{:_^23n}"), input);
|
||||
|
||||
for (CharT c : SV("aAbBcdeEfFgGopPsxX?")) {
|
||||
check_exception("The format specifier should consume the input or end with a '}'",
|
||||
basic_string_view{STR("{:") + c + STR("}")}, input);
|
||||
}
|
||||
}
|
||||
|
||||
template <class CharT, class TestFunction, class ExceptionTest>
|
||||
void test_tuple_int_int(TestFunction check, ExceptionTest check_exception) {
|
||||
test_tuple_or_pair_int_int<CharT>(check, check_exception, make_tuple(42, 99));
|
||||
}
|
||||
|
||||
template <class CharT, class TestFunction, class ExceptionTest>
|
||||
void test_tuple_int_string(TestFunction check, ExceptionTest check_exception) {
|
||||
test_tuple_or_pair_int_string<CharT>(check, check_exception, make_tuple(42, SV("hello")));
|
||||
test_tuple_or_pair_int_string<CharT>(check, check_exception, make_tuple(42, STR("hello")));
|
||||
test_tuple_or_pair_int_string<CharT>(check, check_exception, make_tuple(42, CSTR("hello")));
|
||||
}
|
||||
|
||||
template <class CharT, class TestFunction, class ExceptionTest, class Nested>
|
||||
void test_nested(TestFunction check, ExceptionTest check_exception, Nested&& input) {
|
||||
// N4971 [format.formatter.spec]/2
|
||||
// A debug-enabled specialization of formatter additionally provides a
|
||||
// public, constexpr, non-static member function set_debug_format()
|
||||
// which modifies the state of the formatter to be as if the type of the
|
||||
// std-format-spec parsed by the last call to parse were ?.
|
||||
// pair and tuple are not debug-enabled specializations so the
|
||||
// set_debug_format is not propagated. The paper
|
||||
// P2733 Fix handling of empty specifiers in format
|
||||
// addressed this.
|
||||
|
||||
check(SV("(42, (\"hello\", \"red\"))"), SV("{}"), input);
|
||||
check(SV("(42, (\"hello\", \"red\"))^42"), SV("{}^42"), input);
|
||||
check(SV("(42, (\"hello\", \"red\"))^42"), SV("{:}^42"), input);
|
||||
|
||||
// *** align-fill & width ***
|
||||
check(SV("(42, (\"hello\", \"red\")) "), SV("{:27}"), input);
|
||||
check(SV("(42, (\"hello\", \"red\"))*****"), SV("{:*<27}"), input);
|
||||
check(SV("__(42, (\"hello\", \"red\"))___"), SV("{:_^27}"), input);
|
||||
check(SV("#####(42, (\"hello\", \"red\"))"), SV("{:#>27}"), input);
|
||||
|
||||
check(SV("(42, (\"hello\", \"red\")) "), SV("{:{}}"), input, 27);
|
||||
check(SV("(42, (\"hello\", \"red\"))*****"), SV("{:*<{}}"), input, 27);
|
||||
check(SV("__(42, (\"hello\", \"red\"))___"), SV("{:_^{}}"), input, 27);
|
||||
check(SV("#####(42, (\"hello\", \"red\"))"), SV("{:#>{}}"), input, 27);
|
||||
|
||||
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
|
||||
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
|
||||
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
|
||||
|
||||
// *** sign ***
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{:+}"), input);
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{: }"), input);
|
||||
|
||||
// *** alternate form ***
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{:#}"), input);
|
||||
|
||||
// *** zero-padding ***
|
||||
check_exception("The width option should not have a leading zero", SV("{:0}"), input);
|
||||
|
||||
// *** precision ***
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{:.}"), input);
|
||||
|
||||
// *** locale-specific form ***
|
||||
check_exception("The format specifier should consume the input or end with a '}'", SV("{:L}"), input);
|
||||
|
||||
// *** type ***
|
||||
check(SV("__42: (\"hello\", \"red\")___"), SV("{:_^25m}"), input);
|
||||
check(SV("__42, (\"hello\", \"red\")___"), SV("{:_^25n}"), input);
|
||||
|
||||
for (CharT c : SV("aAbBcdeEfFgGopPsxX?")) {
|
||||
check_exception("The format specifier should consume the input or end with a '}'",
|
||||
basic_string_view{STR("{:") + c + STR("}")}, input);
|
||||
}
|
||||
}
|
||||
|
||||
template <class CharT, class TestFunction, class ExceptionTest>
|
||||
void run_tests(TestFunction check, ExceptionTest check_exception) {
|
||||
test_pair_int_int<CharT>(check, check_exception);
|
||||
test_pair_int_string<CharT>(check, check_exception);
|
||||
|
||||
test_tuple_int<CharT>(check, check_exception);
|
||||
test_tuple_int_int<CharT>(check, check_exception);
|
||||
test_tuple_int_string<CharT>(check, check_exception);
|
||||
test_tuple_int_string_color<CharT>(check, check_exception);
|
||||
|
||||
test_nested<CharT>(check, check_exception, make_pair(42, make_pair(SV("hello"), color::red)));
|
||||
test_nested<CharT>(check, check_exception, make_pair(42, make_tuple(SV("hello"), color::red)));
|
||||
test_nested<CharT>(check, check_exception, make_tuple(42, make_pair(SV("hello"), color::red)));
|
||||
test_nested<CharT>(check, check_exception, make_tuple(42, make_tuple(SV("hello"), color::red)));
|
||||
|
||||
test_escaping<CharT>(check, make_pair(CharT('*'), STR("")));
|
||||
test_escaping<CharT>(check, make_tuple(CharT('*'), STR("")));
|
||||
|
||||
// Test const ref-qualified types.
|
||||
// clang-format off
|
||||
check(SV("(42)"), SV("{}"), tuple< int >{42});
|
||||
check(SV("(42)"), SV("{}"), tuple<const int >{42});
|
||||
|
||||
int answer = 42;
|
||||
check(SV("(42)"), SV("{}"), tuple< int& >{answer});
|
||||
check(SV("(42)"), SV("{}"), tuple<const int& >{answer});
|
||||
|
||||
check(SV("(42)"), SV("{}"), tuple< int&&>{42});
|
||||
check(SV("(42)"), SV("{}"), tuple<const int&&>{42});
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
template <class>
|
||||
struct format_context_for_impl {};
|
||||
|
||||
template <>
|
||||
struct format_context_for_impl<char> {
|
||||
using type = format_context;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct format_context_for_impl<wchar_t> {
|
||||
using type = wformat_context;
|
||||
};
|
||||
|
||||
template <class CharT>
|
||||
using format_context_for = format_context_for_impl<CharT>::type;
|
||||
|
||||
auto test_format = []<class CharT, class... Args>(basic_string_view<CharT> expected,
|
||||
type_identity_t<basic_format_string<CharT, Args...>> fmt, Args&&... args) {
|
||||
auto out = format(fmt, forward<Args>(args)...);
|
||||
assert(out == expected);
|
||||
};
|
||||
|
||||
auto test_format_exception = []<class CharT, class... Args>(string_view, basic_string_view<CharT>, Args&&...) {
|
||||
// After P2216 most exceptions thrown by format become ill-formed.
|
||||
// Therefore this test does nothing.
|
||||
// A basic ill-formed test is done in format.verify.cpp
|
||||
// The exceptions are tested by other functions that don't use the basic-format-string as fmt argument.
|
||||
};
|
||||
|
||||
auto test_vformat = []<class CharT, class... Args>(
|
||||
basic_string_view<CharT> expected, basic_string_view<CharT> fmt, Args&&... args) {
|
||||
auto out = vformat(fmt, make_format_args<format_context_for<CharT>>(args...));
|
||||
assert(out == expected);
|
||||
};
|
||||
|
||||
auto test_vformat_exception = []<class CharT, class... Args>([[maybe_unused]] string_view what,
|
||||
[[maybe_unused]] basic_string_view<CharT> fmt, [[maybe_unused]] Args&&... args) {
|
||||
try {
|
||||
(void) vformat(fmt, make_format_args<format_context_for<CharT>>(args...));
|
||||
assert(false);
|
||||
} catch (const format_error&) {
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
run_tests<char>(test_format, test_format_exception);
|
||||
run_tests<char>(test_vformat, test_vformat_exception);
|
||||
|
||||
run_tests<wchar_t>(test_format, test_format_exception);
|
||||
run_tests<wchar_t>(test_vformat, test_vformat_exception);
|
||||
}
|
Загрузка…
Ссылка в новой задаче