| // { dg-do run { target c++20 } } | |
| // { dg-warning "deprecated" "std::visit_format_arg" { target c++26 } 0 } | |
| #include <format> | |
| #include <testsuite_hooks.h> | |
| // LWG 4106. basic_format_args should not be default-constructible | |
| static_assert( ! std::is_default_constructible_v<std::format_args> ); | |
| static_assert( ! std::is_default_constructible_v<std::wformat_args> ); | |
| 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() | |
| { | |
| bool b = false; | |
| int i = 1; | |
| char c = '2'; | |
| double d = 3.4; | |
| // We need an lvalue of type format-arg-store<Context, Args...> for the | |
| // lvalue args to point to, otherwise it will have a dangling pointer. | |
| // This is not how you're supposed to use format args, just for testing. | |
| auto store = std::make_format_args(b, i, c, d); | |
| 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)); | |
| long l = 5L; | |
| unsigned long long ull = 6ULL; | |
| float f = 7.8f; | |
| auto cstore = std::make_format_args<std::format_context>(l, ull, f); | |
| 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)); | |
| std::string s = "tenfour"; | |
| VERIFY(equals(std::format_args(std::make_format_args(s)).get(0), std::string_view("tenfour"))); | |
| char nine = '9'; | |
| wchar_t ten = L'X'; | |
| // This needs to be on the stack so that testing pointer equality works. | |
| wchar_t eleven[] = L"eleven"; | |
| long double twelve13 = 12.13L; | |
| std::wstring tenfour = L"tenfour"; | |
| auto wstore = std::make_wformat_args(nine, ten, eleven, twelve13, 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)); | |
| std::nullptr_t null; | |
| E eebygum = E::ByGum; | |
| auto another_store = std::make_format_args(null, eebygum); | |
| 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))); | |
| } | |
| void | |
| test_member_visit() | |
| { | |
| #if __cpp_lib_format >= 202306L // C++26 adds std::basic_format_arg::visit | |
| int one = 1; | |
| float two = 2.0; | |
| std::string_view three = "three"; | |
| auto store = std::make_format_args(one, two, three); // See comment above. | |
| std::format_args args = store; | |
| auto a1 = args.get(0).visit([=](auto a) { | |
| if constexpr (std::is_same_v<decltype(a), int>) | |
| return a == one; | |
| return false; | |
| }); | |
| VERIFY( a1 ); | |
| auto a2 = args.get(1).visit([=](auto a) { | |
| if constexpr (std::is_same_v<decltype(a), float>) | |
| return a == two; | |
| return false; | |
| }); | |
| VERIFY( a2 ); | |
| auto a3 = args.get(2).visit([=](auto a) { | |
| if constexpr (std::is_same_v<decltype(a), std::string_view>) | |
| return a == three; | |
| return false; | |
| }); | |
| VERIFY( a3 ); | |
| auto a4 = args.get(0).visit<std::string_view>([](auto) { return "str"; }); | |
| static_assert( std::is_same_v<decltype(a4), std::string_view> ); | |
| VERIFY( a4 == "str" ); | |
| args.get(0).visit<void>([](auto){}); | |
| using V = decltype(args.get(0).visit<void>([](auto){})); | |
| static_assert( std::is_same_v<V, void> ); | |
| #endif | |
| } | |
| template<typename T> | |
| void test_visited_as_handle() | |
| { | |
| T v{}; | |
| auto store = std::make_format_args(v); | |
| std::format_args args = store; | |
| constexpr auto is_handle = [](auto arg) { | |
| return std::is_same_v<decltype(arg), decltype(args.get(0))::handle>; | |
| }; | |
| VERIFY( std::visit_format_arg(is_handle, args.get(0)) ); | |
| #if __cpp_lib_format >= 202306L // C++26 adds std::basic_format_arg::visit | |
| VERIFY( args.get(0).visit(is_handle) ); | |
| #endif | |
| } | |
| template<typename T> | |
| concept can_format = std::is_default_constructible_v<std::formatter<T, char>>; | |
| int main() | |
| { | |
| test_empty(); | |
| test_args(); | |
| test_member_visit(); | |
| #ifdef __SIZEOF_INT128__ | |
| test_visited_as_handle<__int128>(); | |
| test_visited_as_handle<unsigned __int128>(); | |
| #endif | |
| #ifdef __BFLT16_DIG__ | |
| if constexpr (can_format<__gnu_cxx::__bfloat16_t>) | |
| test_visited_as_handle<__gnu_cxx::__bfloat16_t>(); | |
| #endif | |
| #ifdef __FLT16_DIG__ | |
| if constexpr (can_format<_Float16>) | |
| test_visited_as_handle<_Float16>(); | |
| #endif | |
| #ifdef __FLT32_DIG__ | |
| if constexpr (can_format<_Float32>) | |
| test_visited_as_handle<_Float32>(); | |
| #endif | |
| #ifdef __FLT64_DIG__ | |
| if constexpr (can_format<_Float64>) | |
| test_visited_as_handle<_Float64>(); | |
| #endif | |
| #ifdef __FLT128_DIG__ | |
| if constexpr (can_format<_Float128>) | |
| test_visited_as_handle<_Float128>(); | |
| #endif | |
| #ifdef __SIZEOF_FLOAT128__ | |
| // __ieee128 is same type as __float128, and may be long double | |
| if constexpr (!std::is_same_v<__float128, long double>) | |
| if constexpr (can_format<__float128>) | |
| test_visited_as_handle<__float128>(); | |
| #endif | |
| #ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT | |
| if constexpr (!std::is_same_v<__ieee128, long double>) | |
| test_visited_as_handle<__ieee128>(); | |
| if constexpr (!std::is_same_v<__ibm128, long double>) | |
| test_visited_as_handle<__ibm128>(); | |
| #endif | |
| } |