libstdc++: Implement C++20 <format> [PR104166]

This doesn't add the newer C++23 features like formatting ranges
and escaped string prsentation types.

However, C++23 extended floating-point types are supported, as are
128-bit integers.

It could do with more tests.

libstdc++-v3/ChangeLog:

	PR libstdc++/104166
	* include/Makefile.am (std_headers): Add <format>.
	* include/Makefile.in: Regenerate.
	* include/precompiled/stdc++.h: Add <format>.
	* include/std/format: New file.
	* python/libstdcxx/v6/printers.py (StdFormatArgsPrinter): New
	printer for std::format_args.
	* testsuite/std/format/arguments/args.cc: New test.
	* testsuite/std/format/error.cc: New test.
	* testsuite/std/format/formatter.cc: New test.
	* testsuite/std/format/functions/format.cc: New test.
	* testsuite/std/format/functions/format_to_n.cc: New test.
	* testsuite/std/format/functions/size.cc: New test.
	* testsuite/std/format/functions/vformat_to.cc: New test.
	* testsuite/std/format/parse_ctx.cc: New test.
	* testsuite/std/format/string.cc: New test.
	* testsuite/std/format/string_neg.cc: New test.
This commit is contained in:
Jonathan Wakely 2022-10-18 21:20:06 +01:00
parent d4ba3b369c
commit 1d9454aba6
15 changed files with 5191 additions and 0 deletions

View file

@ -68,6 +68,7 @@ std_headers = \
${std_srcdir}/deque \
${std_srcdir}/execution \
${std_srcdir}/filesystem \
${std_srcdir}/format \
${std_srcdir}/forward_list \
${std_srcdir}/fstream \
${std_srcdir}/future \

View file

@ -424,6 +424,7 @@ std_freestanding = \
@GLIBCXX_HOSTED_TRUE@ ${std_srcdir}/deque \
@GLIBCXX_HOSTED_TRUE@ ${std_srcdir}/execution \
@GLIBCXX_HOSTED_TRUE@ ${std_srcdir}/filesystem \
@GLIBCXX_HOSTED_TRUE@ ${std_srcdir}/format \
@GLIBCXX_HOSTED_TRUE@ ${std_srcdir}/forward_list \
@GLIBCXX_HOSTED_TRUE@ ${std_srcdir}/fstream \
@GLIBCXX_HOSTED_TRUE@ ${std_srcdir}/future \

View file

@ -210,6 +210,7 @@
#include <bit>
#include <compare>
#include <concepts>
#include <format>
#include <latch>
#include <numbers>
#include <ranges>

File diff suppressed because it is too large Load diff

View file

@ -1815,6 +1815,32 @@ class StdAtomicPrinter:
val = self.val['_M_i']
return '%s<%s> = { %s }' % (self.typename, str(self.value_type), val)
class StdFormatArgsPrinter:
"Print a std::basic_format_args"
# TODO: add printer for basic_format_arg<C> and print out children
# TODO: add printer for basic_format_args<C>::_Store<Args...>
def __init__(self, typename, val):
self.typename = strip_versioned_namespace(typename)
self.val = val
def to_string(self):
targs = get_template_arg_list(self.val.type)
char_type = get_template_arg_list(targs[0])[1]
if char_type == gdb.lookup_type('char'):
typ = 'std::format_args'
elif char_type == gdb.lookup_type('wchar_t'):
typ = 'std::wformat_args'
else:
typ = 'std::basic_format_args'
size = self.val['_M_packed_size']
if size == 1:
return "%s with 1 argument" % (typ)
if size == 0:
size = self.val['_M_unpacked_size']
return "%s with %d arguments" % (typ, size)
# A "regular expression" printer which conforms to the
# "SubPrettyPrinter" protocol from gdb.printing.
class RxPrinter(object):
@ -2355,6 +2381,7 @@ def build_libstdcxx_dictionary ():
libstdcxx_printer.add_version('std::', 'weak_ordering', StdCmpCatPrinter)
libstdcxx_printer.add_version('std::', 'strong_ordering', StdCmpCatPrinter)
libstdcxx_printer.add_version('std::', 'span', StdSpanPrinter)
libstdcxx_printer.add_version('std::', 'basic_format_args', StdFormatArgsPrinter)
# Extensions.
libstdcxx_printer.add_version('__gnu_cxx::', 'slist', StdSlistPrinter)

View file

@ -0,0 +1,96 @@
// { dg-options "-std=gnu++20" }
// { dg-do run { target c++20 } }
#include <format>
#include <testsuite_hooks.h>
template<typename Ctx, typename T>
bool equals(std::basic_format_arg<Ctx> fmt_arg, T expected) {
return std::visit_format_arg([=](auto arg_val) {
if constexpr (std::is_same_v<decltype(arg_val), T>)
return arg_val == expected;
else
return false;
}, fmt_arg);
}
void
test_empty()
{
std::format_args args = std::make_format_args();
VERIFY(!args.get(0));
VERIFY(!args.get(1));
VERIFY(!args.get((std::size_t)-1));
VERIFY(equals(args.get(0), std::monostate{}));
std::format_args cargs = std::make_format_args<std::format_context>();
VERIFY(!cargs.get(0));
VERIFY(equals(cargs.get(0), std::monostate{}));
std::wformat_args wargs = std::make_wformat_args();
VERIFY(!wargs.get(0));
VERIFY(equals(wargs.get(0), std::monostate{}));
}
enum E { ByGum };
template<>
struct std::formatter<E> : std::formatter<int>
{
using std::formatter<int>::parse;
std::format_context::iterator
format(E e, std::format_context& fc) const
{ return std::formatter<int>::format((int)e, fc); }
};
void
test_args()
{
auto store = std::make_format_args(false, 1, '2', 3.4);
std::format_args args = store;
VERIFY(equals(args.get(0), false));
VERIFY(equals(args.get(1), 1));
VERIFY(equals(args.get(2), '2'));
VERIFY(equals(args.get(3), 3.4));
VERIFY(!args.get(4));
auto cstore = std::make_format_args<std::format_context>(5L, 6ULL, 7.8f);
std::format_args cargs = cstore;
if constexpr (sizeof(long) == sizeof(int))
VERIFY(equals(cargs.get(0), 5));
else
VERIFY(equals(cargs.get(0), 5LL));
VERIFY(equals(cargs.get(1), 6ULL));
VERIFY(equals(cargs.get(2), 7.8f));
VERIFY(!cargs.get(3));
VERIFY(equals(std::format_args(std::make_format_args(std::string("tenfour"))).get(0), std::string_view("tenfour")));
// This needs to be on the stack so that testing pointer equality works.
wchar_t eleven[] = L"eleven";
// This needs to be on the stack so that the wstring_view doesn't dangle.
std::wstring tenfour = L"tenfour";
auto wstore = std::make_wformat_args('9', L'X', eleven, 12.13L, tenfour);
std::wformat_args wargs = wstore;
VERIFY(equals(wargs.get(0), static_cast<wchar_t>('9')));
VERIFY(equals(wargs.get(1), L'X'));
VERIFY(equals(wargs.get(2), static_cast<const wchar_t*>(eleven)));
VERIFY(equals(wargs.get(3), 12.13L));
VERIFY(equals(wargs.get(4), std::wstring_view(tenfour)));
VERIFY(!wargs.get(5));
auto another_store = std::make_format_args(nullptr, E::ByGum);
args = another_store;
VERIFY(equals(args.get(0), static_cast<const void*>(nullptr)));
using handle = std::basic_format_arg<std::format_context>::handle;
auto is_handle = []<typename T>(T) { return std::is_same_v<T, handle>; };
VERIFY(std::visit_format_arg(is_handle, args.get(1)));
}
int main()
{
test_empty();
test_args();
}

