// { dg-do run { target c++23 } } // { dg-options "-fexec-charset=UTF-8" } // { dg-timeout-factor 2 } #include #include #include #include #include #include #include #include #include struct NotFormattable {}; static_assert(!std::formattable, char>); static_assert(!std::formattable, wchar_t>); template 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; } } template bool is_format_string_for(const wchar_t* str, Args&&... args) { try { (void) std::vformat(str, std::make_wformat_args(args...)); return true; } catch (const std::format_error&) { return false; } } template bool is_range_formatter_spec_for(CharT const* spec, Rg&& rg) { using V = std::remove_cvref_t>; std::range_formatter fmt; std::basic_format_parse_context pc(spec); try { (void)fmt.parse(pc); return true; } catch (const std::format_error&) { return false; } } void test_format_string() { // invalid format spec 'p' VERIFY( !is_range_formatter_spec_for("p", std::vector()) ); VERIFY( !is_format_string_for("{:p}", std::vector()) ); VERIFY( !is_range_formatter_spec_for("np", std::vector()) ); VERIFY( !is_format_string_for("{:np}", std::vector()) ); // width needs to be integer type VERIFY( !is_format_string_for("{:{}}", std::vector(), 1.0f) ); // element format needs to be valid VERIFY( !is_range_formatter_spec_for(":p", std::vector()) ); VERIFY( !is_format_string_for("{::p}", std::vector()) ); VERIFY( !is_range_formatter_spec_for("n:p", std::vector()) ); VERIFY( !is_format_string_for("{:n:p}", std::vector()) ); } #define WIDEN_(C, S) ::std::__format::_Widen(S, L##S) #define WIDEN(S) WIDEN_(CharT, S) template void test_output() { using Sv = std::basic_string_view; using T = std::ranges::range_value_t; auto makeRange = [](Storage& s) -> Range { if constexpr (std::is_same_v, Storage>) return s; else return Range(std::ranges::data(s), std::ranges::data(s) + std::ranges::size(s)); }; std::basic_string res; size_t size = 0; Storage v1{1, 2, 3}; res = std::format(WIDEN("{}"), makeRange(v1)); VERIFY( res == WIDEN("[1, 2, 3]") ); res = std::format(WIDEN("{:}"), makeRange(v1)); VERIFY( res == WIDEN("[1, 2, 3]") ); res = std::format(WIDEN("{:n}"), makeRange(v1)); VERIFY( res == WIDEN("1, 2, 3") ); res = std::format(WIDEN("{:3}"), makeRange(v1)); VERIFY( res == WIDEN("[1, 2, 3]") ); res = std::format(WIDEN("{:10}"), makeRange(v1)); VERIFY( res == WIDEN("[1, 2, 3] ") ); res = std::format(WIDEN("{:{}}"), makeRange(v1), 10); VERIFY( res == WIDEN("[1, 2, 3] ") ); res = std::format(WIDEN("{1:{0}}"), 10, makeRange(v1)); VERIFY( res == WIDEN("[1, 2, 3] ") ); res = std::format(WIDEN("{:10n}"), makeRange(v1)); VERIFY( res == WIDEN("1, 2, 3 ") ); res = std::format(WIDEN("{:*<11}"), makeRange(v1)); VERIFY( res == WIDEN("[1, 2, 3]**") ); res = std::format(WIDEN("{:->12}"), makeRange(v1)); VERIFY( res == WIDEN("---[1, 2, 3]") ); res = std::format(WIDEN("{:=^13}"), makeRange(v1)); VERIFY( res == WIDEN("==[1, 2, 3]==") ); res = std::format(WIDEN("{:=^13n}"), makeRange(v1)); VERIFY( res == WIDEN("===1, 2, 3===") ); res = std::format(WIDEN("{::#x}"), makeRange(v1)); VERIFY( res == WIDEN("[0x1, 0x2, 0x3]") ); res = std::format(WIDEN("{:|^25n:#05x}"), makeRange(v1)); VERIFY( res == WIDEN("|||0x001, 0x002, 0x003|||") ); // ':' is start of the format string for element res = std::format(WIDEN("{::^+04}"), makeRange(v1)); VERIFY( res == WIDEN("[ +1 , +2 , +3 ]") ); size = std::formatted_size(WIDEN("{:}"), makeRange(v1)); VERIFY( size == Sv(WIDEN("[1, 2, 3]")).size() ); size = std::formatted_size(WIDEN("{:3}"), makeRange(v1)); VERIFY( size == Sv(WIDEN("[1, 2, 3]")).size() ); size = std::formatted_size(WIDEN("{:10}"), makeRange(v1)); VERIFY( size == 10 ); size = std::formatted_size(WIDEN("{:|^25n:#05x}"), makeRange(v1)); VERIFY( size == 25 ); } template void test_output_cont() { test_output(); test_output(); } template void test_output_view() { test_output(); test_output(); } void test_outputs() { using namespace __gnu_test; test_output_cont>(); test_output_cont>(); test_output_cont>(); test_output_view>(); test_output_view>(); test_output_view>(); test_output_view>(); test_output_view>(); test_output_view>(); test_output_view>(); test_output_view>(); } void test_nested() { std::vector> v { {1, 2}, {11, 12} }; std::string res = std::format("{}", v); VERIFY( res == "[[1, 2], [11, 12]]" ); res = std::format("{:+^18:n:02}", v); VERIFY( res == "+[01, 02, 11, 12]+" ); } bool strip_quote(std::string_view& v) { if (!v.starts_with('"')) return false; v.remove_prefix(1); return true; } bool strip_prefix(std::string_view& v, std::string_view expected, bool quoted = false) { if (quoted && !strip_quote(v)) return false; if (!v.starts_with(expected)) return false; v.remove_prefix(expected.size()); if (quoted && !strip_quote(v)) return false; return true; } bool strip_squares(std::string_view& v) { if (!v.starts_with('[') || !v.ends_with(']')) return false; v.remove_prefix(1); v.remove_suffix(1); return true; } bool strip_prefix(std::string_view& v, size_t n, char c) { size_t pos = v.find_first_not_of(c); if (pos == std::string_view::npos) pos = v.size(); if (pos != n) return false; v.remove_prefix(n); return true; } void test_padding() { std::string res; std::string_view resv; // width is 3, size is 15 std::string in = "o\u0302\u0323i\u0302\u0323u\u0302\u0323"; in += in; // width is 6, size is 30 in += in; // width is 12, size is 60 in += in; // width is 24, size is 120 in += in; // width is 48, size is 240 // width is 192, size is 960 std::vector const vs{in, in, in, in}; auto const check_elems = [=](std::string_view& v, bool quoted) { VERIFY( strip_prefix(v, in, quoted) ); VERIFY( strip_prefix(v, ", ", false) ); VERIFY( strip_prefix(v, in, quoted) ); VERIFY( strip_prefix(v, ", ", false) ); VERIFY( strip_prefix(v, in, quoted) ); VERIFY( strip_prefix(v, ", ", false) ); VERIFY( strip_prefix(v, in, quoted) ); return v.empty(); }; resv = res = std::format("{}", vs); VERIFY( strip_squares(resv) ); VERIFY( check_elems(resv, true) ); resv = res = std::format("{:n}", vs); VERIFY( check_elems(resv, true) ); resv = res = std::format("{::}", vs); VERIFY( strip_squares(resv) ); VERIFY( check_elems(resv, false) ); resv = res = std::format("{:n:}", vs); VERIFY( check_elems(resv, false) ); resv = res = std::format("{:*>10}", vs); VERIFY( strip_squares(resv) ); VERIFY( check_elems(resv, true) ); resv = res = std::format("{:*>10n}", vs); VERIFY( check_elems(resv, true) ); resv = res = std::format("{:*>10:}", vs); VERIFY( strip_squares(resv) ); VERIFY( check_elems(resv, false) ); resv = res = std::format("{:*>10n:}", vs); VERIFY( check_elems(resv, false) ); resv = res = std::format("{:*>256}", vs); VERIFY( strip_prefix(resv, 48, '*') ); VERIFY( strip_squares(resv) ); VERIFY( check_elems(resv, true) ); resv = res = std::format("{:*>256n}", vs); VERIFY( strip_prefix(resv, 50, '*') ); VERIFY( check_elems(resv, true) ); resv = res = std::format("{:*>240}", vs); VERIFY( strip_prefix(resv, 32, '*') ); VERIFY( strip_squares(resv) ); VERIFY( check_elems(resv, true) ); resv = res = std::format("{:*>240n}", vs); VERIFY( strip_prefix(resv, 34, '*') ); VERIFY( check_elems(resv, true) ); resv = res = std::format("{:*>240:}", vs); VERIFY( strip_prefix(resv, 40, '*') ); VERIFY( strip_squares(resv) ); VERIFY( check_elems(resv, false) ); resv = res = std::format("{:*>240n:}", vs); VERIFY( strip_prefix(resv, 42, '*') ); VERIFY( check_elems(resv, false) ); } int main() { test_format_string(); test_outputs(); test_nested(); test_padding(); }