diff --git a/libcxx/test/std/time/time.syn/formatter.day.pass.cpp b/libcxx/test/std/time/time.syn/formatter.day.pass.cpp --- a/libcxx/test/std/time/time.syn/formatter.day.pass.cpp +++ b/libcxx/test/std/time/time.syn/formatter.day.pass.cpp @@ -39,60 +39,60 @@ using namespace std::literals::chrono_literals; // Valid day - check.template operator()<"{}">(SV("01"), 1d); - check.template operator()<"{:*^4}">(SV("*01*"), 1d); - check.template operator()<"{:*>3}">(SV("*01"), 1d); + check(SV("01"), SV("{}"), 1d); + check(SV("*01*"), SV("{:*^4}"), 1d); + check(SV("*01"), SV("{:*>3}"), 1d); // Invalid day - check.template operator()<"{}">(SV("00 is not a valid day"), 0d); - check.template operator()<"{:*^23}">(SV("*00 is not a valid day*"), 0d); + check(SV("00 is not a valid day"), SV("{}"), 0d); + check(SV("*00 is not a valid day*"), SV("{:*^23}"), 0d); } template static void test_valid_values() { using namespace std::literals::chrono_literals; - constexpr string_literal fmt{"{:%%d='%d'%t%%Od='%Od'%t%%e='%e'%t%%Oe='%Oe'%n}"}; - constexpr string_literal lfmt{"{:L%%d='%d'%t%%Od='%Od'%t%%e='%e'%t%%Oe='%Oe'%n}"}; + constexpr std::basic_string_view fmt = SV("{:%%d='%d'%t%%Od='%Od'%t%%e='%e'%t%%Oe='%Oe'%n}"); + constexpr std::basic_string_view lfmt = SV("{:L%%d='%d'%t%%Od='%Od'%t%%e='%e'%t%%Oe='%Oe'%n}"); const std::locale loc(LOCALE_ja_JP_UTF_8); std::locale::global(std::locale(LOCALE_fr_FR_UTF_8)); // Non localized output using C-locale - check.template operator()(SV("%d='00'\t%Od='00'\t%e=' 0'\t%Oe=' 0'\n"), 0d); - check.template operator()(SV("%d='01'\t%Od='01'\t%e=' 1'\t%Oe=' 1'\n"), 1d); - check.template operator()(SV("%d='31'\t%Od='31'\t%e='31'\t%Oe='31'\n"), 31d); + check(SV("%d='00'\t%Od='00'\t%e=' 0'\t%Oe=' 0'\n"), fmt, 0d); + check(SV("%d='01'\t%Od='01'\t%e=' 1'\t%Oe=' 1'\n"), fmt, 1d); + check(SV("%d='31'\t%Od='31'\t%e='31'\t%Oe='31'\n"), fmt, 31d); #if defined(_AIX) - check.template operator()(SV("%d='55'\t%Od='55'\t%e='55'\t%Oe='55'\n"), 255d); + check(SV("%d='55'\t%Od='55'\t%e='55'\t%Oe='55'\n"), fmt, 255d); #else - check.template operator()(SV("%d='255'\t%Od='255'\t%e='255'\t%Oe='255'\n"), 255d); + check(SV("%d='255'\t%Od='255'\t%e='255'\t%Oe='255'\n"), fmt, 255d); #endif // Use the global locale (fr_FR) - check.template operator()(SV("%d='00'\t%Od='00'\t%e=' 0'\t%Oe=' 0'\n"), 0d); - check.template operator()(SV("%d='01'\t%Od='01'\t%e=' 1'\t%Oe=' 1'\n"), 1d); - check.template operator()(SV("%d='31'\t%Od='31'\t%e='31'\t%Oe='31'\n"), 31d); + check(SV("%d='00'\t%Od='00'\t%e=' 0'\t%Oe=' 0'\n"), lfmt, 0d); + check(SV("%d='01'\t%Od='01'\t%e=' 1'\t%Oe=' 1'\n"), lfmt, 1d); + check(SV("%d='31'\t%Od='31'\t%e='31'\t%Oe='31'\n"), lfmt, 31d); #if defined(_AIX) - check.template operator()(SV("%d='55'\t%Od='55'\t%e='55'\t%Oe='55'\n"), 255d); + check(SV("%d='55'\t%Od='55'\t%e='55'\t%Oe='55'\n"), lfmt, 255d); #else - check.template operator()(SV("%d='255'\t%Od='255'\t%e='255'\t%Oe='255'\n"), 255d); + check(SV("%d='255'\t%Od='255'\t%e='255'\t%Oe='255'\n"), lfmt, 255d); #endif // Use supplied locale (ja_JP). This locale has a different alternate on some platforms. #if defined(__APPLE__) || defined(_AIX) - lcheck.template operator()(loc, SV("%d='00'\t%Od='00'\t%e=' 0'\t%Oe=' 0'\n"), 0d); - lcheck.template operator()(loc, SV("%d='01'\t%Od='01'\t%e=' 1'\t%Oe=' 1'\n"), 1d); - lcheck.template operator()(loc, SV("%d='31'\t%Od='31'\t%e='31'\t%Oe='31'\n"), 31d); + check(loc, SV("%d='00'\t%Od='00'\t%e=' 0'\t%Oe=' 0'\n"), lfmt, 0d); + check(loc, SV("%d='01'\t%Od='01'\t%e=' 1'\t%Oe=' 1'\n"), lfmt, 1d); + check(loc, SV("%d='31'\t%Od='31'\t%e='31'\t%Oe='31'\n"), lfmt, 31d); # if defined(_AIX) - check.template operator()(SV("%d='55'\t%Od='55'\t%e='55'\t%Oe='55'\n"), 255d); + check(SV("%d='55'\t%Od='55'\t%e='55'\t%Oe='55'\n"), fmt, 255d); # else - check.template operator()(SV("%d='255'\t%Od='255'\t%e='255'\t%Oe='255'\n"), 255d); + check(SV("%d='255'\t%Od='255'\t%e='255'\t%Oe='255'\n"), fmt, 255d); # endif #else // defined(__APPLE__) || defined(_AIX) - lcheck.template operator()(loc, SV("%d='00'\t%Od='〇'\t%e=' 0'\t%Oe='〇'\n"), 0d); - lcheck.template operator()(loc, SV("%d='01'\t%Od='一'\t%e=' 1'\t%Oe='一'\n"), 1d); - lcheck.template operator()(loc, SV("%d='31'\t%Od='三十一'\t%e='31'\t%Oe='三十一'\n"), 31d); - lcheck.template operator()(loc, SV("%d='255'\t%Od='255'\t%e='255'\t%Oe='255'\n"), 255d); + check(loc, SV("%d='00'\t%Od='〇'\t%e=' 0'\t%Oe='〇'\n"), lfmt, 0d); + check(loc, SV("%d='01'\t%Od='一'\t%e=' 1'\t%Oe='一'\n"), lfmt, 1d); + check(loc, SV("%d='31'\t%Od='三十一'\t%e='31'\t%Oe='三十一'\n"), lfmt, 31d); + check(loc, SV("%d='255'\t%Od='255'\t%e='255'\t%Oe='255'\n"), lfmt, 255d); #endif // defined(__APPLE__) || defined(_AIX) std::locale::global(std::locale::classic()); diff --git a/libcxx/test/std/time/time.syn/formatter_tests.h b/libcxx/test/std/time/time.syn/formatter_tests.h --- a/libcxx/test/std/time/time.syn/formatter_tests.h +++ b/libcxx/test/std/time/time.syn/formatter_tests.h @@ -10,6 +10,8 @@ #include "make_string.h" #include "string_literal.h" +#include "test_format_string.h" +#include "test_macros.h" #include #include @@ -28,50 +30,52 @@ using format_context = std::format_context; #endif -inline constexpr auto check = []( - std::basic_string_view expected, const Args&... args) constexpr { - std::basic_string out = std::format(fmt.template sv(), args...); +template +void check(std::basic_string_view expected, test_format_string fmt, Args&&... args) { + std::basic_string out = std::format(fmt, std::forward(args)...); if constexpr (std::same_as) if (out != expected) - std::cerr << "\nFormat string " << fmt.template sv() << "\nExpected output " << expected - << "\nActual output " << out << '\n'; + std::cerr << "\nFormat string " << fmt.get() << "\nExpected output " << expected << "\nActual output " << out + << '\n'; assert(out == expected); -}; +} -inline constexpr auto lcheck = []( - const std::locale& loc, std::basic_string_view expected, const Args&... args) constexpr { - std::basic_string out = std::format(loc, fmt.template sv(), args...); +template +void check(const std::locale& loc, + std::basic_string_view expected, + test_format_string fmt, + Args&&... args) { + std::basic_string out = std::format(loc, fmt, std::forward(args)...); if constexpr (std::same_as) if (out != expected) - std::cerr << "\nFormat string " << fmt.template sv() << "\nExpected output " << expected - << "\nActual output " << out << '\n'; + std::cerr << "\nFormat string " << fmt.get() << "\nExpected output " << expected << "\nActual output " << out + << '\n'; assert(out == expected); -}; +} -inline constexpr auto check_exception = - []( - [[maybe_unused]] std::string_view what, - [[maybe_unused]] std::basic_string_view fmt, - [[maybe_unused]] const Args&... args) { +template +void check_exception([[maybe_unused]] std::string_view what, + [[maybe_unused]] std::basic_string_view fmt, + [[maybe_unused]] const Args&... args) { #ifndef TEST_HAS_NO_EXCEPTIONS - try { - TEST_IGNORE_NODISCARD std::vformat(fmt, std::make_format_args>(args...)); - if constexpr (std::same_as) - std::cerr << "\nFormat string " << fmt << "\nDidn't throw an exception.\n"; - assert(false); - } catch (const std::format_error& e) { + try { + TEST_IGNORE_NODISCARD std::vformat(fmt, std::make_format_args>(args...)); + if constexpr (std::same_as) + std::cerr << "\nFormat string " << fmt << "\nDidn't throw an exception.\n"; + assert(false); + } catch (const std::format_error& e) { # if defined(_LIBCPP_VERSION) - if constexpr (std::same_as) - if (e.what() != what) - std::cerr << "\nFormat string " << fmt << "\nExpected exception " << what << "\nActual exception " - << e.what() << '\n'; - assert(e.what() == what); + if constexpr (std::same_as) + if (e.what() != what) + std::cerr << "\nFormat string " << fmt << "\nExpected exception " << what << "\nActual exception " + << e.what() << '\n'; + assert(e.what() == what); # endif - return; - } - assert(false); + return; + } + assert(false); #endif - }; +} template void check_invalid_type(const std::set>& valid_types, diff --git a/libcxx/test/support/test_format_string.h b/libcxx/test/support/test_format_string.h new file mode 100644 --- /dev/null +++ b/libcxx/test/support/test_format_string.h @@ -0,0 +1,48 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef SUPPORT_TEST_FORMAT_STRING_HPP +#define SUPPORT_TEST_FORMAT_STRING_HPP + +#include +#include +#include + +#include "test_macros.h" + +#if TEST_STD_VER < 20 +# error "The format header requires at least C++20" +#endif + +// Wrapper for basic_format_string. +// +// This layer of indirection is used since it's not possible to use +// std::basic_format_string in the test function directly. +// +// In C++20 the basic-format-string was an exposition only type. In C++23 is +// has been replaced with basic_format_string. Both libc++ and MSVC STL offer +// it as an extension in C++20. +#if TEST_STD_VER > 20 || defined(_LIBCPP_VERSION) || defined(_MSVC_STL_VERSION) +# ifndef TEST_HAS_NO_WIDE_CHARACTERS +template +using test_format_string = + std::conditional_t, std::format_string, std::wformat_string>; +# else +template +using test_format_string = std::format_string; +# endif + +#else // TEST_STD_VER > 20 || defined(_LIBCPP_VERSION) || defined( _MSVC_STL_VERSION) + +# error +"Please create a vendor specific version of the test typedef and file a review at https://reviews.llvm.org/" + +#endif // TEST_STD_VER > 20 || defined(_LIBCPP_VERSION) || defined( _MSVC_STL_VERSION) + +#endif // SUPPORT_TEST_FORMAT_STRING_HPP