View file

@ -0,0 +1,26 @@
// { dg-options "-std=gnu++20" }
// { dg-do run { target c++20 } }
#include <format>
#include <string>
#include <testsuite_hooks.h>
static_assert( std::is_base_of_v<std::runtime_error, std::format_error> );
static_assert( std::is_convertible_v<std::format_error*, std::runtime_error*> );
void
test_what()
{
const char* cstr = "test string";
std::format_error e(cstr);
VERIFY( std::string(e.what()).find(cstr) != std::string::npos );
std::string str = "test std::string";
std::format_error ee(str);
VERIFY( std::string(ee.what()).find(str) != std::string::npos );
}
int main()
{
test_what();
}

View file

@ -0,0 +1,89 @@
// { dg-options "-std=gnu++20" }
// { dg-do run { target c++20 } }
#include <format>
#include <testsuite_hooks.h>
struct S { };
template<> struct std::formatter<S> : std::formatter<const char*> {
template<class Out>
auto format(S, std::basic_format_context<Out, char>& ctx) const {
return formatter<const char*>::format("ess", ctx);
}
};
struct T { };
template<> struct std::formatter<T> : std::formatter<const char*> {
// This function only accepts std::format_context, not other contexts.
auto format(T, std::format_context& ctx) const {
return formatter<const char*>::format("tee", ctx);
}
};
struct U { };
void
test_concept() // [format.formattable]
{
static_assert( std::formattable<int, char> );
static_assert( std::formattable<const int, char> );
static_assert( std::formattable<int, wchar_t> );
static_assert( std::formattable<const int, wchar_t> );
static_assert( std::formattable<char, char> );
static_assert( std::formattable<char*, char> );
static_assert( std::formattable<wchar_t, wchar_t> );
static_assert( std::formattable<wchar_t*, wchar_t> );
static_assert( std::formattable<char, wchar_t> );
static_assert( ! std::formattable<char*, wchar_t> );
static_assert( ! std::formattable<wchar_t, char> );
static_assert( ! std::formattable<wchar_t*, char> );
static_assert( std::formattable<S, char> );
static_assert( std::formattable<const S, char> );
static_assert( ! std::formattable<S, wchar_t> ); // only formats as char
static_assert( ! std::formattable<T, char> ); // formatter not generic
static_assert( ! std::formattable<U, char> ); // no formatter
}
enum color { red, green, blue };
const char* color_names[] = { "red", "green", "blue" };
template<> struct std::formatter<color> : std::formatter<const char*> {
auto format(color c, format_context& ctx) const {
return formatter<const char*>::format(color_names[c], ctx);
}
};
struct err {};
void
test_specializations() // [format.formatter.spec]
{
std::string s0 = std::format("{}", 42); // OK, library-provided formatter
VERIFY( s0 == "42" );
// std::string s1 = std::format("{}", L"foo"); // error: disabled formatter
using Fw = std::format_context::formatter_type<wchar_t>;
static_assert( ! std::is_default_constructible_v<Fw> );
static_assert( ! std::is_copy_constructible_v<Fw> );
static_assert( ! std::is_move_constructible_v<Fw> );
static_assert( ! std::is_copy_assignable_v<Fw> );
static_assert( ! std::is_move_assignable_v<Fw> );
std::string s2 = std::format("{}", red); // OK, user-provided formatter
VERIFY( s2 == "red" );
// std::string s3 = std::format("{}", err{}); // error: disabled formatter
using Ferr = std::format_context::formatter_type<err>;
static_assert( ! std::is_default_constructible_v<Ferr> );
static_assert( ! std::is_copy_constructible_v<Ferr> );
static_assert( ! std::is_move_constructible_v<Ferr> );
static_assert( ! std::is_copy_assignable_v<Ferr> );
static_assert( ! std::is_move_assignable_v<Ferr> );
}
int main()
{
test_specializations();
}

View file

