| // { dg-options "-std=gnu++20" } |
| // { dg-do run { target c++20 } } |
| |
| #include <format> |
| #include <testsuite_hooks.h> |
| |
| template<typename T, bool auto_indexing = true> |
| bool |
| is_std_format_spec_for(std::string_view spec) |
| { |
| std::format_parse_context pc(spec); |
| 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, 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{1}d") ); |
| 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, 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>("0{1}f") ); |
| VERIFY(( is_std_format_spec_for<float, 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, false>(".{1}") )); |
| VERIFY( is_std_format_spec_for<float>("{}.{}") ); |
| VERIFY(( is_std_format_spec_for<float, false>("{1}.{1}") )); |
| VERIFY(( is_std_format_spec_for<float, 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*>("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*>("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") ); |
| |
| #if __cplusplus > 202302L || ! defined __STRICT_ANSI__ |
| // As an extension, we support P2510R3 Formatting pointers |
| VERIFY( is_std_format_spec_for<void*>("P") ); |
| VERIFY( is_std_format_spec_for<void*>("0p") ); |
| VERIFY( is_std_format_spec_for<void*>("0P") ); |
| VERIFY( is_std_format_spec_for<void*>("0") ); |
| VERIFY( is_std_format_spec_for<void*>("01p") ); |
| VERIFY( ! is_std_format_spec_for<void*>("00p") ); |
| #endif |
| } |
| |
| 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*, false>(".{0}") )); |
| VERIFY(( is_std_format_spec_for<const char*, 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(); |
| } |