| // { dg-do run { target c++23 } } |
| // { dg-timeout-factor 2 } |
| |
| #include <flat_map> |
| #include <format> |
| #include <list> |
| #include <map> |
| #include <span> |
| #include <testsuite_hooks.h> |
| #include <testsuite_iterators.h> |
| #include <vector> |
| |
| struct NotFormattable |
| { |
| friend auto operator<=>(NotFormattable, NotFormattable) = default; |
| }; |
| |
| static_assert( !std::formattable<std::map<int, NotFormattable>, char> ); |
| static_assert( !std::formattable<std::map<NotFormattable, int>, wchar_t> ); |
| |
| 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; |
| } |
| } |
| |
| template<typename... Args> |
| 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<typename Rg, typename CharT> |
| bool is_range_formatter_spec_for(CharT const* spec, Rg&& rg) |
| { |
| using V = std::remove_cvref_t<std::ranges::range_reference_t<Rg>>; |
| std::range_formatter<V, CharT> fmt; |
| std::basic_format_parse_context<CharT> pc(spec); |
| try { |
| (void)fmt.parse(pc); |
| return true; |
| } catch (const std::format_error&) { |
| return false; |
| } |
| } |
| |
| #define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S) |
| #define WIDEN(S) WIDEN_(CharT, S) |
| |
| void |
| test_format_string() |
| { |
| // only pair<T, U> amd tuple<T, U> value types are supported |
| VERIFY( !is_range_formatter_spec_for("m", std::vector<int>()) ); |
| VERIFY( !is_format_string_for("{:m}", std::vector<int>()) ); |
| VERIFY( !is_range_formatter_spec_for("m", std::vector<std::tuple<int, int, int>>()) ); |
| VERIFY( !is_format_string_for("{:m}", std::vector<std::tuple<int, int, int>>()) ); |
| |
| // invalid format stringss |
| VERIFY( !is_range_formatter_spec_for("?m", std::vector<std::pair<int, int>>()) ); |
| VERIFY( !is_format_string_for("{:?m}", std::vector<std::pair<int, int>>()) ); |
| VERIFY( !is_range_formatter_spec_for("m:", std::vector<std::pair<int, int>>()) ); |
| VERIFY( !is_format_string_for("{:m:}", std::vector<std::pair<int, int>>()) ); |
| |
| // precision is not supported |
| VERIFY( !is_range_formatter_spec_for(".10m", std::vector<std::pair<int, int>>()) ); |
| VERIFY( !is_format_string_for("{:.10m}", std::vector<std::pair<int, int>>()) ); |
| VERIFY( !is_format_string_for("{:.{}m}", std::vector<std::pair<int, int>>(), 10) ); |
| |
| // width needs to be integer type |
| VERIFY( !is_format_string_for("{:{}m}", std::vector<std::pair<int, int>>(), 1.0f) ); |
| } |
| |
| template<typename CharT, typename Range> |
| void test_output(bool mapIsDefault) |
| { |
| using Sv = std::basic_string_view<CharT>; |
| using Pt = std::ranges::range_value_t<Range>; |
| using Ft = std::remove_cvref_t<std::tuple_element_t<0, Pt>>; |
| using St = std::remove_cvref_t<std::tuple_element_t<1, Pt>>; |
| auto makeRange = [](std::span<Pt> s) { |
| return Range(s.data(), s.data() + s.size()); |
| }; |
| |
| std::basic_string<CharT> res; |
| size_t size = 0; |
| |
| Ft f1[]{1, 2, 3}; |
| St s1[]{11, 22, 33}; |
| Pt v1[]{{f1[0], s1[0]}, {f1[1], s1[1]}, {f1[2], s1[2]}}; |
| |
| res = std::format(WIDEN("{}"), makeRange(v1)); |
| if (mapIsDefault) |
| VERIFY( res == WIDEN("{1: 11, 2: 22, 3: 33}") ); |
| else |
| VERIFY( res == WIDEN("[(1, 11), (2, 22), (3, 33)]") ); |
| |
| res = std::format(WIDEN("{:m}"), makeRange(v1)); |
| VERIFY( res == WIDEN("{1: 11, 2: 22, 3: 33}") ); |
| res = std::format(WIDEN("{:nm}"), makeRange(v1)); |
| VERIFY( res == WIDEN("1: 11, 2: 22, 3: 33") ); |
| |
| res = std::format(WIDEN("{:3m}"), makeRange(v1)); |
| VERIFY( res == WIDEN("{1: 11, 2: 22, 3: 33}") ); |
| |
| res = std::format(WIDEN("{:25m}"), makeRange(v1)); |
| VERIFY( res == WIDEN("{1: 11, 2: 22, 3: 33} ") ); |
| |
| res = std::format(WIDEN("{:{}m}"), makeRange(v1), 25); |
| VERIFY( res == WIDEN("{1: 11, 2: 22, 3: 33} ") ); |
| |
| res = std::format(WIDEN("{1:{0}m}"), 25, makeRange(v1)); |
| VERIFY( res == WIDEN("{1: 11, 2: 22, 3: 33} ") ); |
| |
| res = std::format(WIDEN("{:25nm}"), makeRange(v1)); |
| VERIFY( res == WIDEN("1: 11, 2: 22, 3: 33 ") ); |
| |
| res = std::format(WIDEN("{:*<23m}"), makeRange(v1)); |
| VERIFY( res == WIDEN("{1: 11, 2: 22, 3: 33}**") ); |
| |
| res = std::format(WIDEN("{:->24m}"), makeRange(v1)); |
| VERIFY( res == WIDEN("---{1: 11, 2: 22, 3: 33}") ); |
| |
| res = std::format(WIDEN("{:=^25m}"), makeRange(v1)); |
| VERIFY( res == WIDEN("=={1: 11, 2: 22, 3: 33}==") ); |
| |
| res = std::format(WIDEN("{:=^25nm}"), makeRange(v1)); |
| VERIFY( res == WIDEN("===1: 11, 2: 22, 3: 33===") ); |
| |
| size = std::formatted_size(WIDEN("{:m}"), makeRange(v1)); |
| VERIFY( size == Sv(WIDEN("{1: 11, 2: 22, 3: 33}")).size() ); |
| |
| size = std::formatted_size(WIDEN("{:3m}"), makeRange(v1)); |
| VERIFY( size == Sv(WIDEN("{1: 11, 2: 22, 3: 33}")).size() ); |
| |
| size = std::formatted_size(WIDEN("{:25m}"), makeRange(v1)); |
| VERIFY( size == 25 ); |
| } |
| |
| template<class Range> |
| void test_output_c(bool mapIsDefault = false) |
| { |
| test_output<char, Range>(mapIsDefault); |
| test_output<wchar_t, Range>(mapIsDefault); |
| } |
| |
| template<template<typename> class RangeT> |
| void test_output_pc() |
| { |
| test_output_c<RangeT<std::pair<int, int>>>(); |
| test_output_c<RangeT<std::pair<const int, int>>>(); |
| test_output_c<RangeT<std::tuple<const int&, int&>>>(); |
| } |
| |
| void |
| test_outputs() |
| { |
| using namespace __gnu_test; |
| test_output_c<std::map<int, int>>(true); |
| test_output_c<std::flat_map<int, int>>(true); |
| |
| test_output_pc<std::vector>(); |
| test_output_pc<std::list>(); |
| test_output_pc<std::span>(); |
| |
| test_output_pc<test_forward_range>(); |
| test_output_pc<test_input_range>(); |
| test_output_pc<test_input_range_nocopy>(); |
| } |
| |
| void |
| test_nested() |
| { |
| std::vector<std::map<int, std::string>> vm{ |
| {{1, "one"}, {2, "two"}}, |
| {{1, "jeden"}, {2, "dwa"}}, |
| }; |
| std::string res; |
| |
| res = std::format("{}", vm); |
| VERIFY( res == R"([{1: "one", 2: "two"}, {1: "jeden", 2: "dwa"}])" ); |
| res = std::format("{:n:n}", vm); |
| VERIFY( res == R"(1: "one", 2: "two", 1: "jeden", 2: "dwa")" ); |
| |
| std::map<std::string, std::vector<std::string>> mv{ |
| {"english", {"zero", "one", "two"}}, |
| {"polish", {"zero", "jeden", "dwa"}}, |
| }; |
| res = std::format("{}", mv); |
| VERIFY( res == R"({"english": ["zero", "one", "two"], "polish": ["zero", "jeden", "dwa"]})" ); |
| } |
| |
| int main() |
| { |
| test_format_string(); |
| test_outputs(); |
| test_nested(); |
| } |