@ -0,0 +1,313 @@
// { dg-options "-std=gnu++20" }
// { dg-do run { target c++20 } }
#include <format>
#include <string>
#include <limits>
#include <cstdint>
#include <testsuite_hooks.h>
void
test_no_args()
{
std::string s;
s = std::format("disco");
VERIFY( s == "disco" );
s = std::format("}} machine {{ funk }} specialists {{");
VERIFY( s == "} machine { funk } specialists {" );
s = std::format("128bpm }}");
VERIFY( s == "128bpm }" );
}
void
test_unescaped()
{
#ifdef __cpp_exceptions
for (auto f : { "{", "}", "{{{", "{{}", "}{", "{{{{{" })
try {
(void) std::vformat(f, std::make_format_args());
VERIFY( false );
} catch (const std::format_error& e) {
std::string what = e.what();
VERIFY( what.find("unmatched") != what.npos );
}
#endif
}
struct brit_punc : std::numpunct<char>
{
std::string do_grouping() const override { return "\3\3"; }
char do_thousands_sep() const override { return ','; }
std::string do_truename() const override { return "yes mate"; }
std::string do_falsename() const override { return "nah bruv"; }
};
void
test_std_examples()
{
using namespace std;
string s = format("{0}-{{", 8); // value of s is "8-{"
VERIFY( s == "8-{" );
// align
{
char c = 120;
string s0 = format("{:6}", 42);
VERIFY(s0 == " 42");
string s1 = format("{:6}", 'x');
VERIFY(s1 == "x ");
string s2 = format("{:*<6}", 'x');
VERIFY(s2 == "x*****");
string s3 = format("{:*>6}", 'x');
VERIFY(s3 == "*****x");
string s4 = format("{:*^6}", 'x');
VERIFY(s4 == "**x***");
string s5 = format("{:6d}", c);
VERIFY(s5 == " 120");
string s6 = format("{:6}", true);
VERIFY(s6 == "true ");
}
// sign
{
double inf = numeric_limits<double>::infinity();
double nan = numeric_limits<double>::quiet_NaN();
string s0 = format("{0:},{0:+},{0:-},{0: }", 1);
VERIFY(s0 == "1,+1,1, 1");
string s1 = format("{0:},{0:+},{0:-},{0: }", -1);
VERIFY(s1 == "-1,-1,-1,-1");
string s2 = format("{0:},{0:+},{0:-},{0: }", inf);
VERIFY(s2 == "inf,+inf,inf, inf");
string s3 = format("{0:},{0:+},{0:-},{0: }", nan);
VERIFY(s3 == "nan,+nan,nan, nan");
}
// alternate form and zero fill
{
char c = 120;
string s1 = format("{:+06d}", c);
VERIFY(s1 == "+00120");
string s2 = format("{:#06x}", 0xa);
VERIFY(s2 == "0x000a");
string s3 = format("{:<06}", -42);
VERIFY(s3 == "-42 "); // 0 is ignored because of < alignment
}
// integer presentation types
{
// Change global locale so "{:L}" adds digit separators.
std::locale::global(std::locale({}, new brit_punc));
string s0 = format("{}", 42);
VERIFY(s0 == "42");
string s1 = format("{0:b} {0:d} {0:o} {0:x}", 42);
VERIFY(s1 == "101010 42 52 2a");
string s2 = format("{0:#x} {0:#X}", 42);
VERIFY(s2 == "0x2a 0X2A");
string s3 = format("{:L}", 1234);
VERIFY(s3 == "1,234");
// Restore
std::locale::global(std::locale::classic());
}
}
void
test_alternate_forms()
{
std::string s;
s = std::format("{0:#b} {0:+#B} {0:#o} {0:#x} {0:+#X} {0: #d}", 42);
VERIFY( s == "0b101010 +0B101010 052 0x2a +0X2A 42" );
s = std::format("{0:#b} {0:+#B} {0:#o} {0:#x} {0:+#X} {0: #d}", 0);
VERIFY( s == "0b0 +0B0 0 0x0 +0X0 0" );
s = std::format("{0:+#012g} {0:+#014g} {0:+#014g}", 1234.0);
VERIFY( s == "+00001234.00 +0000001234.00 +0000001234.00" );
s = std::format("{0:+#0{1}g} {0:+#0{2}g} {0:+#0{2}g}", 1234.5, 12, 14);
VERIFY( s == "+00001234.50 +0000001234.50 +0000001234.50" );
s = std::format("{:#.2g}", -0.0);
VERIFY( s == "-0.0" );
}
struct euro_punc : std::numpunct<char>
{
std::string do_grouping() const override { return "\3\3"; }
char do_thousands_sep() const override { return '.'; }
char do_decimal_point() const override { return ','; }
};
void
test_locale()
{
// The default C locale.
std::locale cloc = std::locale::classic();
// A custom locale using comma digit separators.
std::locale bloc(cloc, new brit_punc);
// A custom locale using period digit separators.
std::locale eloc(cloc, new euro_punc);
std::string s;
// Change the global locale:
std::locale::global(bloc);
// Format using the global locale:
s = std::format("{0:L} {0:Lx} {0:Lb}", 12345);
VERIFY( s == "12,345 3,039 11,000,000,111,001" );
s = std::format("{0:L} {0:.7Lg} {0:La}", 12345.6789);
VERIFY( s == "12,345.6789 12,345.68 1.81cd6e631f8a1p+13" );
s = std::format("{0:s} {0:L} {1:Ls} {0:Ld}", true, false);
VERIFY( s == "true yes mate nah bruv 1" );
// Format using a specific locale:
s = std::format(eloc, "{0:L} {0:Lx} {0:Lb}", 12345);
VERIFY( s == "12.345 3.039 11.000.000.111.001" );
s = std::format(eloc, "{0:L} {0:.7LG} {0:La}", 12345.6789);
VERIFY( s == "12.345,6789 12.345,68 1,81cd6e631f8a1p+13" );
s = std::format(eloc, "{0:#Lg} {0:+#.3Lg} {0:#08.4Lg}", -1234.);
VERIFY( s == "-1.234,00 -1,23e+03 -01.234," );
// Restore
std::locale::global(cloc);
}
void
test_width()
{
std::string s;
s = std::format("{:4}", "");
VERIFY( s == " " );
s = std::format("{:{}}", "", 3);
VERIFY( s == " " );
s = std::format("{1:{0}}", 2, "");
VERIFY( s == " " );
s = std::format("{:03}", 9);
VERIFY( s == "009" );
s = std::format("DR {0:{1}}: allow width {1} from arg-id", 3721, 0);
VERIFY( s == "DR 3721: allow width 0 from arg-id" );
try {
s = std::format("Negative width is an error: {0:{1}}", 123, -1);
VERIFY(false);
} catch (const std::format_error&) {
}
try {
auto args = std::make_format_args(false, true);
s = std::vformat("DR 3720: restrict type of width arg-id {0:{1}}", args);
VERIFY(false);
} catch (const std::format_error&) {
}
try {
auto args = std::make_format_args('?', '!');
s = std::vformat("DR 3720: restrict type of width arg-id {0:{1}}", args);
VERIFY(false);
} catch (const std::format_error&) {
}
}
void
test_wchar()
{
using namespace std::literals;
std::wstring s;
s = std::format(L"{} {} {} {} {} {}", L'0', 1, 2LL, 3.4, L"five", L"six"s);
VERIFY( s == L"0 1 2 3.4 five six" );
std::locale loc;
s = std::format(loc, L"{:L} {:.3s}{:Lc}", true, L"data"sv, '.');
VERIFY( s == L"true dat." );
}
void
test_minmax()
{
auto check = []<typename T>(T) {
const int digits = std::numeric_limits<T>::digits;
const std::string zeros(digits, '0');
const std::string ones(digits, '1');
auto s = std::format("{:b}" , std::numeric_limits<T>::min());
VERIFY( s == "-1" + zeros );
s = std::format("{:b}" , std::numeric_limits<T>::max());
VERIFY( s == ones );
using U = std::make_unsigned_t<T>;
s = std::format("{:0{}b}" , std::numeric_limits<U>::min(), digits + 1);
VERIFY( s == '0' + zeros );
s = std::format("{:b}" , std::numeric_limits<U>::max());
VERIFY( s == '1' + ones );
};
check(std::int8_t(0));
check(std::int16_t(0));
check(std::int32_t(0));
check(std::int64_t(0));
#ifdef __SIZEOF_INT128__
check(__int128(0));
#endif
}
void
test_p1652r1() // printf corner cases in std::format
{
std::string s;
// Problem 1: "#o" specification should not print 0 as "00"
s = std::format("{:#o}", 0);
VERIFY( s == "0" );
// Problem 2: 'c' should be able to print 65 as "A" (ASCII)
int c = 'A';
s = std::format("{:c}", c);
VERIFY( s == "A" );
// Problem 3: "-000nan" is not a floating point value
double nan = std::numeric_limits<double>::quiet_NaN();
try {
s = std::vformat("{:0=6}", std::make_format_args(nan));
VERIFY( false );
} catch (const std::format_error&) {
}
s = std::format("{:06}", nan);
VERIFY( s == " nan" );
// Problem 4: bool needs a type format specifier
s = std::format("{:s}", true);
VERIFY( s == "true" );
// Problem 5: double does not roundtrip float
s = std::format("{}", 3.31f);
VERIFY( s == "3.31" );
}
void
test_float128()
{
#ifdef __SIZEOF_FLOAT128__
auto s = std::format("{:#} != {:<+7.3f}", (__float128)-0.0, (__float128)0.5);
VERIFY( s == "-0. != +0.500 " );
#endif
}
int main()
{
test_no_args();
test_unescaped();
test_std_examples();
test_alternate_forms();
test_locale();
test_width();
test_wchar();
test_minmax();
test_p1652r1();
test_float128();
}

