https://gcc.gnu.org/g:de231924b73bc120bf2b7ada4eeccd884c249ee1

commit r15-7853-gde231924b73bc120bf2b7ada4eeccd884c249ee1
Author: Giuseppe D'Angelo <giuseppe.dang...@kdab.com>
Date:   Thu Feb 27 22:47:27 2025 +0100

    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 a
    newly added getter that extracts references to the real/imaginary parts
    of a complex<T>. This getter is 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__).
    To avoid duplication and minimize template instantiations, the getter
    uses C++23's deducing this (this avoids const overloads). The value
    category is dealt with by the std::get overloads.
    
    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.
    
            PR libstdc++/113310
    
    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 a private getter to obtain references to the real
            and the imaginary part, on the primary class template and on its
            specializations.
            (get): Add overloads 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.

Diff:
---
 libstdc++-v3/include/bits/stl_pair.h               |  18 +++
 libstdc++-v3/include/bits/version.def              |   5 +
 libstdc++-v3/include/bits/version.h                |   7 +-
 libstdc++-v3/include/std/complex                   |  91 +++++++++++
 .../testsuite/20_util/tuple/tuple_like_ftm.cc      |  17 ++
 .../testsuite/26_numerics/complex/tuple_like.cc    | 179 +++++++++++++++++++++
 6 files changed, 316 insertions(+), 1 deletion(-)

diff --git a/libstdc++-v3/include/bits/stl_pair.h 
b/libstdc++-v3/include/bits/stl_pair.h
index c57e3c097652..8c57712b4617 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 eb2c6d8a3ee7..2af5a54bff28 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1797,6 +1797,11 @@ ftms = {
 
 ftms = {
   name = tuple_like;
+  values = {
+    v = 202311;
+    cxxmin = 26;
+    extra_cond = "__cpp_explicit_this_parameter >= 202110L";
+  };
   values = {
     v = 202207;
     cxxmin = 23;
diff --git a/libstdc++-v3/include/bits/version.h 
b/libstdc++-v3/include/bits/version.h
index 05dadd6fd19c..9833023cfdc8 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -1986,7 +1986,12 @@
 #undef __glibcxx_want_to_underlying
 
 #if !defined(__cpp_lib_tuple_like)
-# if (__cplusplus >= 202100L)
+# if (__cplusplus >  202302L) && (__cpp_explicit_this_parameter >= 202110L)
+#  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 289a144cd7ed..59ef9057c418 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,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _GLIBCXX_CONSTEXPR complex __rep() const
       { return *this; }
 
+#if __glibcxx_tuple_like >= 202311L // >= C++26
+    template<typename _Cp>
+      [[__gnu__::__always_inline__]]
+      constexpr auto&
+      __get_part(this _Cp& __z, size_t __i) noexcept
+      {
+       return __i == 0 ? __z._M_real : __z._M_imag;
+      }
+#endif
+
     private:
       _Tp _M_real;
       _Tp _M_imag;
@@ -608,6 +637,44 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __z.imag(); }
 #endif
 
+#if __glibcxx_tuple_like >= 202311L // >= C++26
+  template<size_t _Int, typename _Tp>
+    [[nodiscard,__gnu__::__always_inline__]]
+    constexpr _Tp&
+    get(complex<_Tp>& __z) noexcept
+    {
+      static_assert(_Int < 2);
+      return __z.__get_part(_Int);
+    }
+
+  template<size_t _Int, typename _Tp>
+    [[nodiscard,__gnu__::__always_inline__]]
+    constexpr _Tp&&
+    get(complex<_Tp>&& __z) noexcept
+    {
+      static_assert(_Int < 2);
+      return std::move(__z.__get_part(_Int));
+    }
+
+  template<size_t _Int, typename _Tp>
+    [[nodiscard,__gnu__::__always_inline__]]
+    constexpr const _Tp&
+    get(const complex<_Tp>& __z) noexcept
+    {
+      static_assert(_Int < 2);
+      return __z.__get_part(_Int);
+    }
+
+  template<size_t _Int, typename _Tp>
+    [[nodiscard,__gnu__::__always_inline__]]
+    constexpr const _Tp&&
+    get(const complex<_Tp>&& __z) noexcept
+    {
+      static_assert(_Int < 2);
+      return std::move(__z.__get_part(_Int));
+    }
+#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 +1415,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
                         : std::pow(complex<_Tp>(__x), __y);
     }
 
+#if __glibcxx_tuple_like >= 202311L // >= C++26
+#define _GLIBCXX26_DECLARE_COMPLEX_TUPLE_HELPER_ACCESSOR \
+  template<typename _Cp> \
+    [[__gnu__::__always_inline__]] \
+    constexpr auto& \
+    __get_part(this _Cp& __z, size_t __i) noexcept \
+    { \
+      return __i == 0 ? __real__ __z._M_value \
+                     : __imag__ __z._M_value; \
+    }
+#else
+#define _GLIBCXX26_DECLARE_COMPLEX_TUPLE_HELPER_ACCESSOR
+#endif
+
   /// 26.2.3  complex specializations
   /// complex<float> specialization
   template<>
@@ -1501,6 +1582,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       _GLIBCXX_CONSTEXPR _ComplexT __rep() const { return _M_value; }
 
+      _GLIBCXX26_DECLARE_COMPLEX_TUPLE_HELPER_ACCESSOR
+
     private:
       _ComplexT _M_value;
     };
@@ -1658,6 +1741,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       _GLIBCXX_CONSTEXPR _ComplexT __rep() const { return _M_value; }
 
+      _GLIBCXX26_DECLARE_COMPLEX_TUPLE_HELPER_ACCESSOR
+
     private:
       _ComplexT _M_value;
     };
@@ -1817,6 +1902,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       _GLIBCXX_CONSTEXPR _ComplexT __rep() const { return _M_value; }
 
+      _GLIBCXX26_DECLARE_COMPLEX_TUPLE_HELPER_ACCESSOR
+
     private:
       _ComplexT _M_value;
     };
@@ -1971,11 +2058,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       constexpr _ComplexT __rep() const { return _M_value; }
 
+      _GLIBCXX26_DECLARE_COMPLEX_TUPLE_HELPER_ACCESSOR
+
     private:
       _ComplexT _M_value;
     };
 #endif
 
+#undef _GLIBCXX26_DECLARE_COMPLEX_TUPLE_HELPER_ACCESSOR
+
 #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 000000000000..d7399b506eab
--- /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 000000000000..7d8d2ee99ce7
--- /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)

Reply via email to