// { dg-do run { target c++20 } } #include #include static_assert(std::is_constructible_v); static_assert(std::is_constructible_v); #if __cpp_lib_format < 202305 constexpr bool construct_with_num_args = true; #else constexpr bool construct_with_num_args = false; #endif static_assert(std::is_constructible_v == construct_with_num_args); static_assert(std::is_constructible_v == construct_with_num_args); static_assert( ! std::is_constructible_v); static_assert( ! std::is_constructible_v); static_assert( ! std::is_convertible_v ); static_assert( ! std::is_convertible_v ); static_assert( ! std::is_default_constructible_v ); static_assert( ! std::is_copy_constructible_v ); static_assert( ! std::is_move_constructible_v ); static_assert( ! std::is_copy_assignable_v ); static_assert( ! std::is_move_assignable_v ); // This concept is satisfied if the next_arg_id() call is a constant expression template> concept arg_id_available = requires { typename std::integral_constant::type; }; void test_members() { std::string_view s = "spec string"; std::format_parse_context pc(s); VERIFY( pc.begin() == s.begin() ); VERIFY( pc.end() == s.end() ); pc.advance_to(s.begin() + 5); VERIFY( pc.begin() == s.begin() + 5 ); // Runtime calls to these do not check for the correct number of args. VERIFY( pc.next_arg_id() == 0 ); VERIFY( pc.next_arg_id() == 1 ); VERIFY( pc.next_arg_id() == 2 ); try { // Cannot mix manual and automatic indexing. pc.check_arg_id(0); VERIFY( false ); } catch (const std::format_error&) { } // But they do check during constant evaluation: VERIFY( ! arg_id_available ); VERIFY( ! arg_id_available ); std::format_parse_context pc2(""); pc2.check_arg_id(2); pc2.check_arg_id(1); pc2.check_arg_id(3); try { // Cannot mix manual and automatic indexing. (void) pc2.next_arg_id(); VERIFY( false ); } catch (const std::format_error&) { } } template 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 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("") ); VERIFY( is_std_format_spec_for("<") ); VERIFY( is_std_format_spec_for(">") ); VERIFY( is_std_format_spec_for("^") ); VERIFY( is_std_format_spec_for("0<") ); VERIFY( is_std_format_spec_for("0>") ); VERIFY( is_std_format_spec_for("0^") ); VERIFY( ! is_std_format_spec_for("{^") ); VERIFY( ! is_std_format_spec_for("+") ); VERIFY( ! is_std_format_spec_for("-") ); VERIFY( ! is_std_format_spec_for(" ") ); VERIFY( ! is_std_format_spec_for("#") ); VERIFY( is_std_format_spec_for("0d") ); VERIFY( ! is_std_format_spec_for("0") ); VERIFY( ! is_std_format_spec_for("00d") ); VERIFY( is_std_format_spec_for("01d") ); VERIFY( is_std_format_spec_for("0{}d") ); VERIFY( ! is_std_format_spec_for("0{1}d") ); VERIFY(( is_std_format_spec_for("0{1}d") )); VERIFY( is_std_format_spec_for("1") ); VERIFY( ! is_std_format_spec_for("-1") ); VERIFY( is_std_format_spec_for("-1d") ); // sign and width VERIFY( ! is_std_format_spec_for(".") ); VERIFY( ! is_std_format_spec_for(".1") ); VERIFY( is_std_format_spec_for("c") ); VERIFY( is_std_format_spec_for("b") ); VERIFY( is_std_format_spec_for("B") ); VERIFY( is_std_format_spec_for("d") ); VERIFY( is_std_format_spec_for("o") ); VERIFY( is_std_format_spec_for("x") ); VERIFY( is_std_format_spec_for("X") ); VERIFY( ! is_std_format_spec_for("s") ); VERIFY( is_std_format_spec_for("?") == escaped_strings_supported ); VERIFY( ! is_std_format_spec_for("a") ); VERIFY( ! is_std_format_spec_for("A") ); VERIFY( ! is_std_format_spec_for("f") ); VERIFY( ! is_std_format_spec_for("F") ); VERIFY( ! is_std_format_spec_for("g") ); VERIFY( ! is_std_format_spec_for("G") ); VERIFY( ! is_std_format_spec_for("+c") ); VERIFY( ! is_std_format_spec_for("+?") ); VERIFY( is_std_format_spec_for("+d") ); } void test_int() { VERIFY( is_std_format_spec_for("") ); VERIFY( is_std_format_spec_for("<") ); VERIFY( is_std_format_spec_for(">") ); VERIFY( is_std_format_spec_for("^") ); VERIFY( is_std_format_spec_for("0<") ); VERIFY( is_std_format_spec_for("0>") ); VERIFY( is_std_format_spec_for("0^") ); VERIFY( ! is_std_format_spec_for("{^") ); VERIFY( is_std_format_spec_for("+") ); VERIFY( is_std_format_spec_for("-") ); VERIFY( is_std_format_spec_for(" ") ); VERIFY( is_std_format_spec_for("#") ); VERIFY( is_std_format_spec_for("0d") ); VERIFY( is_std_format_spec_for("0") ); VERIFY( ! is_std_format_spec_for("00d") ); VERIFY( is_std_format_spec_for("01d") ); VERIFY( ! is_std_format_spec_for("0{1}d") ); VERIFY(( is_std_format_spec_for("0{}d") )); VERIFY(( ! is_std_format_spec_for("0{1}d") )); VERIFY(( is_std_format_spec_for("0{1}d") )); VERIFY( is_std_format_spec_for("1") ); VERIFY( is_std_format_spec_for("-1") ); // sign and width VERIFY( ! is_std_format_spec_for(".") ); VERIFY( ! is_std_format_spec_for(".1") ); VERIFY( is_std_format_spec_for("c") ); VERIFY( is_std_format_spec_for("b") ); VERIFY( is_std_format_spec_for("B") ); VERIFY( is_std_format_spec_for("d") ); VERIFY( is_std_format_spec_for("o") ); VERIFY( is_std_format_spec_for("x") ); VERIFY( is_std_format_spec_for("X") ); VERIFY( ! is_std_format_spec_for("s") ); VERIFY( ! is_std_format_spec_for("?") ); VERIFY( ! is_std_format_spec_for("a") ); VERIFY( ! is_std_format_spec_for("A") ); VERIFY( ! is_std_format_spec_for("f") ); VERIFY( ! is_std_format_spec_for("F") ); VERIFY( ! is_std_format_spec_for("g") ); VERIFY( ! is_std_format_spec_for("G") ); VERIFY( ! is_std_format_spec_for("p") ); VERIFY( ! is_std_format_spec_for("P") ); VERIFY( is_std_format_spec_for("+c") ); // But LWG 3644 would change it. VERIFY( ! is_std_format_spec_for("+?") ); VERIFY( is_std_format_spec_for("+d") ); } void test_bool() { VERIFY( is_std_format_spec_for("") ); VERIFY( is_std_format_spec_for("<") ); VERIFY( is_std_format_spec_for(">") ); VERIFY( is_std_format_spec_for("^") ); VERIFY( is_std_format_spec_for("0<") ); VERIFY( is_std_format_spec_for("0>") ); VERIFY( is_std_format_spec_for("0^") ); VERIFY( ! is_std_format_spec_for("{^") ); VERIFY( ! is_std_format_spec_for("+") ); VERIFY( ! is_std_format_spec_for("-") ); VERIFY( ! is_std_format_spec_for(" ") ); VERIFY( ! is_std_format_spec_for("#") ); VERIFY( is_std_format_spec_for("0d") ); VERIFY( ! is_std_format_spec_for("0") ); VERIFY( ! is_std_format_spec_for("00d") ); VERIFY( is_std_format_spec_for("01d") ); VERIFY( is_std_format_spec_for("1") ); VERIFY( ! is_std_format_spec_for("-1") ); VERIFY( is_std_format_spec_for("-1d") ); // sign and width VERIFY( ! is_std_format_spec_for(".") ); VERIFY( ! is_std_format_spec_for(".1") ); VERIFY( ! is_std_format_spec_for("c") ); VERIFY( is_std_format_spec_for("b") ); VERIFY( is_std_format_spec_for("B") ); VERIFY( is_std_format_spec_for("d") ); VERIFY( is_std_format_spec_for("o") ); VERIFY( is_std_format_spec_for("x") ); VERIFY( is_std_format_spec_for("X") ); VERIFY( is_std_format_spec_for("s") ); VERIFY( ! is_std_format_spec_for("?") ); VERIFY( ! is_std_format_spec_for("a") ); VERIFY( ! is_std_format_spec_for("A") ); VERIFY( ! is_std_format_spec_for("f") ); VERIFY( ! is_std_format_spec_for("F") ); VERIFY( ! is_std_format_spec_for("g") ); VERIFY( ! is_std_format_spec_for("G") ); VERIFY( ! is_std_format_spec_for("p") ); VERIFY( ! is_std_format_spec_for("P") ); VERIFY( ! is_std_format_spec_for("+s") ); VERIFY( is_std_format_spec_for("+d") ); } void test_float() { VERIFY( is_std_format_spec_for("") ); VERIFY( is_std_format_spec_for("<") ); VERIFY( is_std_format_spec_for(">") ); VERIFY( is_std_format_spec_for("^") ); VERIFY( is_std_format_spec_for("0<") ); VERIFY( is_std_format_spec_for("0>") ); VERIFY( is_std_format_spec_for("0^") ); VERIFY( ! is_std_format_spec_for("{^") ); VERIFY( is_std_format_spec_for("+") ); VERIFY( is_std_format_spec_for("-") ); VERIFY( is_std_format_spec_for(" ") ); VERIFY( is_std_format_spec_for("#") ); VERIFY( is_std_format_spec_for("0f") ); VERIFY( is_std_format_spec_for("0") ); VERIFY( ! is_std_format_spec_for("00f") ); VERIFY( is_std_format_spec_for("01f") ); VERIFY( is_std_format_spec_for("0{}f") ); VERIFY( ! is_std_format_spec_for("0{1}f") ); VERIFY( ! is_std_format_spec_for("0{1}f") ); VERIFY(( is_std_format_spec_for("0{1}f") )); VERIFY( is_std_format_spec_for("1") ); VERIFY( is_std_format_spec_for("-1") ); // sign and width VERIFY( ! is_std_format_spec_for(".") ); VERIFY( is_std_format_spec_for(".1") ); VERIFY( is_std_format_spec_for(".{}") ); VERIFY( ! is_std_format_spec_for(".{1}") ); VERIFY(( is_std_format_spec_for(".{1}") )); VERIFY( is_std_format_spec_for("{}.{}") ); VERIFY(( is_std_format_spec_for("{1}.{1}") )); VERIFY(( is_std_format_spec_for("{2}.{1}") )); VERIFY( ! is_std_format_spec_for("c") ); VERIFY( ! is_std_format_spec_for("b") ); VERIFY( ! is_std_format_spec_for("B") ); VERIFY( ! is_std_format_spec_for("d") ); VERIFY( ! is_std_format_spec_for("o") ); VERIFY( ! is_std_format_spec_for("x") ); VERIFY( ! is_std_format_spec_for("X") ); VERIFY( ! is_std_format_spec_for("s") ); VERIFY( ! is_std_format_spec_for("?") ); VERIFY( is_std_format_spec_for("a") ); VERIFY( is_std_format_spec_for("A") ); VERIFY( is_std_format_spec_for("f") ); VERIFY( is_std_format_spec_for("F") ); VERIFY( is_std_format_spec_for("g") ); VERIFY( is_std_format_spec_for("G") ); VERIFY( ! is_std_format_spec_for("p") ); VERIFY( ! is_std_format_spec_for("P") ); VERIFY( is_std_format_spec_for("+f") ); VERIFY( is_std_format_spec_for("_<+#09.6Lf") ); VERIFY( is_std_format_spec_for("<+#09.6Lf") ); VERIFY( is_std_format_spec_for("<+#9.6Lf") ); VERIFY( is_std_format_spec_for(".0006f") ); } void test_pointer() { VERIFY( is_std_format_spec_for("") ); VERIFY( is_std_format_spec_for("<") ); VERIFY( is_std_format_spec_for(">") ); VERIFY( is_std_format_spec_for("^") ); VERIFY( is_std_format_spec_for("0<") ); VERIFY( is_std_format_spec_for("0>") ); VERIFY( is_std_format_spec_for("0^") ); VERIFY( ! is_std_format_spec_for("{^") ); VERIFY( ! is_std_format_spec_for("+") ); VERIFY( ! is_std_format_spec_for("-") ); VERIFY( ! is_std_format_spec_for(" ") ); VERIFY( ! is_std_format_spec_for("#") ); VERIFY( is_std_format_spec_for("1") ); VERIFY( ! is_std_format_spec_for("-1") ); VERIFY( ! is_std_format_spec_for("-1p") ); VERIFY( ! is_std_format_spec_for(".") ); VERIFY( ! is_std_format_spec_for(".1") ); VERIFY( ! is_std_format_spec_for("c") ); VERIFY( ! is_std_format_spec_for("b") ); VERIFY( ! is_std_format_spec_for("B") ); VERIFY( ! is_std_format_spec_for("d") ); VERIFY( ! is_std_format_spec_for("o") ); VERIFY( ! is_std_format_spec_for("x") ); VERIFY( ! is_std_format_spec_for("X") ); VERIFY( ! is_std_format_spec_for("s") ); VERIFY( ! is_std_format_spec_for("?") ); VERIFY( is_std_format_spec_for("p") ); VERIFY( ! is_std_format_spec_for("a") ); VERIFY( ! is_std_format_spec_for("A") ); VERIFY( ! is_std_format_spec_for("f") ); VERIFY( ! is_std_format_spec_for("F") ); VERIFY( ! is_std_format_spec_for("g") ); VERIFY( ! is_std_format_spec_for("G") ); VERIFY( ! is_std_format_spec_for("+p") ); #if __cpp_lib_format >= 202304L // P2510R3 Formatting pointers VERIFY( is_std_format_spec_for("P") ); VERIFY( is_std_format_spec_for("0p") ); VERIFY( is_std_format_spec_for("0P") ); VERIFY( is_std_format_spec_for("0") ); VERIFY( is_std_format_spec_for("01p") ); VERIFY( ! is_std_format_spec_for("00p") ); #endif } void test_string() { VERIFY( is_std_format_spec_for("") ); VERIFY( is_std_format_spec_for("<") ); VERIFY( is_std_format_spec_for(">") ); VERIFY( is_std_format_spec_for("^") ); VERIFY( is_std_format_spec_for("0<") ); VERIFY( is_std_format_spec_for("0>") ); VERIFY( is_std_format_spec_for("0^") ); VERIFY( ! is_std_format_spec_for("{^") ); VERIFY( ! is_std_format_spec_for("+") ); VERIFY( ! is_std_format_spec_for("-") ); VERIFY( ! is_std_format_spec_for(" ") ); VERIFY( ! is_std_format_spec_for("#") ); VERIFY( ! is_std_format_spec_for("0") ); VERIFY( ! is_std_format_spec_for("01s") ); VERIFY( is_std_format_spec_for("1") ); VERIFY( ! is_std_format_spec_for("-1") ); VERIFY( ! is_std_format_spec_for("-1s") ); VERIFY( ! is_std_format_spec_for(".") ); VERIFY( is_std_format_spec_for(".1") ); VERIFY( is_std_format_spec_for(".{}") ); VERIFY(( is_std_format_spec_for(".{0}") )); VERIFY(( is_std_format_spec_for(".{1}") )); VERIFY( ! is_std_format_spec_for("c") ); VERIFY( ! is_std_format_spec_for("b") ); VERIFY( ! is_std_format_spec_for("B") ); VERIFY( ! is_std_format_spec_for("d") ); VERIFY( ! is_std_format_spec_for("o") ); VERIFY( ! is_std_format_spec_for("x") ); VERIFY( ! is_std_format_spec_for("X") ); VERIFY( is_std_format_spec_for("s") ); VERIFY( is_std_format_spec_for("?") == escaped_strings_supported ); VERIFY( ! is_std_format_spec_for("p") ); VERIFY( ! is_std_format_spec_for("P") ); VERIFY( ! is_std_format_spec_for("a") ); VERIFY( ! is_std_format_spec_for("A") ); VERIFY( ! is_std_format_spec_for("f") ); VERIFY( ! is_std_format_spec_for("F") ); VERIFY( ! is_std_format_spec_for("g") ); VERIFY( ! is_std_format_spec_for("G") ); VERIFY( is_std_format_spec_for("*^6s") ); VERIFY( is_std_format_spec_for(">6s") ); VERIFY( is_std_format_spec_for("_<6.4?") == escaped_strings_supported ); } struct S { }; template<> struct std::formatter { 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("custom") ); VERIFY( ! is_std_format_spec_for("customer") ); VERIFY( ! is_std_format_spec_for("custard") ); VERIFY( ! is_std_format_spec_for("") ); } #if __cpp_lib_format >= 202305 #include struct X { }; template<> struct std::formatter { 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) spec = spec.substr(0, p); // truncate to closing brace if (spec == "int") { pc.check_dynamic_spec_integral(pc.next_arg_id()); type = Type::integral; } else if (spec == "str") { pc.check_dynamic_spec_string(pc.next_arg_id()); type = Type::string; } else if (spec == "float") { pc.check_dynamic_spec(pc.next_arg_id()); type = Type::floating; } else if (spec == "other") type = Type::other; else throw std::format_error("invalid format-spec"); return pc.begin() + spec.size(); } std::format_context::iterator format(X, std::format_context& c) const { std::visit_format_arg([this](T) { // { dg-warning "deprecated" "" { target c++26 } } constexpr bool is_handle = std::is_same_v::handle, T>; constexpr bool is_integral = std::is_same_v || std::is_same_v || is_same_v || std::is_same_v; constexpr bool is_string = std::is_same_v || std::is_same_v; constexpr bool is_floating = std::is_same_v || std::is_same_v || std::is_same_v; switch (this->type) { case Type::other: if (is_handle) return; break; case Type::integral: if (is_integral) return; break; case Type::string: if (is_string) return; break; case Type::floating: if (is_floating) return; break; } throw std::format_error("invalid argument type"); }, c.arg(1)); return c.out(); } private: enum class Type { other, integral, string, floating, }; Type type = Type::other; }; #endif void test_dynamic_type_check() { #if __cpp_lib_format >= 202305 std::format_parse_context pc("{1}.{2}"); // None of these calls should do anything at runtime, only during consteval: pc.check_dynamic_spec(0); pc.check_dynamic_spec_integral(0); pc.check_dynamic_spec_string(0); (void) std::format("{:int}", X{}, 42L); (void) std::format("{:str}", X{}, "H2G2"); (void) std::format("{:float}", X{}, 10.0); #ifdef __STDCPP_BFLOAT16_T__ if constexpr (std::formattable) (void) std::format("{:other}", X{}, 10.0bf16); #endif #ifdef __STDCPP_FLOAT16_T__ if constexpr (std::formattable) (void) std::format("{:other}", X{}, 10.0f16); #endif #ifdef __STDCPP_FLOAT32_T__ if constexpr (std::formattable) (void) std::format("{:other}", X{}, 10.0f32); #endif #ifdef __STDCPP_FLOAT64_T__ if constexpr (std::formattable) (void) std::format("{:other}", X{}, 10.0f64); #endif #ifdef __STDCPP_FLOAT128_T__ if constexpr (std::formattable) (void) std::format("{:other}", X{}, 10.0f128); #endif #endif } int main() { test_char(); test_int(); test_bool(); test_float(); test_string(); test_pointer(); test_custom(); test_dynamic_type_check(); }