View file

@ -0,0 +1,96 @@
// { dg-options "-std=gnu++20" }
// { dg-do run { target c++20 } }
#include <format>
#include <vector>
#include <testsuite_hooks.h>
struct punct : std::numpunct<char>
{
std::string do_grouping() const override { return "\2"; }
std::string do_truename() const override { return "troo"; }
std::string do_falsename() const override { return "falz"; }
};
void
test()
{
char buf[4] = { };
auto [out, len] = std::format_to_n(buf, 3, "123 + 456 = {}", 579);
VERIFY( out == buf+3 );
VERIFY( len == 15 );
std::locale loc({}, new punct);
auto [out2, len2] = std::format_to_n(buf, 4, loc, "{:Ld}", 12345);
VERIFY( out2 == buf+4 );
VERIFY( len2 == 7 );
VERIFY( std::string_view(buf, 4) == "1,23" );
}
struct wpunct : std::numpunct<wchar_t>
{
std::string do_grouping() const override { return "\2"; }
std::wstring do_truename() const override { return L"troo"; }
std::wstring do_falsename() const override { return L"falz"; }
};
void
test_wchar()
{
wchar_t buf[4] = { };
auto [out, len] = std::format_to_n(buf, 3, L"123 + 456 = {}", 579);
VERIFY( out == buf+3 );
VERIFY( len == 15 );
std::locale loc({}, new wpunct);
auto [out2, len2] = std::format_to_n(buf, 4, loc, L"{:Ld}", 12345);
VERIFY( out2 == buf+4 );
VERIFY( len2 == 7 );
VERIFY( std::wstring_view(buf, 4) == L"1,23" );
}
template<typename I>
struct move_only_iterator
{
using iterator = I;
using value_type = iterator::value_type;
using difference_type = iterator::difference_type;
using iterator_category = std::output_iterator_tag;
move_only_iterator(iterator b) : base_(b) { }
move_only_iterator(move_only_iterator&&) = default;
move_only_iterator& operator=(move_only_iterator&&) = default;
move_only_iterator& operator++() { ++base_; return *this; }
move_only_iterator operator++(int) { auto tmp = *this; ++base_; return tmp; }
decltype(auto) operator*() { return *base_; }
private:
iterator base_;
};
void
test_move_only()
{
std::string str;
move_only_iterator mo(std::back_inserter(str));
auto [res, len] = std::format_to_n(std::move(mo), 4, "for{:.3} that{:c}",
"matte", (int)'!');
VERIFY( str == "form" );
VERIFY( len == 12 );
std::vector<wchar_t> vec;
move_only_iterator wmo(std::back_inserter(vec));
auto [wres, wlen] = std::format_to_n(std::move(wmo), 9, L"for{:.3} hat{:c}",
L"matte", (long)L'!');
VERIFY( std::wstring_view(vec.data(), vec.size()) == L"format ha" );
VERIFY( wlen == 11 );
}
int main()
{
test();
test_wchar();
test_move_only();
}

View file

@ -0,0 +1,52 @@
// { dg-options "-std=gnu++20" }
// { dg-do run { target c++20 } }
#include <format>
#include <string>
#include <testsuite_hooks.h>
void
test()
{
auto n = std::formatted_size("");
static_assert( std::is_same_v<std::size_t, decltype(n)> );
VERIFY( n == 0 );
n = std::formatted_size("abc");
VERIFY( n == 3 );
n = std::formatted_size("{{abc}}");
VERIFY( n == 5 );
n = std::formatted_size("{{{}}}", 1);
VERIFY( n == 3 );
n = std::formatted_size("{{{}}}", "abc");
VERIFY( n == 5 );
}
void
test_wchar()
{
auto n = std::formatted_size(L"");
static_assert( std::is_same_v<std::size_t, decltype(n)> );
VERIFY( n == 0 );
n = std::formatted_size(L"abc");
VERIFY( n == 3 );
n = std::formatted_size(L"{{abc}}");
VERIFY( n == 5 );
n = std::formatted_size(L"{{{}}}", 1);
VERIFY( n == 3 );
n = std::formatted_size(L"{{{}}}", L"abc");
VERIFY( n == 5 );
}
int main()
{
test();
test_wchar();
}

View file

@ -0,0 +1,51 @@
// { dg-options "-std=gnu++20" }
// { dg-do run { target c++20 } }
#include <format>
#include <string>
#include <iterator>
#include <testsuite_hooks.h>
template<typename C>
struct move_only_iterator
{
using iterator = std::back_insert_iterator<std::basic_string<C>>;
using value_type = iterator::value_type;
using difference_type = iterator::difference_type;
using iterator_category = std::output_iterator_tag;
move_only_iterator(iterator b) : base_(b) { }
move_only_iterator(move_only_iterator&&) = default;
move_only_iterator& operator=(move_only_iterator&&) = default;
move_only_iterator& operator++() { ++base_; return *this; }
move_only_iterator operator++(int) { auto tmp = *this; ++base_; return tmp; }
decltype(auto) operator*() { return *base_; }
private:
iterator base_;
};
void
test_move_only()
{
std::string str;
move_only_iterator<char> mo(std::back_inserter(str));
auto res = std::vformat_to(std::move(mo), "for{:.3} that{:c}",
std::make_format_args("matte", (int)'!'));
static_assert(std::is_same_v<decltype(res), decltype(mo)>);
VERIFY( str == "format that!" );
std::wstring wstr;
move_only_iterator<wchar_t> wmo(std::back_inserter(wstr));
auto wres = std::vformat_to(std::move(wmo), L"for{:.3} that{:c}",
std::make_wformat_args(L"matte", (long)L'!'));
static_assert(std::is_same_v<decltype(wres), decltype(wmo)>);
VERIFY( wstr == L"format that!" );
}
int main()
{
test_move_only();
}

