diff options
author | Tomasz Kamiński <tkaminsk@redhat.com> | 2025-03-10 16:51:57 +0100 |
---|---|---|
committer | Tomasz Kamiński <tkaminsk@redhat.com> | 2025-03-13 10:57:35 +0100 |
commit | 22847ef193670e761ed205a4a6f0a694b939d4e4 (patch) | |
tree | 57a658f338af4b527beceafbe634f51e287c927e /libstdc++-v3 | |
parent | 77ef91d7159613c0cfc2920ddd5a32952c61ff5b (diff) | |
download | gcc-22847ef193670e761ed205a4a6f0a694b939d4e4.zip gcc-22847ef193670e761ed205a4a6f0a694b939d4e4.tar.gz gcc-22847ef193670e761ed205a4a6f0a694b939d4e4.tar.bz2 |
libstdc++: Hide 128-bit int and float types behind handle for basic_format_arg visitation [PR108053]
Implement visit_format_arg and basic_format_arg::visit function,
in terms of _M_visit_user member functions, that wraps any type
stored inside basic_format_arg, that is not specified in the standard,
into the handle. This affects __int128, unsigned __int128,
PowerPC specific __ieee128 and __ibm128, and _Float128 for architectures
where long double is not 128bits.
The bfloat16, _Float16, _Float32, _Float32, and _Float128 for
128bits long double are not are not addressed, as they
are transformed into a standard floating point types.
For internal purposes __format::__visit_format_arg function is
used, that provides an unmodified access to stored object.
PR libstdc++/108053
libstdc++-v3/ChangeLog:
* include/std/format (basic_format_arg::_M_visit_user):
Helper function for wrapping extension types into handle.
(visit_format_arg): Call `_M_visit_user` instead of `_M_visit`.
(basic_format_arg::visit): As above.
(__format::__visit_format_arg): Provides direct access to
values stored in basic_format_arg.
(__format::__int_from_arg): Use __format::__visit_format_arg
instead of std::visit_format_arg.
(_Formatting_scanner::_M_format_arg): As above.
(_Checking_scanner::__do_vformat_to): As above.
* testsuite/std/format/arguments/args.cc: New tests.
* testsuite/std/format/string.cc: Test for using __int128
as width/precision.
Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
Diffstat (limited to 'libstdc++-v3')
-rw-r--r-- | libstdc++-v3/include/std/format | 49 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/std/format/arguments/args.cc | 73 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/std/format/string.cc | 10 |
3 files changed, 123 insertions, 9 deletions
diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format index f52645a..1b38913 100644 --- a/libstdc++-v3/include/std/format +++ b/libstdc++-v3/include/std/format @@ -3308,10 +3308,12 @@ namespace __format template<typename _Context, typename... _Args> class _Arg_store; + template<typename _Visitor, typename _Ctx> + decltype(auto) __visit_format_arg(_Visitor&&, basic_format_arg<_Ctx>); + template<typename _Ch, typename _Tp> consteval _Arg_t __to_arg_t_enum() noexcept; - } // namespace __format /// @endcond @@ -3383,12 +3385,12 @@ namespace __format template<typename _Visitor> decltype(auto) visit(this basic_format_arg __arg, _Visitor&& __vis) - { return __arg._M_visit(std::forward<_Visitor>(__vis), __arg._M_type); } + { return __arg._M_visit_user(std::forward<_Visitor>(__vis), __arg._M_type); } template<typename _Res, typename _Visitor> _Res visit(this basic_format_arg __arg, _Visitor&& __vis) - { return __arg._M_visit(std::forward<_Visitor>(__vis), __arg._M_type); } + { return __arg._M_visit_user(std::forward<_Visitor>(__vis), __arg._M_type); } #endif private: @@ -3589,6 +3591,10 @@ namespace __format friend decltype(auto) visit_format_arg(_Visitor&& __vis, basic_format_arg<_Ctx>); + template<typename _Visitor, typename _Ctx> + friend decltype(auto) + __format::__visit_format_arg(_Visitor&&, basic_format_arg<_Ctx>); + template<typename _Ch, typename _Tp> friend consteval __format::_Arg_t __format::__to_arg_t_enum() noexcept; @@ -3657,6 +3663,28 @@ namespace __format __builtin_unreachable(); } } + + template<typename _Visitor> + decltype(auto) + _M_visit_user(_Visitor&& __vis, __format::_Arg_t __type) + { + return _M_visit([&__vis]<typename _Tp>(_Tp& __val) -> decltype(auto) + { + constexpr bool __user_facing = __is_one_of<_Tp, + monostate, bool, _CharT, + int, unsigned int, long long int, unsigned long long int, + float, double, long double, + const _CharT*, basic_string_view<_CharT>, + const void*, handle>::value; + if constexpr (__user_facing) + return std::forward<_Visitor>(__vis)(__val); + else + { + handle __h(__val); + return std::forward<_Visitor>(__vis)(__h); + } + }, __type); + } }; template<typename _Visitor, typename _Context> @@ -3664,12 +3692,19 @@ namespace __format inline decltype(auto) visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg) { - return __arg._M_visit(std::forward<_Visitor>(__vis), __arg._M_type); + return __arg._M_visit_user(std::forward<_Visitor>(__vis), __arg._M_type); } /// @cond undocumented namespace __format { + template<typename _Visitor, typename _Ctx> + inline decltype(auto) + __visit_format_arg(_Visitor&& __vis, basic_format_arg<_Ctx> __arg) + { + return __arg._M_visit(std::forward<_Visitor>(__vis), __arg._M_type); + } + struct _WidthPrecVisitor { template<typename _Tp> @@ -3701,7 +3736,7 @@ namespace __format template<typename _Context> inline size_t __int_from_arg(const basic_format_arg<_Context>& __arg) - { return std::visit_format_arg(_WidthPrecVisitor(), __arg); } + { return __format::__visit_format_arg(_WidthPrecVisitor(), __arg); } // Pack _Arg_t enum values into a single 60-bit integer. template<int _Bits, size_t _Nm> @@ -4154,7 +4189,7 @@ namespace __format using _Context = basic_format_context<_Out, _CharT>; using handle = typename basic_format_arg<_Context>::handle; - std::visit_format_arg([this](auto& __arg) { + __format::__visit_format_arg([this](auto& __arg) { using _Type = remove_reference_t<decltype(__arg)>; using _Formatter = typename _Context::template formatter_type<_Type>; if constexpr (is_same_v<_Type, monostate>) @@ -4256,7 +4291,7 @@ namespace __format if (__fmt.size() == 2 && __fmt[0] == '{' && __fmt[1] == '}') { bool __done = false; - std::visit_format_arg([&](auto& __arg) { + __format::__visit_format_arg([&](auto& __arg) { using _Tp = remove_cvref_t<decltype(__arg)>; if constexpr (is_same_v<_Tp, bool>) { diff --git a/libstdc++-v3/testsuite/std/format/arguments/args.cc b/libstdc++-v3/testsuite/std/format/arguments/args.cc index 2cea0a1..4c50bc7 100644 --- a/libstdc++-v3/testsuite/std/format/arguments/args.cc +++ b/libstdc++-v3/testsuite/std/format/arguments/args.cc @@ -148,9 +148,82 @@ test_member_visit() #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 E, typename S> +void test_visited_as() +{ + auto v = static_cast<S>(1.0); + auto store = std::make_format_args(v); + std::format_args args = store; + + auto is_expected_val = [v](auto arg) { + if constexpr (std::is_same_v<decltype(arg), E>) + return arg == static_cast<E>(v); + return false; + }; + VERIFY( std::visit_format_arg(is_expected_val, args.get(0)) ); +#if __cpp_lib_format >= 202306L // C++26 adds std::basic_format_arg::visit + VERIFY( args.get(0).visit(is_expected_val) ); +#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 +// TODO: This should be visited as handle. +#ifdef __STDCPP_FLOAT16_T__ + if constexpr (can_format<_Float16>) + test_visited_as<float, _Float16>(); +#endif +#ifdef __STDCPP_BFLOAT16_T__ + if constexpr (can_format<__gnu_cxx::__bfloat16_t>) + test_visited_as<float, __gnu_cxx::__bfloat16_t>(); +#endif +#ifdef __FLT32_DIG__ + if constexpr (can_format<_Float32>) + test_visited_as<float, _Float32>(); +#endif +#ifdef __FLT64_DIG__ + if constexpr (can_format<_Float64>) + test_visited_as<double, _Float64>(); +#endif +#ifdef __FLT128_DIG__ + if constexpr (can_format<_Float128>) +# ifdef _GLIBCXX_LDOUBLE_IS_IEEE_BINARY128 + test_visited_as<long double, _Float128>(); +# else + test_visited_as_handle<_Float128>(); +# endif +#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 } diff --git a/libstdc++-v3/testsuite/std/format/string.cc b/libstdc++-v3/testsuite/std/format/string.cc index d5f3050..ee987a1 100644 --- a/libstdc++-v3/testsuite/std/format/string.cc +++ b/libstdc++-v3/testsuite/std/format/string.cc @@ -113,11 +113,14 @@ test_format_spec() VERIFY( ! is_format_string_for("{:0c}", 'c') ); VERIFY( ! is_format_string_for("{:0s}", true) ); - // Dynamic width arg must be an integer type. + // Dynamic width arg must be a standar integer type. VERIFY( ! is_format_string_for("{:{}d}", 1, 1.5) ); VERIFY( ! is_format_string_for("{:{}d}", 1, true) ); VERIFY( ! is_format_string_for("{:{}d}", 1, "str") ); VERIFY( ! is_format_string_for("{:{}d}", 1, nullptr) ); +#ifdef __SIZEOF_INT128__ + VERIFY( ! is_format_string_for("{:{}d}", 1, static_cast<__int128>(1)) ); +#endif // Precision only valid for string and floating-point types. VERIFY( ! is_format_string_for("{:.3d}", 1) ); @@ -126,11 +129,14 @@ test_format_spec() VERIFY( ! is_format_string_for("{:3.3s}", 'c') ); VERIFY( ! is_format_string_for("{:3.3p}", nullptr) ); - // Dynamic precision arg must be an integer type. + // Dynamic precision arg must be a standard integer type. VERIFY( ! is_format_string_for("{:.{}f}", 1.0, 1.5) ); VERIFY( ! is_format_string_for("{:.{}f}", 1.0, true) ); VERIFY( ! is_format_string_for("{:.{}f}", 1.0, "str") ); VERIFY( ! is_format_string_for("{:.{}f}", 1.0, nullptr) ); +#ifdef __SIZEOF_INT128__ + VERIFY( ! is_format_string_for("{:{}f}", 1.0, static_cast<unsigned __int128>(1)) ); +#endif // Invalid presentation types for integers. VERIFY( ! is_format_string_for("{:f}", 1) ); |