[libc++][format] Adds formatter for tuple and pair
Implements parts of - P2286R8 Formatting Ranges Reviewed By: ldionne, #libc Differential Revision: https://reviews.llvm.org/D136775
This commit is contained in:
@@ -33,5 +33,5 @@ Section,Description,Dependencies,Assignee,Status,First released version
|
||||
`[format.range] <https://wg21.link/format.range>`_,"Formatting for ranges: sequences",,Mark de Wever,|In Progress|,
|
||||
`[format.range] <https://wg21.link/format.range>`_,"Formatting for ranges: associative",,Mark de Wever,,
|
||||
`[format.range] <https://wg21.link/format.range>`_,"Formatting for ranges: container adaptors",,Mark de Wever,,
|
||||
`[format.range] <https://wg21.link/format.range>`_,"Formatting for ranges: ``pair`` and ``tuple``",,Mark de Wever,|In Progress|,
|
||||
`[format.range] <https://wg21.link/format.range>`_,"Formatting for ranges: ``pair`` and ``tuple``",,Mark de Wever,|Complete|,Clang 16
|
||||
`[format.range] <https://wg21.link/format.range>`_,"Formatting for ranges: ``vector<bool>``",,Mark de Wever,,
|
||||
|
||||
|
@@ -313,6 +313,7 @@ set(files
|
||||
__format/formatter_output.h
|
||||
__format/formatter_pointer.h
|
||||
__format/formatter_string.h
|
||||
__format/formatter_tuple.h
|
||||
__format/parser_std_format_spec.h
|
||||
__format/range_default_formatter.h
|
||||
__format/unicode.h
|
||||
|
||||
@@ -26,26 +26,26 @@ _LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
|
||||
template <__fmt_char_type _CharT>
|
||||
consteval const _CharT* __statically_widen(const char* __str, const wchar_t* __wstr) {
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr const _CharT* __statically_widen(const char* __str, const wchar_t* __wstr) {
|
||||
if constexpr (same_as<_CharT, char>)
|
||||
return __str;
|
||||
else
|
||||
return __wstr;
|
||||
}
|
||||
# define _LIBCPP_STATICALLY_WIDEN(_CharT, __str) ::std::__statically_widen<_CharT>(__str, L##__str)
|
||||
# else // _LIBCPP_HAS_NO_WIDE_CHARACTERS
|
||||
# else // _LIBCPP_HAS_NO_WIDE_CHARACTERS
|
||||
|
||||
// Without this indirection the unit test test/libcxx/modules_include.sh.cpp
|
||||
// fails for the CI build "No wide characters". This seems like a bug.
|
||||
// TODO FMT investigate why this is needed.
|
||||
template <__fmt_char_type _CharT>
|
||||
consteval const _CharT* __statically_widen(const char* __str) {
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr const _CharT* __statically_widen(const char* __str) {
|
||||
return __str;
|
||||
}
|
||||
# define _LIBCPP_STATICALLY_WIDEN(_CharT, __str) ::std::__statically_widen<_CharT>(__str)
|
||||
# endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS
|
||||
|
||||
#endif //if _LIBCPP_STD_VER > 17
|
||||
#endif //_LIBCPP_STD_VER > 17
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
|
||||
@@ -38,7 +38,16 @@ struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter {
|
||||
formatter& operator=(const formatter&) = delete;
|
||||
};
|
||||
|
||||
#endif //_LIBCPP_STD_VER > 17
|
||||
# if _LIBCPP_STD_VER > 20
|
||||
|
||||
template <class _Tp>
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr void __set_debug_format(_Tp& __formatter) {
|
||||
if constexpr (requires { __formatter.set_debug_format(); })
|
||||
__formatter.set_debug_format();
|
||||
}
|
||||
|
||||
# endif // _LIBCPP_STD_VER > 20
|
||||
#endif // _LIBCPP_STD_VER > 17
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
|
||||
178
libcxx/include/__format/formatter_tuple.h
Normal file
178
libcxx/include/__format/formatter_tuple.h
Normal file
@@ -0,0 +1,178 @@
|
||||
// -*- C++ -*-
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _LIBCPP___FORMAT_FORMATTER_TUPLE_H
|
||||
#define _LIBCPP___FORMAT_FORMATTER_TUPLE_H
|
||||
|
||||
#include <__algorithm/ranges_copy.h>
|
||||
#include <__availability>
|
||||
#include <__chrono/statically_widen.h>
|
||||
#include <__config>
|
||||
#include <__format/concepts.h>
|
||||
#include <__format/format_args.h>
|
||||
#include <__format/format_context.h>
|
||||
#include <__format/format_error.h>
|
||||
#include <__format/format_parse_context.h>
|
||||
#include <__format/formatter.h>
|
||||
#include <__format/formatter_output.h>
|
||||
#include <__format/parser_std_format_spec.h>
|
||||
#include <__iterator/back_insert_iterator.h>
|
||||
#include <__type_traits/remove_cvref.h>
|
||||
#include <__utility/integer_sequence.h>
|
||||
#include <__utility/pair.h>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
# pragma GCC system_header
|
||||
#endif
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
#if _LIBCPP_STD_VER > 20
|
||||
|
||||
template <__fmt_char_type _CharT, class _Tuple, formattable<_CharT>... _Args>
|
||||
struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT __formatter_tuple {
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr void set_separator(basic_string_view<_CharT> __separator) {
|
||||
__separator_ = __separator;
|
||||
}
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr void
|
||||
set_brackets(basic_string_view<_CharT> __opening_bracket, basic_string_view<_CharT> __closing_bracket) {
|
||||
__opening_bracket_ = __opening_bracket;
|
||||
__closing_bracket_ = __closing_bracket;
|
||||
}
|
||||
|
||||
template <class _ParseContext>
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __parse_ctx) {
|
||||
const _CharT* __begin = __parser_.__parse(__parse_ctx, __format_spec::__fields_tuple);
|
||||
|
||||
// [format.tuple]/7
|
||||
// ... For each element e in underlying_, if e.set_debug_format()
|
||||
// is a valid expression, calls e.set_debug_format().
|
||||
// TODO FMT this can be removed when P2733 is accepted.
|
||||
std::__for_each_index_sequence(make_index_sequence<sizeof...(_Args)>(), [&]<size_t _Index> {
|
||||
std::__set_debug_format(std::get<_Index>(__underlying_));
|
||||
});
|
||||
|
||||
const _CharT* __end = __parse_ctx.end();
|
||||
if (__begin == __end)
|
||||
return __begin;
|
||||
|
||||
if (*__begin == _CharT('m')) {
|
||||
if constexpr (sizeof...(_Args) == 2) {
|
||||
set_separator(_LIBCPP_STATICALLY_WIDEN(_CharT, ": "));
|
||||
set_brackets({}, {});
|
||||
++__begin;
|
||||
} else
|
||||
std::__throw_format_error("The format specifier m requires a pair or a two-element tuple");
|
||||
} else if (*__begin == _CharT('n')) {
|
||||
set_brackets({}, {});
|
||||
++__begin;
|
||||
}
|
||||
|
||||
if (__begin != __end && *__begin != _CharT('}'))
|
||||
std::__throw_format_error("The format-spec should consume the input or end with a '}'");
|
||||
|
||||
return __begin;
|
||||
}
|
||||
|
||||
template <class _FormatContext>
|
||||
typename _FormatContext::iterator _LIBCPP_HIDE_FROM_ABI
|
||||
format(conditional_t<(formattable<const _Args, _CharT> && ...), const _Tuple&, _Tuple&> __tuple,
|
||||
_FormatContext& __ctx) const {
|
||||
__format_spec::__parsed_specifications<_CharT> __specs = __parser_.__get_parsed_std_specifications(__ctx);
|
||||
|
||||
if (!__specs.__has_width())
|
||||
return __format_tuple(__tuple, __ctx);
|
||||
|
||||
basic_string<_CharT> __str;
|
||||
|
||||
// Since the output is written to a different iterator a new context is
|
||||
// created. Since the underlying formatter uses the default formatting it
|
||||
// doesn't need a locale or the formatting arguments. So creating a new
|
||||
// context works.
|
||||
//
|
||||
// This solution works for this formatter, but it will not work for the
|
||||
// range_formatter. In that patch a generic solution is work in progress.
|
||||
// Once that is finished it can be used here. (The range_formatter will use
|
||||
// these features so it's easier to add it there and then port it.)
|
||||
//
|
||||
// TODO FMT Use formatting wrapping used in the range_formatter.
|
||||
basic_format_context __c = std::__format_context_create(
|
||||
back_insert_iterator{__str},
|
||||
basic_format_args<basic_format_context<back_insert_iterator<basic_string<_CharT>>, _CharT>>{});
|
||||
|
||||
__format_tuple(__tuple, __c);
|
||||
|
||||
return __formatter::__write_string_no_precision(basic_string_view{__str}, __ctx.out(), __specs);
|
||||
}
|
||||
|
||||
template <class _FormatContext>
|
||||
_LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator __format_tuple(auto&& __tuple, _FormatContext& __ctx) const {
|
||||
__ctx.advance_to(std::ranges::copy(__opening_bracket_, __ctx.out()).out);
|
||||
|
||||
std::__for_each_index_sequence(make_index_sequence<sizeof...(_Args)>(), [&]<size_t _Index> {
|
||||
if constexpr (_Index)
|
||||
__ctx.advance_to(std::ranges::copy(__separator_, __ctx.out()).out);
|
||||
|
||||
// During review Victor suggested to make the exposition only
|
||||
// __underlying_ member a local variable. Currently the Standard
|
||||
// requires nested debug-enabled formatter specializations not to
|
||||
// output escaped output. P2733 fixes that bug, once accepted the
|
||||
// code below can be used.
|
||||
// (Note when a paper allows parsing a tuple-underlying-spec the
|
||||
// exposition only member needs to be a class member. Earlier
|
||||
// revisions of P2286 proposed that, but this was not pursued,
|
||||
// due to time constrains and complexity of the matter.)
|
||||
// TODO FMT This can be updated after P2733 is accepted.
|
||||
# if 0
|
||||
// P2286 uses an exposition only member in the formatter
|
||||
// tuple<formatter<remove_cvref_t<_Args>, _CharT>...> __underlying_;
|
||||
// This was used in earlier versions of the paper since
|
||||
// __underlying_.parse(...) was called. This is no longer the case
|
||||
// so we can reduce the scope of the formatter.
|
||||
//
|
||||
// It does require the underlying's parse effect to be moved here too.
|
||||
using _Arg = tuple_element<_Index, decltype(__tuple)>;
|
||||
formatter<remove_cvref_t<_Args>, _CharT> __underlying;
|
||||
|
||||
// [format.tuple]/7
|
||||
// ... For each element e in underlying_, if e.set_debug_format()
|
||||
// is a valid expression, calls e.set_debug_format().
|
||||
std::__set_debug_format(__underlying);
|
||||
# else
|
||||
__ctx.advance_to(std::get<_Index>(__underlying_).format(std::get<_Index>(__tuple), __ctx));
|
||||
# endif
|
||||
});
|
||||
|
||||
return std::ranges::copy(__closing_bracket_, __ctx.out()).out;
|
||||
}
|
||||
|
||||
__format_spec::__parser<_CharT> __parser_{.__alignment_ = __format_spec::__alignment::__left};
|
||||
|
||||
private:
|
||||
tuple<formatter<remove_cvref_t<_Args>, _CharT>...> __underlying_;
|
||||
basic_string_view<_CharT> __separator_ = _LIBCPP_STATICALLY_WIDEN(_CharT, ", ");
|
||||
basic_string_view<_CharT> __opening_bracket_ = _LIBCPP_STATICALLY_WIDEN(_CharT, "(");
|
||||
basic_string_view<_CharT> __closing_bracket_ = _LIBCPP_STATICALLY_WIDEN(_CharT, ")");
|
||||
};
|
||||
|
||||
template <__fmt_char_type _CharT, formattable<_CharT>... _Args>
|
||||
struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<pair<_Args...>, _CharT>
|
||||
: public __formatter_tuple<_CharT, pair<_Args...>, _Args...> {};
|
||||
|
||||
template <__fmt_char_type _CharT, formattable<_CharT>... _Args>
|
||||
struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<tuple<_Args...>, _CharT>
|
||||
: public __formatter_tuple<_CharT, tuple<_Args...>, _Args...> {};
|
||||
|
||||
#endif //_LIBCPP_STD_VER > 20
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
#endif // _LIBCPP___FORMAT_FORMATTER_TUPLE_H
|
||||
@@ -101,6 +101,7 @@ __substitute_arg_id(basic_format_arg<_Context> __format_arg) {
|
||||
///
|
||||
/// They default to false so when a new field is added it needs to be opted in
|
||||
/// explicitly.
|
||||
// TODO FMT Use an ABI tag for this struct.
|
||||
struct __fields {
|
||||
uint8_t __sign_ : 1 {false};
|
||||
uint8_t __alternate_form_ : 1 {false};
|
||||
@@ -108,6 +109,13 @@ struct __fields {
|
||||
uint8_t __precision_ : 1 {false};
|
||||
uint8_t __locale_specific_form_ : 1 {false};
|
||||
uint8_t __type_ : 1 {false};
|
||||
// Determines the valid values for fill.
|
||||
//
|
||||
// Originally the fill could be any character except { and }. Range-based
|
||||
// formatters use the colon to mark the beginning of the
|
||||
// underlying-format-spec. To avoid parsing ambiguities these formatter
|
||||
// specializations prohibit the use of the colon as a fill character.
|
||||
uint8_t __allow_colon_in_fill_ : 1 {false};
|
||||
};
|
||||
|
||||
// By not placing this constant in the formatter class it's not duplicated for
|
||||
@@ -128,6 +136,10 @@ inline constexpr __fields __fields_floating_point{
|
||||
inline constexpr __fields __fields_string{.__precision_ = true, .__type_ = true};
|
||||
inline constexpr __fields __fields_pointer{.__type_ = true};
|
||||
|
||||
# if _LIBCPP_STD_VER > 20
|
||||
inline constexpr __fields __fields_tuple{.__type_ = false, .__allow_colon_in_fill_ = true};
|
||||
# endif
|
||||
|
||||
enum class _LIBCPP_ENUM_VIS __alignment : uint8_t {
|
||||
/// No alignment is set in the format string.
|
||||
__default,
|
||||
@@ -256,7 +268,7 @@ public:
|
||||
if (__begin == __end)
|
||||
return __begin;
|
||||
|
||||
if (__parse_fill_align(__begin, __end) && __begin == __end)
|
||||
if (__parse_fill_align(__begin, __end, __fields.__allow_colon_in_fill_) && __begin == __end)
|
||||
return __begin;
|
||||
|
||||
if (__fields.__sign_ && __parse_sign(__begin) && __begin == __end)
|
||||
@@ -364,12 +376,16 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr bool __parse_fill_align(const _CharT*& __begin, const _CharT* __end) {
|
||||
// range-fill and tuple-fill are identical
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr bool
|
||||
__parse_fill_align(const _CharT*& __begin, const _CharT* __end, bool __use_range_fill) {
|
||||
_LIBCPP_ASSERT(__begin != __end, "when called with an empty input the function will cause "
|
||||
"undefined behavior by evaluating data not in the input");
|
||||
if (__begin + 1 != __end) {
|
||||
if (__parse_alignment(*(__begin + 1))) {
|
||||
if (*__begin == _CharT('{') || *__begin == _CharT('}'))
|
||||
if (__use_range_fill && (*__begin == _CharT('{') || *__begin == _CharT('}') || *__begin == _CharT(':')))
|
||||
std::__throw_format_error("The format-spec range-fill field contains an invalid character");
|
||||
else if (*__begin == _CharT('{') || *__begin == _CharT('}'))
|
||||
std::__throw_format_error("The format-spec fill field contains an invalid character");
|
||||
|
||||
__fill_ = *__begin;
|
||||
|
||||
@@ -137,6 +137,14 @@ template<size_t _Np>
|
||||
template<class... _Tp>
|
||||
using index_sequence_for = make_index_sequence<sizeof...(_Tp)>;
|
||||
|
||||
# if _LIBCPP_STD_VER > 17
|
||||
// Executes __func for every element in an index_sequence.
|
||||
template <size_t... _Index, class _Function>
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr void __for_each_index_sequence(index_sequence<_Index...>, _Function __func) {
|
||||
(__func.template operator()<_Index>(), ...);
|
||||
}
|
||||
# endif // _LIBCPP_STD_VER > 17
|
||||
|
||||
#endif // _LIBCPP_STD_VER > 11
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
@@ -191,6 +191,7 @@ namespace std {
|
||||
#include <__format/formatter_integer.h>
|
||||
#include <__format/formatter_pointer.h>
|
||||
#include <__format/formatter_string.h>
|
||||
#include <__format/formatter_tuple.h>
|
||||
#include <__format/parser_std_format_spec.h>
|
||||
#include <__format/range_default_formatter.h>
|
||||
#include <__format/unicode.h>
|
||||
|
||||
@@ -856,6 +856,7 @@ module std [system] {
|
||||
module formatter_output { private header "__format/formatter_output.h" }
|
||||
module formatter_pointer { private header "__format/formatter_pointer.h" }
|
||||
module formatter_string { private header "__format/formatter_string.h" }
|
||||
module formatter_tuple { private header "__format/formatter_tuple.h" }
|
||||
module parser_std_format_spec { private header "__format/parser_std_format_spec.h" }
|
||||
module range_default_formatter { private header "__format/range_default_formatter.h" }
|
||||
module unicode { private header "__format/unicode.h" }
|
||||
|
||||
@@ -345,6 +345,7 @@ END-SCRIPT
|
||||
#include <__format/formatter_output.h> // expected-error@*:* {{use of private header from outside its module: '__format/formatter_output.h'}}
|
||||
#include <__format/formatter_pointer.h> // expected-error@*:* {{use of private header from outside its module: '__format/formatter_pointer.h'}}
|
||||
#include <__format/formatter_string.h> // expected-error@*:* {{use of private header from outside its module: '__format/formatter_string.h'}}
|
||||
#include <__format/formatter_tuple.h> // expected-error@*:* {{use of private header from outside its module: '__format/formatter_tuple.h'}}
|
||||
#include <__format/parser_std_format_spec.h> // expected-error@*:* {{use of private header from outside its module: '__format/parser_std_format_spec.h'}}
|
||||
#include <__format/range_default_formatter.h> // expected-error@*:* {{use of private header from outside its module: '__format/range_default_formatter.h'}}
|
||||
#include <__format/unicode.h> // expected-error@*:* {{use of private header from outside its module: '__format/unicode.h'}}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17
|
||||
|
||||
// <utility>
|
||||
|
||||
// inline constexpr auto __for_each_index_sequence = []<size_t... _Index>(index_sequence<_Index...>, auto __func)
|
||||
|
||||
#include <utility>
|
||||
#include <cassert>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
constexpr bool test() {
|
||||
int count = 0;
|
||||
std::__for_each_index_sequence(std::make_index_sequence<8>(), [&]<size_t _Index> { count += _Index; });
|
||||
assert(count == 28);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -224,8 +224,8 @@ void test_P2286() {
|
||||
|
||||
assert_is_not_formattable<std::valarray<int>, CharT>();
|
||||
|
||||
assert_is_not_formattable<std::pair<int, int>, CharT>();
|
||||
assert_is_not_formattable<std::tuple<int>, CharT>();
|
||||
assert_is_formattable<std::pair<int, int>, CharT>();
|
||||
assert_is_formattable<std::tuple<int>, CharT>();
|
||||
}
|
||||
|
||||
class c {
|
||||
|
||||
@@ -207,7 +207,8 @@ void format_test_string(const W& world, const U& universe, TestFunction check, E
|
||||
check(SV("hello _world__"), SV("hello {:_^8}"), world);
|
||||
check(SV("hello world___"), SV("hello {:_<8}"), world);
|
||||
|
||||
check(SV("hello >>>world"), SV("hello {:>>8}"), world);
|
||||
// The fill character ':' is allowed here (P0645) but not in ranges (P2286).
|
||||
check(SV("hello :::world"), SV("hello {::>8}"), world);
|
||||
check(SV("hello <<<world"), SV("hello {:<>8}"), world);
|
||||
check(SV("hello ^^^world"), SV("hello {:^>8}"), world);
|
||||
|
||||
@@ -441,9 +442,10 @@ void format_test_bool(TestFunction check, ExceptionTest check_exception) {
|
||||
check(SV("answer is 'false '"), SV("answer is '{:<8s}'"), false);
|
||||
check(SV("answer is ' false '"), SV("answer is '{:^8s}'"), false);
|
||||
|
||||
check(SV("answer is '---true'"), SV("answer is '{:->7}'"), true);
|
||||
check(SV("answer is 'true---'"), SV("answer is '{:-<7}'"), true);
|
||||
check(SV("answer is '-true--'"), SV("answer is '{:-^7}'"), true);
|
||||
// The fill character ':' is allowed here (P0645) but not in ranges (P2286).
|
||||
check(SV("answer is ':::true'"), SV("answer is '{::>7}'"), true);
|
||||
check(SV("answer is 'true:::'"), SV("answer is '{::<7}'"), true);
|
||||
check(SV("answer is ':true::'"), SV("answer is '{::^7}'"), true);
|
||||
|
||||
check(SV("answer is '---false'"), SV("answer is '{:->8s}'"), false);
|
||||
check(SV("answer is 'false---'"), SV("answer is '{:-<8s}'"), false);
|
||||
@@ -495,9 +497,10 @@ void format_test_bool_as_integer(TestFunction check, ExceptionTest check_excepti
|
||||
check(SV("answer is '1 '"), SV("answer is '{:<6d}'"), true);
|
||||
check(SV("answer is ' 1 '"), SV("answer is '{:^6d}'"), true);
|
||||
|
||||
check(SV("answer is '*****0'"), SV("answer is '{:*>6d}'"), false);
|
||||
check(SV("answer is '0*****'"), SV("answer is '{:*<6d}'"), false);
|
||||
check(SV("answer is '**0***'"), SV("answer is '{:*^6d}'"), false);
|
||||
// The fill character ':' is allowed here (P0645) but not in ranges (P2286).
|
||||
check(SV("answer is ':::::0'"), SV("answer is '{::>6d}'"), false);
|
||||
check(SV("answer is '0:::::'"), SV("answer is '{::<6d}'"), false);
|
||||
check(SV("answer is '::0:::'"), SV("answer is '{::^6d}'"), false);
|
||||
|
||||
// Test whether zero padding is ignored
|
||||
check(SV("answer is ' 1'"), SV("answer is '{:>06d}'"), true);
|
||||
@@ -581,9 +584,10 @@ void format_test_integer_as_integer(TestFunction check, ExceptionTest check_exce
|
||||
check(SV("answer is '42 '"), SV("answer is '{:<7}'"), I(42));
|
||||
check(SV("answer is ' 42 '"), SV("answer is '{:^7}'"), I(42));
|
||||
|
||||
check(SV("answer is '*****42'"), SV("answer is '{:*>7}'"), I(42));
|
||||
check(SV("answer is '42*****'"), SV("answer is '{:*<7}'"), I(42));
|
||||
check(SV("answer is '**42***'"), SV("answer is '{:*^7}'"), I(42));
|
||||
// The fill character ':' is allowed here (P0645) but not in ranges (P2286).
|
||||
check(SV("answer is ':::::42'"), SV("answer is '{::>7}'"), I(42));
|
||||
check(SV("answer is '42:::::'"), SV("answer is '{::<7}'"), I(42));
|
||||
check(SV("answer is '::42:::'"), SV("answer is '{::^7}'"), I(42));
|
||||
|
||||
// Test whether zero padding is ignored
|
||||
check(SV("answer is ' 42'"), SV("answer is '{:>07}'"), I(42));
|
||||
@@ -709,9 +713,10 @@ void format_test_integer_as_char(TestFunction check, ExceptionTest check_excepti
|
||||
check(SV("answer is '* '"), SV("answer is '{:<6c}'"), I(42));
|
||||
check(SV("answer is ' * '"), SV("answer is '{:^6c}'"), I(42));
|
||||
|
||||
check(SV("answer is '-----*'"), SV("answer is '{:->6c}'"), I(42));
|
||||
check(SV("answer is '*-----'"), SV("answer is '{:-<6c}'"), I(42));
|
||||
check(SV("answer is '--*---'"), SV("answer is '{:-^6c}'"), I(42));
|
||||
// The fill character ':' is allowed here (P0645) but not in ranges (P2286).
|
||||
check(SV("answer is ':::::*'"), SV("answer is '{::>6c}'"), I(42));
|
||||
check(SV("answer is '*:::::'"), SV("answer is '{::<6c}'"), I(42));
|
||||
check(SV("answer is '::*:::'"), SV("answer is '{::^6c}'"), I(42));
|
||||
|
||||
// *** Sign ***
|
||||
check(SV("answer is *"), SV("answer is {:c}"), I(42));
|
||||
@@ -893,9 +898,10 @@ void format_test_char(TestFunction check, ExceptionTest check_exception) {
|
||||
check(SV("answer is '* '"), SV("answer is '{:<6c}'"), CharT('*'));
|
||||
check(SV("answer is ' * '"), SV("answer is '{:^6c}'"), CharT('*'));
|
||||
|
||||
check(SV("answer is '-----*'"), SV("answer is '{:->6}'"), CharT('*'));
|
||||
check(SV("answer is '*-----'"), SV("answer is '{:-<6}'"), CharT('*'));
|
||||
check(SV("answer is '--*---'"), SV("answer is '{:-^6}'"), CharT('*'));
|
||||
// The fill character ':' is allowed here (P0645) but not in ranges (P2286).
|
||||
check(SV("answer is ':::::*'"), SV("answer is '{::>6}'"), CharT('*'));
|
||||
check(SV("answer is '*:::::'"), SV("answer is '{::<6}'"), CharT('*'));
|
||||
check(SV("answer is '::*:::'"), SV("answer is '{::^6}'"), CharT('*'));
|
||||
|
||||
check(SV("answer is '-----*'"), SV("answer is '{:->6c}'"), CharT('*'));
|
||||
check(SV("answer is '*-----'"), SV("answer is '{:-<6c}'"), CharT('*'));
|
||||
@@ -955,9 +961,10 @@ void format_test_char_as_integer(TestFunction check, ExceptionTest check_excepti
|
||||
check(SV("answer is '42 '"), SV("answer is '{:<7d}'"), CharT('*'));
|
||||
check(SV("answer is ' 42 '"), SV("answer is '{:^7d}'"), CharT('*'));
|
||||
|
||||
check(SV("answer is '*****42'"), SV("answer is '{:*>7d}'"), CharT('*'));
|
||||
check(SV("answer is '42*****'"), SV("answer is '{:*<7d}'"), CharT('*'));
|
||||
check(SV("answer is '**42***'"), SV("answer is '{:*^7d}'"), CharT('*'));
|
||||
// The fill character ':' is allowed here (P0645) but not in ranges (P2286).
|
||||
check(SV("answer is ':::::42'"), SV("answer is '{::>7d}'"), CharT('*'));
|
||||
check(SV("answer is '42:::::'"), SV("answer is '{::<7d}'"), CharT('*'));
|
||||
check(SV("answer is '::42:::'"), SV("answer is '{::^7d}'"), CharT('*'));
|
||||
|
||||
// Test whether zero padding is ignored
|
||||
check(SV("answer is ' 42'"), SV("answer is '{:>07d}'"), CharT('*'));
|
||||
@@ -1029,9 +1036,10 @@ void format_test_floating_point_hex_lower_case(TestFunction check) {
|
||||
check(SV("answer is '1p-2 '"), SV("answer is '{:<7a}'"), F(0.25));
|
||||
check(SV("answer is ' 1p-2 '"), SV("answer is '{:^7a}'"), F(0.25));
|
||||
|
||||
check(SV("answer is '---1p-3'"), SV("answer is '{:->7a}'"), F(125e-3));
|
||||
check(SV("answer is '1p-3---'"), SV("answer is '{:-<7a}'"), F(125e-3));
|
||||
check(SV("answer is '-1p-3--'"), SV("answer is '{:-^7a}'"), F(125e-3));
|
||||
// The fill character ':' is allowed here (P0645) but not in ranges (P2286).
|
||||
check(SV("answer is ':::1p-3'"), SV("answer is '{::>7a}'"), F(125e-3));
|
||||
check(SV("answer is '1p-3:::'"), SV("answer is '{::<7a}'"), F(125e-3));
|
||||
check(SV("answer is ':1p-3::'"), SV("answer is '{::^7a}'"), F(125e-3));
|
||||
|
||||
check(SV("answer is '***inf'"), SV("answer is '{:*>6a}'"), std::numeric_limits<F>::infinity());
|
||||
check(SV("answer is 'inf***'"), SV("answer is '{:*<6a}'"), std::numeric_limits<F>::infinity());
|
||||
@@ -2591,9 +2599,10 @@ void format_test_pointer(TestFunction check, ExceptionTest check_exception) {
|
||||
check(SV("answer is '0x0 '"), SV("answer is '{:<6}'"), P(nullptr));
|
||||
check(SV("answer is ' 0x0 '"), SV("answer is '{:^6}'"), P(nullptr));
|
||||
|
||||
check(SV("answer is '---0x0'"), SV("answer is '{:->6}'"), P(nullptr));
|
||||
check(SV("answer is '0x0---'"), SV("answer is '{:-<6}'"), P(nullptr));
|
||||
check(SV("answer is '-0x0--'"), SV("answer is '{:-^6}'"), P(nullptr));
|
||||
// The fill character ':' is allowed here (P0645) but not in ranges (P2286).
|
||||
check(SV("answer is ':::0x0'"), SV("answer is '{::>6}'"), P(nullptr));
|
||||
check(SV("answer is '0x0:::'"), SV("answer is '{::<6}'"), P(nullptr));
|
||||
check(SV("answer is ':0x0::'"), SV("answer is '{::^6}'"), P(nullptr));
|
||||
|
||||
// *** Sign ***
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{:-}"), P(nullptr));
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
|
||||
// UNSUPPORTED: libcpp-has-no-incomplete-format
|
||||
|
||||
// This test requires the dylib support introduced in D92214.
|
||||
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{.+}}
|
||||
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx11.{{.+}}
|
||||
|
||||
// <format>
|
||||
|
||||
// template<class charT, formattable<charT>... Ts>
|
||||
// struct formatter<pair-or-tuple<Ts...>, charT>
|
||||
//
|
||||
// tested in the format functions
|
||||
//
|
||||
// template<class... Args>
|
||||
// string format(format-string<Args...> fmt, const Args&... args);
|
||||
// template<class... Args>
|
||||
// wstring format(wformat-string<Args...> fmt, const Args&... args);
|
||||
|
||||
#include <format>
|
||||
#include <cassert>
|
||||
|
||||
#include "format.functions.tests.h"
|
||||
#include "test_format_string.h"
|
||||
#include "test_macros.h"
|
||||
|
||||
#ifndef TEST_HAS_NO_LOCALIZATION
|
||||
# include <iostream>
|
||||
# include <concepts>
|
||||
#endif
|
||||
|
||||
auto test = []<class CharT, class... Args>(
|
||||
std::basic_string_view<CharT> expected, test_format_string<CharT, Args...> fmt, Args&&... args) {
|
||||
std::basic_string<CharT> out = std::format(fmt, std::forward<Args>(args)...);
|
||||
#ifndef TEST_HAS_NO_LOCALIZATION
|
||||
if constexpr (std::same_as<CharT, char>)
|
||||
if (out != expected)
|
||||
std::cerr << "\nFormat string " << fmt.get() << "\nExpected output " << expected << "\nActual output " << out
|
||||
<< '\n';
|
||||
#endif // TEST_HAS_NO_LOCALIZATION
|
||||
assert(out == expected);
|
||||
};
|
||||
|
||||
auto test_exception = []<class CharT, class... Args>(std::string_view, std::basic_string_view<CharT>, Args&&...) {
|
||||
// After P2216 most exceptions thrown by std::format become ill-formed.
|
||||
// Therefore this tests 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.
|
||||
};
|
||||
|
||||
int main(int, char**) {
|
||||
run_tests<char>(test, test_exception);
|
||||
|
||||
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
|
||||
run_tests<wchar_t>(test, test_exception);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
|
||||
// UNSUPPORTED: libcpp-has-no-incomplete-format
|
||||
|
||||
#include <format>
|
||||
|
||||
#include <utility>
|
||||
#include <tuple>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
// clang-format off
|
||||
|
||||
void f() {
|
||||
std::format("{::}", std::make_tuple(0)); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
|
||||
// expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
|
||||
|
||||
std::format("{::^}", std::make_tuple(0)); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
|
||||
// expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
|
||||
|
||||
std::format("{:+}", std::make_pair(0, 0)); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
|
||||
// expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
|
||||
|
||||
std::format("{:m}", std::make_tuple(0)); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
|
||||
// expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
|
||||
|
||||
std::format("{:m}", std::make_tuple(0, 0, 0)); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
|
||||
// expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
|
||||
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
|
||||
std::format(L"{::}", std::make_tuple(0)); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
|
||||
// expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
|
||||
|
||||
std::format(L"{::^}", std::make_tuple(0)); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
|
||||
// expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
|
||||
|
||||
std::format(L"{:+}", std::make_pair(0, 0)); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
|
||||
// expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
|
||||
|
||||
std::format(L"{:m}", std::make_tuple(0)); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
|
||||
// expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
|
||||
|
||||
std::format(L"{:m}", std::make_tuple(0, 0, 0)); // expected-error-re{{call to consteval function '{{.*}}' is not a constant expression}}
|
||||
// expected-note@*:* {{non-constexpr function '__throw_format_error' cannot be used in a constant expression}}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,390 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef TEST_STD_UTILITIES_FORMAT_FORMAT_TUPLE_FORMAT_TESTS_H
|
||||
#define TEST_STD_UTILITIES_FORMAT_FORMAT_TUPLE_FORMAT_TESTS_H
|
||||
|
||||
#include <concepts>
|
||||
#include <format>
|
||||
|
||||
#include "make_string.h"
|
||||
|
||||
#define STR(S) MAKE_STRING(CharT, S)
|
||||
#define SV(S) MAKE_STRING_VIEW(CharT, S)
|
||||
|
||||
template <class T>
|
||||
struct context {};
|
||||
|
||||
template <>
|
||||
struct context<char> {
|
||||
using type = std::format_context;
|
||||
};
|
||||
|
||||
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
|
||||
template <>
|
||||
struct context<wchar_t> {
|
||||
using type = std::wformat_context;
|
||||
};
|
||||
#endif
|
||||
|
||||
template <class T>
|
||||
using context_t = typename context<T>::type;
|
||||
|
||||
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);
|
||||
|
||||
// *** 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-spec range-fill field contains an invalid character", SV("{:}<}"), input);
|
||||
check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input);
|
||||
check_exception("The format-spec range-fill field contains an invalid character", SV("{::<}"), input);
|
||||
|
||||
// *** sign ***
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{:-}"), input);
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{:+}"), input);
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{: }"), input);
|
||||
|
||||
// *** alternate form ***
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input);
|
||||
|
||||
// *** zero-padding ***
|
||||
check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input);
|
||||
|
||||
// *** precision ***
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{:.}"), input);
|
||||
|
||||
// *** locale-specific form ***
|
||||
check_exception("The format-spec 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("aAbBcdeEfFgGopsxX?")) {
|
||||
check_exception("The format-spec should consume the input or end with a '}'",
|
||||
std::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);
|
||||
|
||||
// *** 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-spec range-fill field contains an invalid character", SV("{:}<}"), input);
|
||||
check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input);
|
||||
check_exception("The format-spec range-fill field contains an invalid character", SV("{::<}"), input);
|
||||
|
||||
// *** sign ***
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{:-}"), input);
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{:+}"), input);
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{: }"), input);
|
||||
|
||||
// *** alternate form ***
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input);
|
||||
|
||||
// *** zero-padding ***
|
||||
check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input);
|
||||
|
||||
// *** precision ***
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{:.}"), input);
|
||||
|
||||
// *** locale-specific form ***
|
||||
check_exception("The format-spec 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("aAbBcdeEfFgGopsxX?")) {
|
||||
check_exception("The format-spec should consume the input or end with a '}'",
|
||||
std::basic_string_view{STR("{:") + c + STR("}")},
|
||||
input);
|
||||
}
|
||||
}
|
||||
|
||||
template <class CharT, class TestFunction, class TupleOrPair>
|
||||
void test_escaping(TestFunction check, TupleOrPair&& input) {
|
||||
static_assert(std::same_as<std::remove_cvref_t<decltype(std::get<0>(input))>, CharT>);
|
||||
static_assert(std::same_as<std::remove_cvref_t<decltype(std::get<1>(input))>, std::basic_string<CharT>>);
|
||||
|
||||
check(SV(R"(('*', ""))"), SV("{}"), input);
|
||||
|
||||
// Char
|
||||
std::get<0>(input) = CharT('\t');
|
||||
check(SV(R"(('\t', ""))"), SV("{}"), input);
|
||||
std::get<0>(input) = CharT('\n');
|
||||
check(SV(R"(('\n', ""))"), SV("{}"), input);
|
||||
std::get<0>(input) = CharT('\0');
|
||||
check(SV(R"(('\u{0}', ""))"), SV("{}"), input);
|
||||
|
||||
// String
|
||||
std::get<0>(input) = CharT('*');
|
||||
std::get<1>(input) = SV("hellö");
|
||||
check(SV("('*', \"hellö\")"), 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, std::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, std::make_pair(42, SV("hello")));
|
||||
}
|
||||
|
||||
//
|
||||
// tuple tests
|
||||
//
|
||||
|
||||
template <class CharT, class TestFunction, class ExceptionTest>
|
||||
void test_tuple_int(TestFunction check, ExceptionTest check_exception) {
|
||||
auto input = std::make_tuple(42);
|
||||
|
||||
check(SV("(42)"), SV("{}"), 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-spec range-fill field contains an invalid character", SV("{:}<}"), input);
|
||||
check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input);
|
||||
check_exception("The format-spec range-fill field contains an invalid character", SV("{::<}"), input);
|
||||
|
||||
// *** sign ***
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{:-}"), input);
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{:+}"), input);
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{: }"), input);
|
||||
|
||||
// *** alternate form ***
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input);
|
||||
|
||||
// *** zero-padding ***
|
||||
check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input);
|
||||
|
||||
// *** precision ***
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{:.}"), input);
|
||||
|
||||
// *** locale-specific form ***
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input);
|
||||
|
||||
// *** type ***
|
||||
check_exception("The format specifier m requires a pair or a two-element tuple", SV("{:m}"), input);
|
||||
check(SV("__42___"), SV("{:_^7n}"), input);
|
||||
|
||||
for (CharT c : SV("aAbBcdeEfFgGopsxX?")) {
|
||||
check_exception("The format-spec should consume the input or end with a '}'",
|
||||
std::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 = std::make_tuple(42, SV("hello"), color::red);
|
||||
|
||||
check(SV("(42, \"hello\", \"red\")"), SV("{}"), 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-spec range-fill field contains an invalid character", SV("{:}<}"), input);
|
||||
check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input);
|
||||
check_exception("The format-spec range-fill field contains an invalid character", SV("{::<}"), input);
|
||||
|
||||
// *** sign ***
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{:-}"), input);
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{:+}"), input);
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{: }"), input);
|
||||
|
||||
// *** alternate form ***
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input);
|
||||
|
||||
// *** zero-padding ***
|
||||
check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input);
|
||||
|
||||
// *** precision ***
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{:.}"), input);
|
||||
|
||||
// *** locale-specific form ***
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input);
|
||||
|
||||
// *** type ***
|
||||
check_exception("The format specifier m requires a pair or a two-element tuple", SV("{:m}"), input);
|
||||
check(SV("__42, \"hello\", \"red\"___"), SV("{:_^23n}"), input);
|
||||
|
||||
for (CharT c : SV("aAbBcdeEfFgGopsxX?")) {
|
||||
check_exception("The format-spec should consume the input or end with a '}'",
|
||||
std::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, std::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, std::make_tuple(42, SV("hello")));
|
||||
}
|
||||
|
||||
//
|
||||
// nested tests
|
||||
//
|
||||
|
||||
template <class CharT, class TestFunction, class ExceptionTest, class Nested>
|
||||
void test_nested(TestFunction check, ExceptionTest check_exception, Nested&& input) {
|
||||
// [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 to the
|
||||
// set_debug_format is not propagated. The paper
|
||||
// P2733 Fix handling of empty specifiers in std::format
|
||||
// addressed this.
|
||||
|
||||
check(SV("(42, (hello, red))"), SV("{}"), input);
|
||||
|
||||
// *** align-fill & width ***
|
||||
check(SV("(42, (hello, red)) "), SV("{:23}"), input);
|
||||
check(SV("(42, (hello, red))*****"), SV("{:*<23}"), input);
|
||||
check(SV("__(42, (hello, red))___"), SV("{:_^23}"), input);
|
||||
check(SV("#####(42, (hello, red))"), SV("{:#>23}"), input);
|
||||
|
||||
check(SV("(42, (hello, red)) "), SV("{:{}}"), input, 23);
|
||||
check(SV("(42, (hello, red))*****"), SV("{:*<{}}"), input, 23);
|
||||
check(SV("__(42, (hello, red))___"), SV("{:_^{}}"), input, 23);
|
||||
check(SV("#####(42, (hello, red))"), SV("{:#>{}}"), input, 23);
|
||||
|
||||
check_exception("The format-spec range-fill field contains an invalid character", SV("{:}<}"), input);
|
||||
check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input);
|
||||
check_exception("The format-spec range-fill field contains an invalid character", SV("{::<}"), input);
|
||||
|
||||
// *** sign ***
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{:-}"), input);
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{:+}"), input);
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{: }"), input);
|
||||
|
||||
// *** alternate form ***
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input);
|
||||
|
||||
// *** zero-padding ***
|
||||
check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input);
|
||||
|
||||
// *** precision ***
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{:.}"), input);
|
||||
|
||||
// *** locale-specific form ***
|
||||
check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input);
|
||||
|
||||
// *** type ***
|
||||
check(SV("__42: (hello, red)___"), SV("{:_^21m}"), input);
|
||||
check(SV("__42, (hello, red)___"), SV("{:_^21n}"), input);
|
||||
|
||||
for (CharT c : SV("aAbBcdeEfFgGopsxX?")) {
|
||||
check_exception("The format-spec should consume the input or end with a '}'",
|
||||
std::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, std::make_pair(42, std::make_pair(SV("hello"), color::red)));
|
||||
test_nested<CharT>(check, check_exception, std::make_pair(42, std::make_tuple(SV("hello"), color::red)));
|
||||
test_nested<CharT>(check, check_exception, std::make_tuple(42, std::make_pair(SV("hello"), color::red)));
|
||||
test_nested<CharT>(check, check_exception, std::make_tuple(42, std::make_tuple(SV("hello"), color::red)));
|
||||
|
||||
test_escaping<CharT>(check, std::make_pair(CharT('*'), STR("")));
|
||||
test_escaping<CharT>(check, std::make_tuple(CharT('*'), STR("")));
|
||||
|
||||
// Test cvref-qualified types.
|
||||
// clang-format off
|
||||
check(SV("(42)"), SV("{}"), std::tuple< int >{42});
|
||||
check(SV("(42)"), SV("{}"), std::tuple<const int >{42});
|
||||
check(SV("(42)"), SV("{}"), std::tuple< volatile int >{42});
|
||||
check(SV("(42)"), SV("{}"), std::tuple<const volatile int >{42});
|
||||
|
||||
int answer = 42;
|
||||
check(SV("(42)"), SV("{}"), std::tuple< int& >{answer});
|
||||
check(SV("(42)"), SV("{}"), std::tuple<const int& >{answer});
|
||||
check(SV("(42)"), SV("{}"), std::tuple< volatile int& >{answer});
|
||||
check(SV("(42)"), SV("{}"), std::tuple<const volatile int& >{answer});
|
||||
|
||||
check(SV("(42)"), SV("{}"), std::tuple< int&&>{42});
|
||||
check(SV("(42)"), SV("{}"), std::tuple<const int&&>{42});
|
||||
check(SV("(42)"), SV("{}"), std::tuple< volatile int&&>{42});
|
||||
check(SV("(42)"), SV("{}"), std::tuple<const volatile int&&>{42});
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
#endif // TEST_STD_UTILITIES_FORMAT_FORMAT_TUPLE_FORMAT_TESTS_H
|
||||
@@ -0,0 +1,86 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
|
||||
// UNSUPPORTED: libcpp-has-no-incomplete-format
|
||||
|
||||
// This test requires the dylib support introduced in D92214.
|
||||
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{.+}}
|
||||
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx11.{{.+}}
|
||||
|
||||
// <format>
|
||||
|
||||
// template<class charT, formattable<charT>... Ts>
|
||||
// struct formatter<pair-or-tuple<Ts...>, charT>
|
||||
//
|
||||
// tested in the format functions
|
||||
//
|
||||
// string vformat(string_view fmt, format_args args);
|
||||
// wstring vformat(wstring_view fmt, wformat_args args);
|
||||
|
||||
#include <format>
|
||||
#include <cassert>
|
||||
|
||||
#include "test_macros.h"
|
||||
#include "format.functions.tests.h"
|
||||
|
||||
#ifndef TEST_HAS_NO_LOCALIZATION
|
||||
# include <iostream>
|
||||
# include <concepts>
|
||||
#endif
|
||||
|
||||
auto test = []<class CharT, class... Args>(
|
||||
std::basic_string_view<CharT> expected, std::basic_string_view<CharT> fmt, Args&&... args) {
|
||||
std::basic_string<CharT> out = std::vformat(fmt, std::make_format_args<context_t<CharT>>(args...));
|
||||
#ifndef TEST_HAS_NO_LOCALIZATION
|
||||
if constexpr (std::same_as<CharT, char>)
|
||||
if (out != expected)
|
||||
std::cerr << "\nFormat string " << fmt << "\nExpected output " << expected << "\nActual output " << out
|
||||
<< '\n';
|
||||
#endif // TEST_HAS_NO_LOCALIZATION
|
||||
assert(out == expected);
|
||||
};
|
||||
|
||||
auto test_exception =
|
||||
[]<class CharT, class... Args>(
|
||||
[[maybe_unused]] std::string_view what,
|
||||
[[maybe_unused]] std::basic_string_view<CharT> fmt,
|
||||
[[maybe_unused]] Args&&... args) {
|
||||
#ifndef TEST_HAS_NO_EXCEPTIONS
|
||||
try {
|
||||
TEST_IGNORE_NODISCARD std::vformat(fmt, std::make_format_args<context_t<CharT>>(args...));
|
||||
# if !defined(TEST_HAS_NO_LOCALIZATION)
|
||||
if constexpr (std::same_as<CharT, char>)
|
||||
std::cerr << "\nFormat string " << fmt << "\nDidn't throw an exception.\n";
|
||||
# endif // !defined(TEST_HAS_NO_LOCALIZATION
|
||||
assert(false);
|
||||
} catch ([[maybe_unused]] const std::format_error& e) {
|
||||
# if defined(_LIBCPP_VERSION)
|
||||
# if !defined(TEST_HAS_NO_LOCALIZATION)
|
||||
if constexpr (std::same_as<CharT, char>) {
|
||||
if (e.what() != what)
|
||||
std::cerr << "\nFormat string " << fmt << "\nExpected exception " << what << "\nActual exception "
|
||||
<< e.what() << '\n';
|
||||
}
|
||||
# endif // !defined(TEST_HAS_NO_LOCALIZATION
|
||||
assert(e.what() == what);
|
||||
# endif // defined(_LIBCPP_VERSION)
|
||||
return;
|
||||
}
|
||||
assert(false);
|
||||
#endif
|
||||
};
|
||||
|
||||
int main(int, char**) {
|
||||
run_tests<char>(test, test_exception);
|
||||
|
||||
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
|
||||
run_tests<wchar_t>(test, test_exception);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
|
||||
// UNSUPPORTED: libcpp-has-no-incomplete-format
|
||||
|
||||
// This test requires the dylib support introduced in D92214.
|
||||
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{.+}}
|
||||
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx11.{{.+}}
|
||||
|
||||
// <format>
|
||||
|
||||
// template<class charT, formattable<charT>... Ts>
|
||||
// struct formatter<pair-or-tuple<Ts...>, charT>
|
||||
|
||||
// template<class FormatContext>
|
||||
// typename FormatContext::iterator
|
||||
// format(see below& elems, FormatContext& ctx) const;
|
||||
|
||||
// Note this tests the basics of this function. It's tested in more detail in
|
||||
// the format functions tests.
|
||||
|
||||
#include <cassert>
|
||||
#include <concepts>
|
||||
#include <format>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include "test_format_context.h"
|
||||
#include "test_macros.h"
|
||||
#include "make_string.h"
|
||||
|
||||
#define SV(S) MAKE_STRING_VIEW(CharT, S)
|
||||
|
||||
template <class StringViewT, class Arg>
|
||||
void test(StringViewT expected, Arg arg) {
|
||||
using CharT = typename StringViewT::value_type;
|
||||
using String = std::basic_string<CharT>;
|
||||
using OutIt = std::back_insert_iterator<String>;
|
||||
using FormatCtxT = std::basic_format_context<OutIt, CharT>;
|
||||
|
||||
const std::formatter<Arg, CharT> formatter;
|
||||
|
||||
String result;
|
||||
OutIt out = std::back_inserter(result);
|
||||
FormatCtxT format_ctx = test_format_context_create<OutIt, CharT>(out, std::make_format_args<FormatCtxT>(arg));
|
||||
formatter.format(arg, format_ctx);
|
||||
assert(result == expected);
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
void test() {
|
||||
test(SV("(1)"), std::tuple<int>{1});
|
||||
test(SV("(1, 1)"), std::tuple<int, CharT>{1, CharT('1')});
|
||||
test(SV("(1, 1)"), std::pair<int, CharT>{1, CharT('1')});
|
||||
test(SV("(1, 1, 1)"), std::tuple<int, CharT, double>{1, CharT('1'), 1.0});
|
||||
}
|
||||
|
||||
void test() {
|
||||
test<char>();
|
||||
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
|
||||
test<wchar_t>();
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
|
||||
return 0;
|
||||
}
|
||||
81
libcxx/test/std/utilities/format/format.tuple/parse.pass.cpp
Normal file
81
libcxx/test/std/utilities/format/format.tuple/parse.pass.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
|
||||
// UNSUPPORTED: libcpp-has-no-incomplete-format
|
||||
|
||||
// This test requires the dylib support introduced in D92214.
|
||||
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{.+}}
|
||||
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx11.{{.+}}
|
||||
|
||||
// <format>
|
||||
|
||||
// template<class charT, formattable<charT>... Ts>
|
||||
// struct formatter<pair-or-tuple<Ts...>, charT>
|
||||
|
||||
// template<class ParseContext>
|
||||
// constexpr typename ParseContext::iterator
|
||||
// parse(ParseContext& ctx);
|
||||
|
||||
// Note this tests the basics of this function. It's tested in more detail in
|
||||
// the format functions tests.
|
||||
|
||||
#include <cassert>
|
||||
#include <concepts>
|
||||
#include <format>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include "test_format_context.h"
|
||||
#include "test_macros.h"
|
||||
#include "make_string.h"
|
||||
|
||||
#define SV(S) MAKE_STRING_VIEW(CharT, S)
|
||||
|
||||
template <class Arg, class StringViewT>
|
||||
constexpr void test(StringViewT fmt) {
|
||||
using CharT = typename StringViewT::value_type;
|
||||
auto parse_ctx = std::basic_format_parse_context<CharT>(fmt);
|
||||
std::formatter<Arg, CharT> formatter;
|
||||
static_assert(std::semiregular<decltype(formatter)>);
|
||||
|
||||
std::same_as<typename StringViewT::iterator> auto it = formatter.parse(parse_ctx);
|
||||
assert(it == fmt.end() - (!fmt.empty() && fmt.back() == '}'));
|
||||
}
|
||||
|
||||
template <class CharT, class Arg>
|
||||
constexpr void test() {
|
||||
test<Arg>(SV(""));
|
||||
test<Arg>(SV("42"));
|
||||
|
||||
test<Arg>(SV("}"));
|
||||
test<Arg>(SV("42}"));
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr void test() {
|
||||
test<CharT, std::tuple<int>>();
|
||||
test<CharT, std::tuple<int, CharT>>();
|
||||
test<CharT, std::pair<int, CharT>>();
|
||||
test<CharT, std::tuple<int, CharT, double>>();
|
||||
}
|
||||
|
||||
constexpr bool test() {
|
||||
test<char>();
|
||||
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
|
||||
test<wchar_t>();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
|
||||
// UNSUPPORTED: libcpp-has-no-incomplete-format
|
||||
|
||||
// This test requires the dylib support introduced in D92214.
|
||||
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{.+}}
|
||||
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx11.{{.+}}
|
||||
|
||||
// <format>
|
||||
|
||||
// template<class charT, formattable<charT>... Ts>
|
||||
// struct formatter<pair-or-tuple<Ts...>, charT>
|
||||
|
||||
// constexpr void constexpr void set_brackets(basic_string_view<charT> opening,
|
||||
// basic_string_view<charT> closing);
|
||||
|
||||
// Note this tests the basics of this function. It's tested in more detail in
|
||||
// the format functions tests.
|
||||
|
||||
#include <format>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include "make_string.h"
|
||||
|
||||
#define SV(S) MAKE_STRING_VIEW(CharT, S)
|
||||
|
||||
template <class CharT, class Arg>
|
||||
constexpr void test() {
|
||||
std::formatter<Arg, CharT> formatter;
|
||||
formatter.set_brackets(SV("open"), SV("close"));
|
||||
|
||||
// Note there is no direct way to validate this function modified the object.
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr void test() {
|
||||
test<CharT, std::tuple<int>>();
|
||||
test<CharT, std::tuple<int, CharT>>();
|
||||
test<CharT, std::pair<int, CharT>>();
|
||||
test<CharT, std::tuple<int, CharT, double>>();
|
||||
}
|
||||
|
||||
constexpr bool test() {
|
||||
test<char>();
|
||||
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
|
||||
test<wchar_t>();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
|
||||
// UNSUPPORTED: libcpp-has-no-incomplete-format
|
||||
|
||||
// This test requires the dylib support introduced in D92214.
|
||||
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{.+}}
|
||||
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx11.{{.+}}
|
||||
|
||||
// <format>
|
||||
|
||||
// class range_formatter
|
||||
// template<class charT, formattable<charT>... Ts>
|
||||
// struct formatter<pair-or-tuple<Ts...>, charT>
|
||||
|
||||
// constexpr void set_separator(basic_string_view<charT> sep);
|
||||
|
||||
// Note this tests the basics of this function. It's tested in more detail in
|
||||
// the format functions tests.
|
||||
|
||||
#include <format>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include "make_string.h"
|
||||
|
||||
#define SV(S) MAKE_STRING_VIEW(CharT, S)
|
||||
|
||||
template <class CharT, class Arg>
|
||||
constexpr void test() {
|
||||
std::formatter<Arg, CharT> formatter;
|
||||
formatter.set_separator(SV("sep"));
|
||||
|
||||
// Note there is no direct way to validate this function modified the object.
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr void test() {
|
||||
test<CharT, std::tuple<int>>();
|
||||
test<CharT, std::tuple<int, CharT>>();
|
||||
test<CharT, std::pair<int, CharT>>();
|
||||
test<CharT, std::tuple<int, CharT, double>>();
|
||||
}
|
||||
|
||||
constexpr bool test() {
|
||||
test<char>();
|
||||
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
|
||||
test<wchar_t>();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -194,6 +194,7 @@ check-generated-output)
|
||||
--exclude '*.dat' \
|
||||
--exclude 'escaped_output.*.pass.cpp' \
|
||||
--exclude 'format_tests.h' \
|
||||
--exclude 'format.functions.tests.h' \
|
||||
--exclude 'formatter.*.pass.cpp' \
|
||||
--exclude 'grep.pass.cpp' \
|
||||
--exclude 'locale-specific_form.pass.cpp' \
|
||||
|
||||
Reference in New Issue
Block a user