Hello,The attached patch implements the tuple protocol for std::complex (added by P2819R2 for C++26).
Tested on x86-64 Linux. Beware that you need the latest GCC trunk, otherwise you'll get an ICE (PR 119045).
It's also on Forge here https://forge.sourceware.org/gcc/gcc-TEST/pulls/34together with a workaround for the ICE (please ignore that, the GCC mirror hasn't synced the proper fix just yet.)
Thank you, -- Giuseppe D'Angelo
From d2df3bf77ff559a602aafbe7379b293a203cf104 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo <giuseppe.dang...@kdab.com> Date: Thu, 27 Feb 2025 22:47:27 +0100 Subject: [PATCH] libstdc++: implement tuple protocol for std::complex (P2819R2) This commit implements P2819R2 for C++26, making std::complex destructurable and tuple-like (see [complex.tuple]). std::get needs to get forward declared in stl_pair.h (following the existing precedent for the implementation of P2165R4, cf. r14-8710-g65b4cba9d6a9ff), and implemented in <complex>. Also, std::get(complex<T>) needs to return *references* to the real and imaginary parts of a std::complex object, honoring the value category and constness of the argument. In principle a straightforward task, it gets a bit convoluted by the fact that: 1) std::complex does not have existing getters that one can use for this (real() and imag() return values, not references); 2) there are specializations for language/extended floating-point types, which requires some duplication -- need to amend the primary and all the specializations; 3) these specializations use a `__complex__ T`, but the primary template uses two non-static data members, making generic code harder to write. The implementation choice used here is to add the overloads of std::get for complex as declared in [complex.tuple]. In turn they dispatch to newly added getters that extract references to the real/imaginary parts of a complex<T>. These getters are private API and the implementation depends on whether it's the primary (bind the data member) or a specialization (use the GCC language extensions for __complex__). Add a test that covers the aspects of the tuple protocol, as well as the tuple-like interface. While at it, add a test for the existing tuple-like feature-testing macro. libstdc++-v3/ChangeLog: * include/bits/stl_pair.h (get): Forward-declare std::get for std::complex. * include/bits/version.def (tuple_like): Bump the value of the feature-testing macro in C++26. * include/bits/version.h: Regenerate. * include/std/complex: Implement the tuple protocol for std::complex. (tuple_size): Specialize for std::complex. (tuple_element): Ditto. (__is_tuple_like_v): Ditto. (complex): Add getters to obtain references to the real and imaginary part, on the primary class template and on its specializations. (get): Add the overload of std::get for std::complex. * testsuite/20_util/tuple/tuple_like_ftm.cc: New test. * testsuite/26_numerics/complex/tuple_like.cc: New test. Signed-off-by: Giuseppe D'Angelo <giuseppe.dang...@kdab.com> --- libstdc++-v3/include/bits/stl_pair.h | 18 ++ libstdc++-v3/include/bits/version.def | 4 + libstdc++-v3/include/bits/version.h | 7 +- libstdc++-v3/include/std/complex | 88 +++++++++ .../testsuite/20_util/tuple/tuple_like_ftm.cc | 17 ++ .../26_numerics/complex/tuple_like.cc | 179 ++++++++++++++++++ 6 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 libstdc++-v3/testsuite/20_util/tuple/tuple_like_ftm.cc create mode 100644 libstdc++-v3/testsuite/26_numerics/complex/tuple_like.cc diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h index c57e3c09765..8c57712b461 100644 --- a/libstdc++-v3/include/bits/stl_pair.h +++ b/libstdc++-v3/include/bits/stl_pair.h @@ -101,6 +101,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<size_t...> struct _Index_tuple; + template<typename _Tp> + class complex; + template<size_t _Int, class _Tp1, class _Tp2> constexpr typename tuple_element<_Int, pair<_Tp1, _Tp2>>::type& get(pair<_Tp1, _Tp2>& __in) noexcept; @@ -149,6 +152,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION constexpr const _Tp&& get(const array<_Tp, _Nm>&&) noexcept; +#if __glibcxx_tuple_like >= 202311 // >= C++26 + template<size_t _Int, typename _Tp> + constexpr _Tp& + get(complex<_Tp>&) noexcept; + template<size_t _Int, typename _Tp> + constexpr _Tp&& + get(complex<_Tp>&&) noexcept; + template<size_t _Int, typename _Tp> + constexpr const _Tp& + get(const complex<_Tp>&) noexcept; + template<size_t _Int, typename _Tp> + constexpr const _Tp&& + get(const complex<_Tp>&&) noexcept; +#endif + #if ! __cpp_lib_concepts // Concept utility functions, reused in conditionally-explicit // constructors. diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 665b92acae5..af28c2013c5 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1792,6 +1792,10 @@ ftms = { ftms = { name = tuple_like; + values = { + v = 202311; + cxxmin = 26; + }; values = { v = 202207; cxxmin = 23; diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index b47b75a1ca9..832929baae4 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -1981,7 +1981,12 @@ #undef __glibcxx_want_to_underlying #if !defined(__cpp_lib_tuple_like) -# if (__cplusplus >= 202100L) +# if (__cplusplus > 202302L) +# define __glibcxx_tuple_like 202311L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_tuple_like) +# define __cpp_lib_tuple_like 202311L +# endif +# elif (__cplusplus >= 202100L) # define __glibcxx_tuple_like 202207L # if defined(__glibcxx_want_all) || defined(__glibcxx_want_tuple_like) # define __cpp_lib_tuple_like 202207L diff --git a/libstdc++-v3/include/std/complex b/libstdc++-v3/include/std/complex index 289a144cd7e..c2cb2281300 100644 --- a/libstdc++-v3/include/std/complex +++ b/libstdc++-v3/include/std/complex @@ -59,8 +59,14 @@ #define __glibcxx_want_constexpr_complex #define __glibcxx_want_complex_udls +#define __glibcxx_want_tuple_like #include <bits/version.h> +#if __glibcxx_tuple_like >= 202311 // >= C++26 +# include <bits/utility.h> // for tuple_element_t +# include <bits/stl_pair.h> // for __is_tuple_like_v +#endif + namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION @@ -123,6 +129,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// Return complex hyperbolic tangent of @a z. template<typename _Tp> complex<_Tp> tanh(const complex<_Tp>&); +#if __glibcxx_tuple_like >= 202311L // >= C++26 + template<typename _Tp> + struct tuple_size<complex<_Tp>> + : public integral_constant<size_t, 2> { }; + template<typename _Tp> + struct tuple_element<0, complex<_Tp>> + { using type = _Tp; }; + template<typename _Tp> + struct tuple_element<1, complex<_Tp>> + { using type = _Tp; }; + template<typename _Tp> + inline constexpr bool __is_tuple_like_v<complex<_Tp>> = true; +#endif // __glibcxx_tuple_like >= 202311 // 26.2.2 Primary template class complex /** @@ -244,6 +263,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _GLIBCXX_CONSTEXPR complex __rep() const { return *this; } +#if __glibcxx_tuple_like >= 202311L // >= C++26 + constexpr _Tp& __real_part() & { return _M_real; } + constexpr const _Tp& __real_part() const & { return _M_real; } + constexpr _Tp& __imag_part() & { return _M_imag; } + constexpr const _Tp& __imag_part() const & { return _M_imag; } +#endif + private: _Tp _M_real; _Tp _M_imag; @@ -608,6 +634,48 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return __z.imag(); } #endif +#if __glibcxx_tuple_like >= 202311L // >= C++26 + template<size_t _Int, typename _Tp> + [[nodiscard]] + constexpr _Tp& + get(complex<_Tp>& __z) noexcept + { + static_assert(_Int < 2); + if constexpr (_Int == 0) + return __z.__real_part(); + else + return __z.__imag_part(); + } + + template<size_t _Int, typename _Tp> + [[nodiscard]] + constexpr _Tp&& + get(complex<_Tp>&& __z) noexcept + { + return std::move(get<_Int>(__z)); + } + + template<size_t _Int, typename _Tp> + [[nodiscard]] + constexpr const _Tp& + get(const complex<_Tp>& __z) noexcept + { + static_assert(_Int < 2); + if constexpr (_Int == 0) + return __z.__real_part(); + else + return __z.__imag_part(); + } + + template<size_t _Int, typename _Tp> + [[nodiscard]] + constexpr const _Tp&& + get(const complex<_Tp>&& __z) noexcept + { + return std::move(get<_Int>(__z)); + } +#endif // __glibcxx_tuple_like >= 202311 + #if _GLIBCXX_USE_C99_COMPLEX #if defined(__STDCPP_FLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) inline _Float16 @@ -1348,6 +1416,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : std::pow(complex<_Tp>(__x), __y); } +#if __glibcxx_tuple_like >= 202311L // >= C++26 +#define _GLIBCXX26_COMPLEX_TUPLE_HELPER_ACCESSORS(_Tp) \ + constexpr _Tp& __real_part() & { return __real__ _M_value; } \ + constexpr const _Tp& __real_part() const & { return __real__ _M_value; } \ + constexpr _Tp& __imag_part() & { return __imag__ _M_value; } \ + constexpr const _Tp& __imag_part() const & { return __imag__ _M_value; } +#else +#define _GLIBCXX26_COMPLEX_TUPLE_HELPER_ACCESSORS(_Tp) +#endif + /// 26.2.3 complex specializations /// complex<float> specialization template<> @@ -1501,6 +1579,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _GLIBCXX_CONSTEXPR _ComplexT __rep() const { return _M_value; } + _GLIBCXX26_COMPLEX_TUPLE_HELPER_ACCESSORS(float) + private: _ComplexT _M_value; }; @@ -1658,6 +1738,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _GLIBCXX_CONSTEXPR _ComplexT __rep() const { return _M_value; } + _GLIBCXX26_COMPLEX_TUPLE_HELPER_ACCESSORS(double) + private: _ComplexT _M_value; }; @@ -1817,6 +1899,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _GLIBCXX_CONSTEXPR _ComplexT __rep() const { return _M_value; } + _GLIBCXX26_COMPLEX_TUPLE_HELPER_ACCESSORS(long double) + private: _ComplexT _M_value; }; @@ -1971,11 +2055,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION constexpr _ComplexT __rep() const { return _M_value; } + _GLIBCXX26_COMPLEX_TUPLE_HELPER_ACCESSORS(_Tp) + private: _ComplexT _M_value; }; #endif +#undef _GLIBCXX26_COMPLEX_TUPLE_HELPER_ACCESSORS + #if __cplusplus <= 202002L // These bits have to be at the end of this file, so that the // specializations have all been defined. diff --git a/libstdc++-v3/testsuite/20_util/tuple/tuple_like_ftm.cc b/libstdc++-v3/testsuite/20_util/tuple/tuple_like_ftm.cc new file mode 100644 index 00000000000..d7399b506ea --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/tuple/tuple_like_ftm.cc @@ -0,0 +1,17 @@ +// { dg-do preprocess { target c++23 } } +// { dg-add-options no_pch } + +#include <utility> + +#if !defined(__cpp_lib_tuple_like) +# error "Feature-test macro for tuple-like is missing" +#elif __cplusplus > 202302L +# if __cpp_lib_tuple_like < 202311L +# error "Feature-test macro for tuple-like has wrong value" +# endif +#else +# if __cpp_lib_tuple_like < 202207L +# error "Feature-test macro for tuple-like has wrong value" +# endif +#endif + diff --git a/libstdc++-v3/testsuite/26_numerics/complex/tuple_like.cc b/libstdc++-v3/testsuite/26_numerics/complex/tuple_like.cc new file mode 100644 index 00000000000..7d8d2ee99ce --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/complex/tuple_like.cc @@ -0,0 +1,179 @@ +// { dg-do compile { target c++26 } } + +#include <complex> +#include <ranges> +#include <string> +#include <type_traits> +#include <tuple> +#include <utility> + +#include <testsuite_hooks.h> + +template <typename T> +constexpr +void +test_sanity() +{ + using C = std::complex<T>; + + static_assert(std::tuple_size_v<C> == 2); + static_assert(std::is_same_v<std::tuple_element_t<0, C>, T>); + static_assert(std::is_same_v<std::tuple_element_t<1, C>, T>); + + static_assert(std::is_same_v<decltype(get<0>(std::declval<C&>())), T&>); + static_assert(std::is_same_v<decltype(get<1>(std::declval<C&>())), T&>); + static_assert(std::is_same_v<decltype(get<0>(std::declval<const C&>())), const T&>); + static_assert(std::is_same_v<decltype(get<1>(std::declval<const C&>())), const T&>); + static_assert(std::is_same_v<decltype(get<0>(std::declval<C>())), T&&>); + static_assert(std::is_same_v<decltype(get<1>(std::declval<C>())), T&&>); + static_assert(std::is_same_v<decltype(get<0>(std::declval<const C>())), const T&&>); + static_assert(std::is_same_v<decltype(get<1>(std::declval<const C>())), const T&&>); +} + +template <typename T> +constexpr +void +test_get() +{ + using C = std::complex<T>; + + C cpx(T(1), T(2)); + VERIFY(std::get<0>(cpx) == T(1)); + VERIFY(std::get<1>(cpx) == T(2)); + + const C cpx2(T(3), T(4)); + VERIFY(std::get<0>(cpx2) == T(3)); + VERIFY(std::get<1>(cpx2) == T(4)); + + struct derived : public C { using C::C; }; + derived cpx3(T(5), T(6)); + VERIFY(std::get<0>(cpx3) == T(5)); + VERIFY(std::get<1>(cpx3) == T(6)); +} + +template <typename T> +constexpr +void +test_structured_binding() +{ + using C = std::complex<T>; + C cpx(T(1), T(2)); + + auto& [r, i] = cpx; + VERIFY(r == T(1)); + VERIFY(i == T(2)); + + r = T(3); + VERIFY(cpx.real() == T(3)); + VERIFY(cpx.imag() == T(2)); + + i = T(4); + VERIFY(cpx.real() == T(3)); + VERIFY(cpx.imag() == T(4)); + + const C cpx2(T(5), T(6)); + auto& [r2, i2] = cpx2; + VERIFY(r2 == T(5)); + VERIFY(i2 == T(6)); +} + +template <typename T> +constexpr +void +test_tuple_cat() +{ + std::complex<T> cpx(T(1), T(2)); + std::pair<int, std::string> p(42, "hello"); + + auto r = std::tuple_cat(cpx, p, cpx); + static_assert(std::is_same_v<decltype(r), std::tuple<T, T, int, std::string, T, T>>); + VERIFY(std::get<0>(r) == T(1)); + VERIFY(std::get<1>(r) == T(2)); + VERIFY(std::get<2>(r) == 42); + VERIFY(std::get<3>(r) == "hello"); + VERIFY(std::get<4>(r) == T(1)); + VERIFY(std::get<5>(r) == T(2)); +} + +template <typename T> +constexpr +void +test_element_view() +{ + std::complex<T> array[5] = { + { T(0), T(1) }, + { T(2), T(3) }, + { T(4), T(5) }, + { T(6), T(7) }, + { T(8), T(9) } + }; + + T real_reduction = std::ranges::fold_left(array | std::views::keys, {}, std::plus{}); + VERIFY(real_reduction == T(20)); + + T imag_reduction = std::ranges::fold_left(array | std::views::values, {}, std::plus{}); + VERIFY(imag_reduction == T(25)); +} + +template <typename T> +constexpr +void +test_apply() +{ + std::complex<T> cpx(T(1), T(2)); + + auto f = [](T a, T b) { return a + b; }; + auto result = std::apply(f, cpx); + + VERIFY(result == T(3)); +} + +template <typename T> +constexpr +bool +all_tests() +{ + test_sanity<T>(); + test_structured_binding<T>(); + test_tuple_cat<T>(); + test_element_view<T>(); + test_apply<T>(); + test_get<T>(); + return true; +} + +#define TEST(T) \ + static_assert(all_tests<T>()); \ + template T& std::get<0, T>(std::complex<T>&); \ + template T& std::get<1, T>(std::complex<T>&); \ + template T&& std::get<0, T>(std::complex<T>&&); \ + template T&& std::get<1, T>(std::complex<T>&&); \ + template const T& std::get<0, T>(const std::complex<T>&); \ + template const T& std::get<1, T>(const std::complex<T>&); \ + template const T&& std::get<0, T>(const std::complex<T>&&); \ + template const T&& std::get<1, T>(const std::complex<T>&&); \ + +TEST(float) +TEST(double) +TEST(long double) + +#ifdef __STDCPP_FLOAT16_T__ +TEST(_Float16) +#endif +#ifdef __STDCPP_FLOAT32_T__ +TEST(_Float32) +#endif +#ifdef __STDCPP_FLOAT64_T__ +TEST(_Float64) +#endif +#ifdef __STDCPP_FLOAT128_T__ +TEST(_Float128) +#endif +#ifdef __STDCPP_BFLOAT16_T__ +TEST(__gnu_cxx::__bfloat16_t) +#endif + +TEST(char) +TEST(int) +TEST(unsigned int) +TEST(size_t) -- 2.34.1
smime.p7s
Description: S/MIME Cryptographic Signature