View file

@ -0,0 +1,374 @@
// { dg-options "-std=gnu++20" }
// { dg-do run { target c++20 } }
#include <format>
#include <testsuite_hooks.h>
template<typename T, size_t N = 1, bool auto_indexing = true>
bool
is_std_format_spec_for(std::string_view spec)
{
std::format_parse_context pc(spec, N);
if (auto_indexing)
(void) pc.next_arg_id();
else
pc.check_arg_id(0);
std::formatter<T> f;
try {
auto end = f.parse(pc);
VERIFY( end == spec.end() || *end == '}' );
return true;
} catch (const std::format_error&) {
return false;
}
}
#if __cpp_lib_format_ranges
constexpr bool escaped_strings_supported = true;
#else
constexpr bool escaped_strings_supported = false;
#endif
void
test_char()
{
VERIFY( is_std_format_spec_for<char>("") );
VERIFY( is_std_format_spec_for<char>("<") );
VERIFY( is_std_format_spec_for<char>(">") );
VERIFY( is_std_format_spec_for<char>("^") );
VERIFY( is_std_format_spec_for<char>("0<") );
VERIFY( is_std_format_spec_for<char>("0>") );
VERIFY( is_std_format_spec_for<char>("0^") );
VERIFY( ! is_std_format_spec_for<char>("{^") );
VERIFY( ! is_std_format_spec_for<char>("+") );
VERIFY( ! is_std_format_spec_for<char>("-") );
VERIFY( ! is_std_format_spec_for<char>(" ") );
VERIFY( ! is_std_format_spec_for<char>("#") );
VERIFY( is_std_format_spec_for<char>("0d") );
VERIFY( ! is_std_format_spec_for<char>("0") );
VERIFY( ! is_std_format_spec_for<char>("00d") );
VERIFY( is_std_format_spec_for<char>("01d") );
VERIFY( ! is_std_format_spec_for<char>("0{}d") );
VERIFY( ! is_std_format_spec_for<char>("0{1}d") );
VERIFY(( is_std_format_spec_for<char, 2>("0{}d") ));
VERIFY(( ! is_std_format_spec_for<char, 2>("0{1}d") ));
VERIFY(( is_std_format_spec_for<char, 2, false>("0{1}d") ));
VERIFY( is_std_format_spec_for<char>("1") );
VERIFY( ! is_std_format_spec_for<char>("-1") );
VERIFY( is_std_format_spec_for<char>("-1d") ); // sign and width
VERIFY( ! is_std_format_spec_for<char>(".") );
VERIFY( ! is_std_format_spec_for<char>(".1") );
VERIFY( is_std_format_spec_for<char>("c") );
VERIFY( is_std_format_spec_for<char>("b") );
VERIFY( is_std_format_spec_for<char>("B") );
VERIFY( is_std_format_spec_for<char>("d") );
VERIFY( is_std_format_spec_for<char>("o") );
VERIFY( is_std_format_spec_for<char>("x") );
VERIFY( is_std_format_spec_for<char>("X") );
VERIFY( ! is_std_format_spec_for<char>("s") );
VERIFY( is_std_format_spec_for<char>("?") == escaped_strings_supported );
VERIFY( ! is_std_format_spec_for<char>("a") );
VERIFY( ! is_std_format_spec_for<char>("A") );
VERIFY( ! is_std_format_spec_for<char>("f") );
VERIFY( ! is_std_format_spec_for<char>("F") );
VERIFY( ! is_std_format_spec_for<char>("g") );
VERIFY( ! is_std_format_spec_for<char>("G") );
VERIFY( ! is_std_format_spec_for<char>("+c") );
VERIFY( ! is_std_format_spec_for<char>("+?") );
VERIFY( is_std_format_spec_for<char>("+d") );
}
void
test_int()
{
VERIFY( is_std_format_spec_for<int>("") );
VERIFY( is_std_format_spec_for<int>("<") );
VERIFY( is_std_format_spec_for<int>(">") );
VERIFY( is_std_format_spec_for<int>("^") );
VERIFY( is_std_format_spec_for<int>("0<") );
VERIFY( is_std_format_spec_for<int>("0>") );
VERIFY( is_std_format_spec_for<int>("0^") );
VERIFY( ! is_std_format_spec_for<int>("{^") );
VERIFY( is_std_format_spec_for<int>("+") );
VERIFY( is_std_format_spec_for<int>("-") );
VERIFY( is_std_format_spec_for<int>(" ") );
VERIFY( is_std_format_spec_for<int>("#") );
VERIFY( is_std_format_spec_for<int>("0d") );
VERIFY( is_std_format_spec_for<int>("0") );
VERIFY( ! is_std_format_spec_for<int>("00d") );
VERIFY( is_std_format_spec_for<int>("01d") );
VERIFY( ! is_std_format_spec_for<int>("0{}d") );
VERIFY( ! is_std_format_spec_for<int>("0{1}d") );
VERIFY(( is_std_format_spec_for<int, 2>("0{}d") ));
VERIFY(( ! is_std_format_spec_for<int, 2>("0{1}d") ));
VERIFY(( is_std_format_spec_for<int, 2, false>("0{1}d") ));
VERIFY( is_std_format_spec_for<int>("1") );
VERIFY( is_std_format_spec_for<int>("-1") ); // sign and width
VERIFY( ! is_std_format_spec_for<int>(".") );
VERIFY( ! is_std_format_spec_for<int>(".1") );
VERIFY( is_std_format_spec_for<int>("c") );
VERIFY( is_std_format_spec_for<int>("b") );
VERIFY( is_std_format_spec_for<int>("B") );
VERIFY( is_std_format_spec_for<int>("d") );
VERIFY( is_std_format_spec_for<int>("o") );
VERIFY( is_std_format_spec_for<int>("x") );
VERIFY( is_std_format_spec_for<int>("X") );
VERIFY( ! is_std_format_spec_for<int>("s") );
VERIFY( ! is_std_format_spec_for<int>("?") );
VERIFY( ! is_std_format_spec_for<int>("a") );
VERIFY( ! is_std_format_spec_for<int>("A") );
VERIFY( ! is_std_format_spec_for<int>("f") );
VERIFY( ! is_std_format_spec_for<int>("F") );
VERIFY( ! is_std_format_spec_for<int>("g") );
VERIFY( ! is_std_format_spec_for<int>("G") );
VERIFY( ! is_std_format_spec_for<int>("p") );
VERIFY( ! is_std_format_spec_for<int>("P") );
VERIFY( is_std_format_spec_for<int>("+c") ); // But LWG 3644 would change it.
VERIFY( ! is_std_format_spec_for<int>("+?") );
VERIFY( is_std_format_spec_for<int>("+d") );
}
void
test_bool()
{
VERIFY( is_std_format_spec_for<bool>("") );
VERIFY( is_std_format_spec_for<bool>("<") );
VERIFY( is_std_format_spec_for<bool>(">") );
VERIFY( is_std_format_spec_for<bool>("^") );
VERIFY( is_std_format_spec_for<bool>("0<") );
VERIFY( is_std_format_spec_for<bool>("0>") );
VERIFY( is_std_format_spec_for<bool>("0^") );
VERIFY( ! is_std_format_spec_for<bool>("{^") );
VERIFY( ! is_std_format_spec_for<bool>("+") );
VERIFY( ! is_std_format_spec_for<bool>("-") );
VERIFY( ! is_std_format_spec_for<bool>(" ") );
VERIFY( ! is_std_format_spec_for<bool>("#") );
VERIFY( is_std_format_spec_for<bool>("0d") );
VERIFY( ! is_std_format_spec_for<bool>("0") );
VERIFY( ! is_std_format_spec_for<bool>("00d") );
VERIFY( is_std_format_spec_for<bool>("01d") );
VERIFY( is_std_format_spec_for<bool>("1") );
VERIFY( ! is_std_format_spec_for<bool>("-1") );
VERIFY( is_std_format_spec_for<bool>("-1d") ); // sign and width
VERIFY( ! is_std_format_spec_for<bool>(".") );
VERIFY( ! is_std_format_spec_for<bool>(".1") );
VERIFY( ! is_std_format_spec_for<bool>("c") );
VERIFY( is_std_format_spec_for<bool>("b") );
VERIFY( is_std_format_spec_for<bool>("B") );
VERIFY( is_std_format_spec_for<bool>("d") );
VERIFY( is_std_format_spec_for<bool>("o") );
VERIFY( is_std_format_spec_for<bool>("x") );
VERIFY( is_std_format_spec_for<bool>("X") );
VERIFY( is_std_format_spec_for<bool>("s") );
VERIFY( ! is_std_format_spec_for<bool>("?") );
VERIFY( ! is_std_format_spec_for<bool>("a") );
VERIFY( ! is_std_format_spec_for<bool>("A") );
VERIFY( ! is_std_format_spec_for<bool>("f") );
VERIFY( ! is_std_format_spec_for<bool>("F") );
VERIFY( ! is_std_format_spec_for<bool>("g") );
VERIFY( ! is_std_format_spec_for<bool>("G") );
VERIFY( ! is_std_format_spec_for<bool>("p") );
VERIFY( ! is_std_format_spec_for<bool>("P") );
VERIFY( ! is_std_format_spec_for<bool>("+s") );
VERIFY( is_std_format_spec_for<bool>("+d") );
}
void
test_float()
{
VERIFY( is_std_format_spec_for<float>("") );
VERIFY( is_std_format_spec_for<float>("<") );
VERIFY( is_std_format_spec_for<float>(">") );
VERIFY( is_std_format_spec_for<float>("^") );
VERIFY( is_std_format_spec_for<float>("0<") );
VERIFY( is_std_format_spec_for<float>("0>") );
VERIFY( is_std_format_spec_for<float>("0^") );
VERIFY( ! is_std_format_spec_for<float>("{^") );
VERIFY( is_std_format_spec_for<float>("+") );
VERIFY( is_std_format_spec_for<float>("-") );
VERIFY( is_std_format_spec_for<float>(" ") );
VERIFY( is_std_format_spec_for<float>("#") );
VERIFY( is_std_format_spec_for<float>("0f") );
VERIFY( is_std_format_spec_for<float>("0") );
VERIFY( ! is_std_format_spec_for<float>("00f") );
VERIFY( is_std_format_spec_for<float>("01f") );
VERIFY( ! is_std_format_spec_for<float>("0{}f") );
VERIFY( ! is_std_format_spec_for<float>("0{1}f") );
VERIFY(( is_std_format_spec_for<float, 2>("0{}f") ));
VERIFY(( ! is_std_format_spec_for<float, 2>("0{1}f") ));
VERIFY(( is_std_format_spec_for<float, 2, false>("0{1}f") ));
VERIFY( is_std_format_spec_for<float>("1") );
VERIFY( is_std_format_spec_for<float>("-1") ); // sign and width
VERIFY( ! is_std_format_spec_for<float>(".") );
VERIFY( is_std_format_spec_for<float>(".1") );
VERIFY( ! is_std_format_spec_for<float>(".{}") );
VERIFY( ! is_std_format_spec_for<float>(".{1}") );
VERIFY(( is_std_format_spec_for<float, 2>(".{}") ));
VERIFY(( ! is_std_format_spec_for<float, 2>(".{1}") ));
VERIFY(( is_std_format_spec_for<float, 2, false>(".{1}") ));
VERIFY(( ! is_std_format_spec_for<float, 2>("{}.{}") ));
VERIFY(( is_std_format_spec_for<float, 3>("{}.{}") ));
VERIFY(( is_std_format_spec_for<float, 2, false>("{1}.{1}") ));
VERIFY(( is_std_format_spec_for<float, 3, false>("{2}.{1}") ));
VERIFY( ! is_std_format_spec_for<float>("c") );
VERIFY( ! is_std_format_spec_for<float>("b") );
VERIFY( ! is_std_format_spec_for<float>("B") );
VERIFY( ! is_std_format_spec_for<float>("d") );
VERIFY( ! is_std_format_spec_for<float>("o") );
VERIFY( ! is_std_format_spec_for<float>("x") );
VERIFY( ! is_std_format_spec_for<float>("X") );
VERIFY( ! is_std_format_spec_for<float>("s") );
VERIFY( ! is_std_format_spec_for<float>("?") );
VERIFY( is_std_format_spec_for<float>("a") );
VERIFY( is_std_format_spec_for<float>("A") );
VERIFY( is_std_format_spec_for<float>("f") );
VERIFY( is_std_format_spec_for<float>("F") );
VERIFY( is_std_format_spec_for<float>("g") );
VERIFY( is_std_format_spec_for<float>("G") );
VERIFY( ! is_std_format_spec_for<float>("p") );
VERIFY( ! is_std_format_spec_for<float>("P") );
VERIFY( is_std_format_spec_for<float>("+f") );
VERIFY( is_std_format_spec_for<float>("_<+#09.6Lf") );
VERIFY( is_std_format_spec_for<float>("<+#09.6Lf") );
VERIFY( is_std_format_spec_for<float>("<+#9.6Lf") );
VERIFY( is_std_format_spec_for<float>(".0006f") );
}
void
test_pointer()
{
VERIFY( is_std_format_spec_for<void*>("") );
VERIFY( is_std_format_spec_for<void*>("<") );
VERIFY( is_std_format_spec_for<void*>(">") );
VERIFY( is_std_format_spec_for<void*>("^") );
VERIFY( is_std_format_spec_for<void*>("0<") );
VERIFY( is_std_format_spec_for<void*>("0>") );
VERIFY( is_std_format_spec_for<void*>("0^") );
VERIFY( ! is_std_format_spec_for<void*>("{^") );
VERIFY( ! is_std_format_spec_for<void*>("+") );
VERIFY( ! is_std_format_spec_for<void*>("-") );
VERIFY( ! is_std_format_spec_for<void*>(" ") );
VERIFY( ! is_std_format_spec_for<void*>("#") );
VERIFY( is_std_format_spec_for<void*>("0p") ); // P2510
VERIFY( is_std_format_spec_for<void*>("0") );
VERIFY( ! is_std_format_spec_for<void*>("00p") );
VERIFY( is_std_format_spec_for<void*>("01p") );
VERIFY( is_std_format_spec_for<void*>("1") );
VERIFY( ! is_std_format_spec_for<void*>("-1") );
VERIFY( ! is_std_format_spec_for<void*>("-1p") );
VERIFY( ! is_std_format_spec_for<void*>(".") );
VERIFY( ! is_std_format_spec_for<void*>(".1") );
VERIFY( ! is_std_format_spec_for<void*>("c") );
VERIFY( ! is_std_format_spec_for<void*>("b") );
VERIFY( ! is_std_format_spec_for<void*>("B") );
VERIFY( ! is_std_format_spec_for<void*>("d") );
VERIFY( ! is_std_format_spec_for<void*>("o") );
VERIFY( ! is_std_format_spec_for<void*>("x") );
VERIFY( ! is_std_format_spec_for<void*>("X") );
VERIFY( ! is_std_format_spec_for<void*>("s") );
VERIFY( ! is_std_format_spec_for<void*>("?") );
VERIFY( is_std_format_spec_for<void*>("p") );
VERIFY( is_std_format_spec_for<void*>("P") );
VERIFY( ! is_std_format_spec_for<void*>("a") );
VERIFY( ! is_std_format_spec_for<void*>("A") );
VERIFY( ! is_std_format_spec_for<void*>("f") );
VERIFY( ! is_std_format_spec_for<void*>("F") );
VERIFY( ! is_std_format_spec_for<void*>("g") );
VERIFY( ! is_std_format_spec_for<void*>("G") );
VERIFY( ! is_std_format_spec_for<void*>("+p") );
}
void
test_string()
{
VERIFY( is_std_format_spec_for<const char*>("") );
VERIFY( is_std_format_spec_for<const char*>("<") );
VERIFY( is_std_format_spec_for<const char*>(">") );
VERIFY( is_std_format_spec_for<const char*>("^") );
VERIFY( is_std_format_spec_for<const char*>("0<") );
VERIFY( is_std_format_spec_for<const char*>("0>") );
VERIFY( is_std_format_spec_for<const char*>("0^") );
VERIFY( ! is_std_format_spec_for<const char*>("{^") );
VERIFY( ! is_std_format_spec_for<const char*>("+") );
VERIFY( ! is_std_format_spec_for<const char*>("-") );
VERIFY( ! is_std_format_spec_for<const char*>(" ") );
VERIFY( ! is_std_format_spec_for<const char*>("#") );
VERIFY( ! is_std_format_spec_for<const char*>("0") );
VERIFY( ! is_std_format_spec_for<const char*>("01s") );
VERIFY( is_std_format_spec_for<const char*>("1") );
VERIFY( ! is_std_format_spec_for<const char*>("-1") );
VERIFY( ! is_std_format_spec_for<const char*>("-1s") );
VERIFY( ! is_std_format_spec_for<const char*>(".") );
VERIFY( is_std_format_spec_for<const char*>(".1") );
VERIFY( ! is_std_format_spec_for<const char*>(".{}") );
VERIFY(( ! is_std_format_spec_for<const char*, 1, false>(".{1}") ));
VERIFY(( is_std_format_spec_for<const char*, 1, false>(".{0}") ));
VERIFY(( is_std_format_spec_for<const char*, 2>(".{}") ));
VERIFY(( is_std_format_spec_for<const char*, 2, false>(".{1}") ));
VERIFY( ! is_std_format_spec_for<const char*>("c") );
VERIFY( ! is_std_format_spec_for<const char*>("b") );
VERIFY( ! is_std_format_spec_for<const char*>("B") );
VERIFY( ! is_std_format_spec_for<const char*>("d") );
VERIFY( ! is_std_format_spec_for<const char*>("o") );
VERIFY( ! is_std_format_spec_for<const char*>("x") );
VERIFY( ! is_std_format_spec_for<const char*>("X") );
VERIFY( is_std_format_spec_for<const char*>("s") );
VERIFY( is_std_format_spec_for<const char*>("?") == escaped_strings_supported );
VERIFY( ! is_std_format_spec_for<const char*>("p") );
VERIFY( ! is_std_format_spec_for<const char*>("P") );
VERIFY( ! is_std_format_spec_for<const char*>("a") );
VERIFY( ! is_std_format_spec_for<const char*>("A") );
VERIFY( ! is_std_format_spec_for<const char*>("f") );
VERIFY( ! is_std_format_spec_for<const char*>("F") );
VERIFY( ! is_std_format_spec_for<const char*>("g") );
VERIFY( ! is_std_format_spec_for<const char*>("G") );
VERIFY( is_std_format_spec_for<const char*>("*^6s") );
VERIFY( is_std_format_spec_for<const char*>(">6s") );
VERIFY( is_std_format_spec_for<const char*>("_<6.4?") == escaped_strings_supported );
}
struct S { };
template<>
struct std::formatter<S, char>
{
constexpr std::format_parse_context::iterator
parse(std::format_parse_context& pc)
{
std::string_view spec(pc.begin(), pc.end());
auto p = spec.find('}');
if (p == std::string_view::npos)
p = spec.size();
if (p == 0)
throw std::format_error("empty format-spec");
if (spec != "custom")
throw std::format_error("invalid format-spec");
return pc.begin() + p;
}
std::format_context::iterator
format(const S&, std::format_context&) const;
};
void
test_custom()
{
VERIFY( is_std_format_spec_for<S>("custom") );
VERIFY( ! is_std_format_spec_for<S>("customer") );
VERIFY( ! is_std_format_spec_for<S>("custard") );
VERIFY( ! is_std_format_spec_for<S>("") );
}
int main()
{
test_char();
test_int();
test_bool();
test_float();
test_string();
test_pointer();
test_custom();
}

View file

@ -0,0 +1,131 @@
// { dg-options "-std=gnu++20" }
// { dg-do run { target c++20 } }
#include <format>
#include <testsuite_hooks.h>
template<typename... Args>
bool
is_format_string_for(const char* str, Args&&... args)
{
try {
(void) std::vformat(str, std::make_format_args(args...));
return true;
} catch (const std::format_error&) {
return false;
}
}
void
test_no_args()
{
VERIFY( is_format_string_for("") );
VERIFY( is_format_string_for("chars") );
VERIFY( is_format_string_for(" The Great Escape {{}} ") );
VERIFY( ! is_format_string_for("{") );
VERIFY( ! is_format_string_for("}") );
VERIFY( ! is_format_string_for("}{") );
VERIFY( ! is_format_string_for("{{}") );
VERIFY( ! is_format_string_for("{{{") );
VERIFY( ! is_format_string_for("{{{{{") );
}
void
test_indexing()
{
VERIFY( is_format_string_for("{} to {}", "a", "b") ); // automatic indexing
VERIFY( is_format_string_for("{1} to {0}", "a", "b") ); // manual indexing
VERIFY( ! is_format_string_for("{0} to {}", "a", "b") ); // mixed indexing
VERIFY( ! is_format_string_for("{} to {1}", "a", "b") ); // mixed indexing
VERIFY( is_format_string_for("{} {} {}", 1, 2, 3) );
VERIFY( is_format_string_for("{} {} {}", 1, 2, 3, 4) );
VERIFY( is_format_string_for("{0} {1} {2}", 1, 2, 3, 4) );
VERIFY( is_format_string_for("{1} {2} {3}", 1, 2, 3, 4) );
VERIFY( is_format_string_for("{3} {3} {3}", 1, 2, 3, 4) );
VERIFY( ! is_format_string_for("{2}", 1, 2) );
VERIFY( ! is_format_string_for("{0} {}", 1) );
VERIFY( ! is_format_string_for("{} {0}", 1) );
}
#if __cpp_lib_format_ranges
constexpr bool escaped_strings_supported = true;
#else
constexpr bool escaped_strings_supported = false;
#endif
void
test_format_spec()
{
VERIFY( is_format_string_for("{:}", 1) );
VERIFY( is_format_string_for("{0:}", 1) );
VERIFY( is_format_string_for("{2:}", 1, 2, 3) );
VERIFY( is_format_string_for("{0:s} {0:}", "str") );
VERIFY( is_format_string_for("{0:} {0:c}", 'c') );
VERIFY( is_format_string_for("{0:p} {0:}", nullptr) );
VERIFY( is_format_string_for("{:d} {:+d}", true, true) );
VERIFY( is_format_string_for("{:0<-#03Ld}", 1) );
VERIFY( is_format_string_for("{1:0<-#03.4Lf}", 1, 2.3) );
VERIFY( is_format_string_for("{1:3.3f}", 1, 2.3) );
VERIFY( is_format_string_for("{:#d}", 'c') );
VERIFY( is_format_string_for("{:#d}", true) );
VERIFY( is_format_string_for("{0:s} {0:?}", "str") == escaped_strings_supported );
VERIFY( is_format_string_for("{0:} {0:?}", 'c') == escaped_strings_supported );
// Invalid sign options.
VERIFY( ! is_format_string_for("{:+}", "str") );
VERIFY( ! is_format_string_for("{:+s}", "str") );
VERIFY( ! is_format_string_for("{:+}", 'c') );
VERIFY( ! is_format_string_for("{:+c}", 'c') );
VERIFY( ! is_format_string_for("{:+p}", nullptr) );
VERIFY( ! is_format_string_for("{:+}", true) );
VERIFY( ! is_format_string_for("{:+s}", true) );
VERIFY( ! is_format_string_for("{:+?}", "str") );
VERIFY( ! is_format_string_for("{:+?}", 'c') );
// Invalid alternate forms.
VERIFY( ! is_format_string_for("{:#}", "str") );
VERIFY( ! is_format_string_for("{:#s}", "str") );
VERIFY( ! is_format_string_for("{:#}", 'c') );
VERIFY( ! is_format_string_for("{:#c}", 'c') );
VERIFY( ! is_format_string_for("{:#}", true) );
VERIFY( ! is_format_string_for("{:#s}", true) );
VERIFY( ! is_format_string_for("{:#}", nullptr) );
VERIFY( ! is_format_string_for("{:#p}", nullptr) );
VERIFY( ! is_format_string_for("{:#?}", "str") );
VERIFY( ! is_format_string_for("{:#?}", 'c') );
// Precision only valid for string and floating-point types.
VERIFY( ! is_format_string_for("{:.3d}", 1) );
VERIFY( ! is_format_string_for("{:3.3d}", 1) );
VERIFY( is_format_string_for("{:3.3s}", "str") );
VERIFY( ! is_format_string_for("{:3.3s}", 'c') );
VERIFY( ! is_format_string_for("{:3.3p}", nullptr) );
// Invalid presentation types for integers.
VERIFY( ! is_format_string_for("{:f}", 1) );
VERIFY( ! is_format_string_for("{:s}", 1) );
VERIFY( ! is_format_string_for("{:g}", 1) );
VERIFY( ! is_format_string_for("{:E}", 1) );
VERIFY( ! is_format_string_for("{:D}", 1) );
// Invalid presentation types for floating-point types.
VERIFY( ! is_format_string_for("{:d}", 1.2) );
VERIFY( ! is_format_string_for("{:b}", 1.2) );
VERIFY( ! is_format_string_for("{:x}", 1.2) );
VERIFY( ! is_format_string_for("{:s}", 1.2) );
// Invalid presentation types for strings.
VERIFY( ! is_format_string_for("{:S}", "str") );
VERIFY( ! is_format_string_for("{:d}", "str") );
}
int main()
{
test_no_args();
test_indexing();
test_format_spec();
}

View file

@ -0,0 +1,7 @@
// { dg-options "-std=gnu++20" }
// { dg-do compile { target c++20 } }
#include <format>
auto s = std::format(" {9} ");
// { dg-error "invalid.arg.id" "" { target *-*-* } 0 }