[libcxx-commits] [libcxx] [libc++] Implement P2819: Add tuple protocol to complex (PR #72776)

via libcxx-commits libcxx-commits at lists.llvm.org
Mon Nov 20 04:42:36 PST 2023


https://github.com/PragmaTwice updated https://github.com/llvm/llvm-project/pull/72776

>From 3f0679d16016630ad57a830c8c676bc87bc1eeb8 Mon Sep 17 00:00:00 2001
From: PragmaTwice <twice at apache.org>
Date: Sat, 18 Nov 2023 05:51:52 +0900
Subject: [PATCH 1/5] [libc++] Implement P2819: Add tuple protocol to complex

---
 libcxx/include/complex | 68 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 68 insertions(+)

diff --git a/libcxx/include/complex b/libcxx/include/complex
index 5cfb160ee00f020..44df42557ea3ca7 100644
--- a/libcxx/include/complex
+++ b/libcxx/include/complex
@@ -1538,6 +1538,74 @@ inline namespace literals
 } // namespace literals
 #endif
 
+#if _LIBCPP_STD_VER >= 26
+// Tuple interface [complex.tuple]
+template<class _Tp>
+struct _LIBCPP_TEMPLATE_VIS tuple_size<complex<_Tp>>
+  : public integral_constant<size_t, 2> {};
+
+template<size_t _Ip, class _Tp>
+struct _LIBCPP_TEMPLATE_VIS tuple_element<_Ip, complex<_Tp>>
+{
+  static_assert(_Ip < 2, "Index out of bounds in std::tuple_element<std::complex<T>>");
+
+  using type _LIBCPP_NODEBUG = _Tp;
+};
+
+template <size_t _Ip>
+struct __get_complex
+{
+  static_assert(_Ip < 2, "Index out of bounds in std::get<std::complex<T>>");
+
+  template <typename _Tp>
+  struct to_array_type;
+
+  template <typename _Tp>
+  struct to_array_type<complex<_Tp>>
+  {
+    using type = _Tp[2];
+  };
+
+  template <class _Tp>
+  static _LIBCPP_HIDE_FROM_ABI constexpr
+  decltype(auto)
+  get(_Tp&& __z) _NOEXCEPT
+  {
+    using _Array_type = typename __get_complex::to_array_type<std::remove_cvref_t<_Tp>>::type;
+    using _Forwarded_array_type = decltype(forward_like<_Tp>(declval<_Array_type>()));
+    return reinterpret_cast<_Forwarded_array_type>(__z)[_Ip];
+  }
+};
+
+template<size_t _Ip, class _Tp>
+inline _LIBCPP_HIDE_FROM_ABI
+constexpr _Tp& get(complex<_Tp>& __z) _NOEXCEPT
+{
+  return __get_complex<_Ip>::get(__z);
+}
+
+template<size_t _Ip, class _Tp>
+inline _LIBCPP_HIDE_FROM_ABI
+constexpr const _Tp& get(const complex<_Tp>& __z) _NOEXCEPT
+{
+  return __get_complex<_Ip>::get(__z);
+}
+
+template<size_t _Ip, class _Tp>
+inline _LIBCPP_HIDE_FROM_ABI
+constexpr _Tp&& get(complex<_Tp>&& __z) _NOEXCEPT
+{
+  return __get_complex<_Ip>::get(std::move(__z));
+}
+
+template<size_t _Ip, class _Tp>
+inline _LIBCPP_HIDE_FROM_ABI
+constexpr const _Tp&& get(const complex<_Tp>&& __z) _NOEXCEPT
+{
+  return __get_complex<_Ip>::get(std::move(__z));
+}
+#endif
+
 _LIBCPP_END_NAMESPACE_STD
 
 #if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20

>From 7bca8296a22ffee1ce4729989a418a67227b5c8c Mon Sep 17 00:00:00 2001
From: PragmaTwice <twice at apache.org>
Date: Sat, 18 Nov 2023 16:04:57 +0900
Subject: [PATCH 2/5] use _ForwardLike instead of std::forward_like

---
 libcxx/include/complex | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/complex b/libcxx/include/complex
index 44df42557ea3ca7..c917bdcbc377b3e 100644
--- a/libcxx/include/complex
+++ b/libcxx/include/complex
@@ -244,6 +244,16 @@ template<class T> complex<T> tanh (const complex<T>&);
 #  pragma GCC system_header
 #endif
 
+#if _LIBCPP_STD_VER >= 26
+// Tuple interface [complex.tuple]
+#  include <__fwd/get.h>
+#  include <__tuple/tuple_element.h>
+#  include <__tuple/tuple_size.h>
+#  include <__type_traits/remove_cvref.h>
+#  include <__type_traits/integral_constant.h>
+#  include <__utility/move.h>
+#endif
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template<class _Tp> class _LIBCPP_TEMPLATE_VIS complex;
@@ -1563,7 +1573,7 @@ struct __get_complex
   template <typename _Tp>
   struct to_array_type<complex<_Tp>>
   {
-    using type = _Tp[2];
+    using type _LIBCPP_NODEBUG = _Tp[2];
   };
 
   template <class _Tp>
@@ -1572,8 +1582,7 @@ struct __get_complex
   get(_Tp&& __z) _NOEXCEPT
   {
     using _Array_type = typename __get_complex::to_array_type<std::remove_cvref_t<_Tp>>::type;
-    using _Forwarded_array_type = decltype(forward_like<_Tp>(declval<_Array_type>()));
-    return reinterpret_cast<_Forwarded_array_type>(__z)[_Ip];
+    return reinterpret_cast<_ForwardLike<_Tp, _Array_type>>(__z)[_Ip];
   }
 };
 

>From fd4f4d9a72823a3e42aac91bd52812cc97fd2baf Mon Sep 17 00:00:00 2001
From: PragmaTwice <twice at apache.org>
Date: Sat, 18 Nov 2023 17:35:00 +0900
Subject: [PATCH 3/5] add complex to tuple_like

---
 libcxx/include/CMakeLists.txt       |  1 +
 libcxx/include/__fwd/complex.h      | 25 +++++++++++++++++++++++++
 libcxx/include/__tuple/tuple_like.h |  9 +++++++++
 libcxx/include/complex              |  9 +++++----
 4 files changed, 40 insertions(+), 4 deletions(-)
 create mode 100644 libcxx/include/__fwd/complex.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 889d7fedbf2965f..7719654b2e18545 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -422,6 +422,7 @@ set(files
   __functional/weak_result_type.h
   __fwd/array.h
   __fwd/bit_reference.h
+  __fwd/complex.h
   __fwd/fstream.h
   __fwd/get.h
   __fwd/hash.h
diff --git a/libcxx/include/__fwd/complex.h b/libcxx/include/__fwd/complex.h
new file mode 100644
index 000000000000000..b8cfbf7daf84858
--- /dev/null
+++ b/libcxx/include/__fwd/complex.h
@@ -0,0 +1,25 @@
+//===---------------------------------------------------------------------===//
+//
+// 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 _LIBCPP___FWD_COMPLEX_H
+#define _LIBCPP___FWD_COMPLEX_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class>
+struct _LIBCPP_TEMPLATE_VIS complex;
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___FWD_COMPLEX_H
diff --git a/libcxx/include/__tuple/tuple_like.h b/libcxx/include/__tuple/tuple_like.h
index dab395be616b7d2..00f8fc818d66c22 100644
--- a/libcxx/include/__tuple/tuple_like.h
+++ b/libcxx/include/__tuple/tuple_like.h
@@ -18,6 +18,10 @@
 #include <__type_traits/remove_cvref.h>
 #include <cstddef>
 
+#if _LIBCPP_STD_VER >= 26
+#  include <__fwd/complex.h>
+#endif
+
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
 #endif
@@ -41,6 +45,11 @@ struct __tuple_like_impl<array<_Tp, _Size> > : true_type {};
 template <class _Ip, class _Sp, ranges::subrange_kind _Kp>
 struct __tuple_like_impl<ranges::subrange<_Ip, _Sp, _Kp> > : true_type {};
 
+#if _LIBCPP_STD_VER >= 26
+template <class _Tp>
+struct __tuple_like_impl<complex<_Tp> > : true_type {};
+#endif
+
 template <class _Tp>
 concept __tuple_like = __tuple_like_impl<remove_cvref_t<_Tp>>::value;
 
diff --git a/libcxx/include/complex b/libcxx/include/complex
index c917bdcbc377b3e..97e48fb73b2ef83 100644
--- a/libcxx/include/complex
+++ b/libcxx/include/complex
@@ -240,13 +240,10 @@ template<class T> complex<T> tanh (const complex<T>&);
 #   include <sstream> // for std::basic_ostringstream
 #endif
 
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-#  pragma GCC system_header
-#endif
-
 #if _LIBCPP_STD_VER >= 26
 // Tuple interface [complex.tuple]
 #  include <__fwd/get.h>
+#  include <__fwd/complex.h>
 #  include <__tuple/tuple_element.h>
 #  include <__tuple/tuple_size.h>
 #  include <__type_traits/remove_cvref.h>
@@ -254,6 +251,10 @@ template<class T> complex<T> tanh (const complex<T>&);
 #  include <__utility/move.h>
 #endif
 
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template<class _Tp> class _LIBCPP_TEMPLATE_VIS complex;

>From e3b65bcd5b73428735e58553140c1b7ca316cebf Mon Sep 17 00:00:00 2001
From: PragmaTwice <twice at apache.org>
Date: Sat, 18 Nov 2023 17:44:07 +0900
Subject: [PATCH 4/5] format

---
 libcxx/include/__tuple/tuple_like.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__tuple/tuple_like.h b/libcxx/include/__tuple/tuple_like.h
index 00f8fc818d66c22..4a4d990e546e1e8 100644
--- a/libcxx/include/__tuple/tuple_like.h
+++ b/libcxx/include/__tuple/tuple_like.h
@@ -45,10 +45,10 @@ struct __tuple_like_impl<array<_Tp, _Size> > : true_type {};
 template <class _Ip, class _Sp, ranges::subrange_kind _Kp>
 struct __tuple_like_impl<ranges::subrange<_Ip, _Sp, _Kp> > : true_type {};
 
-#if _LIBCPP_STD_VER >= 26
+#  if _LIBCPP_STD_VER >= 26
 template <class _Tp>
 struct __tuple_like_impl<complex<_Tp> > : true_type {};
-#endif
+#  endif
 
 template <class _Tp>
 concept __tuple_like = __tuple_like_impl<remove_cvref_t<_Tp>>::value;

>From 1835beba1cc2bc9001e1d8df846a35d3e4965647 Mon Sep 17 00:00:00 2001
From: PragmaTwice <twice at apache.org>
Date: Sun, 19 Nov 2023 01:21:19 +0900
Subject: [PATCH 5/5] use friend class rather than reinterpret_cast

---
 libcxx/include/__fwd/complex.h                |  2 +-
 libcxx/include/complex                        | 62 ++++++++-------
 .../complex.number/complex.tuple/get.pass.cpp | 78 +++++++++++++++++++
 .../complex.tuple/tuple_element.pass.cpp      | 28 +++++++
 .../complex.tuple/tuple_size.pass.cpp         | 27 +++++++
 5 files changed, 169 insertions(+), 28 deletions(-)
 create mode 100644 libcxx/test/std/numerics/complex.number/complex.tuple/get.pass.cpp
 create mode 100644 libcxx/test/std/numerics/complex.number/complex.tuple/tuple_element.pass.cpp
 create mode 100644 libcxx/test/std/numerics/complex.number/complex.tuple/tuple_size.pass.cpp

diff --git a/libcxx/include/__fwd/complex.h b/libcxx/include/__fwd/complex.h
index b8cfbf7daf84858..d096269aed2eebf 100644
--- a/libcxx/include/__fwd/complex.h
+++ b/libcxx/include/__fwd/complex.h
@@ -18,7 +18,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class>
-struct _LIBCPP_TEMPLATE_VIS complex;
+class _LIBCPP_TEMPLATE_VIS complex;
 
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/complex b/libcxx/include/complex
index 97e48fb73b2ef83..3859b4d14fe5e06 100644
--- a/libcxx/include/complex
+++ b/libcxx/include/complex
@@ -319,6 +319,10 @@ public:
             *this = *this / complex(__c.real(), __c.imag());
             return *this;
         }
+#if _LIBCPP_STD_VER >= 26
+    // Tuple interface [complex.tuple]
+    friend struct __get_complex;
+#endif
 };
 
 template<> class _LIBCPP_TEMPLATE_VIS complex<double>;
@@ -380,6 +384,10 @@ public:
             *this = *this / complex(__c.real(), __c.imag());
             return *this;
         }
+#if _LIBCPP_STD_VER >= 26
+    // Tuple interface [complex.tuple]
+    friend struct __get_complex;
+#endif
 };
 
 template<>
@@ -438,6 +446,10 @@ public:
             *this = *this / complex(__c.real(), __c.imag());
             return *this;
         }
+#if _LIBCPP_STD_VER >= 26
+    // Tuple interface [complex.tuple]
+    friend struct __get_complex;
+#endif
 };
 
 template<>
@@ -496,6 +508,10 @@ public:
             *this = *this / complex(__c.real(), __c.imag());
             return *this;
         }
+#if _LIBCPP_STD_VER >= 26
+    // Tuple interface [complex.tuple]
+    friend struct __get_complex;
+#endif
 };
 
 inline
@@ -1553,66 +1569,58 @@ inline namespace literals
 // Tuple interface [complex.tuple]
 template<class _Tp>
 struct _LIBCPP_TEMPLATE_VIS tuple_size<complex<_Tp>>
-  : public integral_constant<size_t, 2> {};
+    : public integral_constant<size_t, 2> {};
 
 template<size_t _Ip, class _Tp>
 struct _LIBCPP_TEMPLATE_VIS tuple_element<_Ip, complex<_Tp>>
 {
-  static_assert(_Ip < 2, "Index out of bounds in std::tuple_element<std::complex<T>>");
+    static_assert(_Ip < 2, "Index out of bounds in std::tuple_element<std::complex<T>>");
 
-  using type _LIBCPP_NODEBUG = _Tp;
+    using type _LIBCPP_NODEBUG = _Tp;
 };
 
-template <size_t _Ip>
 struct __get_complex
 {
-  static_assert(_Ip < 2, "Index out of bounds in std::get<std::complex<T>>");
-
-  template <typename _Tp>
-  struct to_array_type;
-
-  template <typename _Tp>
-  struct to_array_type<complex<_Tp>>
-  {
-    using type _LIBCPP_NODEBUG = _Tp[2];
-  };
-
-  template <class _Tp>
-  static _LIBCPP_HIDE_FROM_ABI constexpr
-  decltype(auto)
-  get(_Tp&& __z) _NOEXCEPT
-  {
-    using _Array_type = typename __get_complex::to_array_type<std::remove_cvref_t<_Tp>>::type;
-    return reinterpret_cast<_ForwardLike<_Tp, _Array_type>>(__z)[_Ip];
-  }
+    template <size_t _Ip, class _Tp>
+    static _LIBCPP_HIDE_FROM_ABI constexpr
+    auto&&
+    get(_Tp&& __z) _NOEXCEPT
+    {
+        static_assert(_Ip < 2, "Index out of bounds in std::get<std::complex<T>>");
+        if constexpr (_Ip == 0) {
+            return std::forward<_Tp>(__z).__re_;
+        } else {
+            return std::forward<_Tp>(__z).__im_;
+        }
+    }
 };
 
 template<size_t _Ip, class _Tp>
 inline _LIBCPP_HIDE_FROM_ABI
 constexpr _Tp& get(complex<_Tp>& __z) _NOEXCEPT
 {
-  return __get_complex<_Ip>::get(__z);
+    return __get_complex::get<_Ip>(__z);
 }
 
 template<size_t _Ip, class _Tp>
 inline _LIBCPP_HIDE_FROM_ABI
 constexpr const _Tp& get(const complex<_Tp>& __z) _NOEXCEPT
 {
-  return __get_complex<_Ip>::get(__z);
+    return __get_complex::get<_Ip>(__z);
 }
 
 template<size_t _Ip, class _Tp>
 inline _LIBCPP_HIDE_FROM_ABI
 constexpr _Tp&& get(complex<_Tp>&& __z) _NOEXCEPT
 {
-  return __get_complex<_Ip>::get(std::move(__z));
+    return __get_complex::get<_Ip>(std::move(__z));
 }
 
 template<size_t _Ip, class _Tp>
 inline _LIBCPP_HIDE_FROM_ABI
 constexpr const _Tp&& get(const complex<_Tp>&& __z) _NOEXCEPT
 {
-  return __get_complex<_Ip>::get(std::move(__z));
+    return __get_complex::get<_Ip>(std::move(__z));
 }
 #endif
 
diff --git a/libcxx/test/std/numerics/complex.number/complex.tuple/get.pass.cpp b/libcxx/test/std/numerics/complex.number/complex.tuple/get.pass.cpp
new file mode 100644
index 000000000000000..687d36372342997
--- /dev/null
+++ b/libcxx/test/std/numerics/complex.number/complex.tuple/get.pass.cpp
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <complex>
+
+// template<size_t I, class T>
+// constexpr T& get(complex<T>& __z) noexcept;
+
+// template<size_t I, class T>
+// constexpr const T& get(const complex<T>& __z) noexcept;
+
+// template<size_t I, class T>
+// constexpr T&& get(complex<T>&& __z) noexcept;
+
+// template<size_t I, class T>
+// constexpr const T&& get(const complex<T>&& __z) noexcept;
+
+#include <complex>
+#include <cassert>
+
+constexpr bool test() {
+    // T&
+    {
+        std::complex<double> c(-1, 1);
+        static_assert(std::is_same_v<decltype(std::get<0>(c)), double&>);
+        static_assert(std::is_same_v<decltype(std::get<1>(c)), double&>);
+        assert(std::get<0>(c) == -1);
+        assert(std::get<1>(c) == 1);
+
+        std::get<0>(c) = 2;
+        assert(std::get<0>(c) == 2);
+
+        std::get<1>(c) = -2;
+        assert(std::get<0>(c) == 2);
+        assert(std::get<1>(c) == -2);
+    }
+
+    // const T&
+    {
+        const std::complex<double> c(-1, 1);
+        static_assert(std::is_same_v<decltype(std::get<0>(c)), const double&>);
+        static_assert(std::is_same_v<decltype(std::get<1>(c)), const double&>);
+        assert(std::get<0>(c) == -1);
+        assert(std::get<1>(c) == 1);
+    }
+
+    // T&&
+    {
+        std::complex<double> c1(-1, 1), c2(-2, 2);
+        static_assert(std::is_same_v<decltype(std::get<0>(std::move(c1))), double&&>);
+        static_assert(std::is_same_v<decltype(std::get<1>(std::move(c1))), double&&>);
+        assert(std::get<0>(std::move(c1)) == -1);
+        assert(std::get<1>(std::move(c2)) == 2);
+    }
+
+    // const T&&
+    {
+        const std::complex<double> c1(-1, 1), c2(-2, 2);
+        static_assert(std::is_same_v<decltype(std::get<0>(std::move(c1))), const double&&>);
+        static_assert(std::is_same_v<decltype(std::get<1>(std::move(c1))), const double&&>);
+        assert(std::get<0>(std::move(c1)) == -1);
+        assert(std::get<1>(std::move(c2)) == 2);
+    }
+
+    return true;
+}
+
+int main() {
+    test();
+    static_assert(test());
+}
diff --git a/libcxx/test/std/numerics/complex.number/complex.tuple/tuple_element.pass.cpp b/libcxx/test/std/numerics/complex.number/complex.tuple/tuple_element.pass.cpp
new file mode 100644
index 000000000000000..a62a8b332ffd6c7
--- /dev/null
+++ b/libcxx/test/std/numerics/complex.number/complex.tuple/tuple_element.pass.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <complex>
+
+// template <size_t I, class T>
+// struct tuple_element<I, complex<T>>;
+
+#include <complex>
+
+template <typename T>
+void test() {
+    static_assert(std::is_same_v<typename std::tuple_element<0, std::complex<T>>::type, T>);
+    static_assert(std::is_same_v<typename std::tuple_element<1, std::complex<T>>::type, T>);
+}
+
+int main() {
+    test<float>();
+    test<double>();
+    test<long double>();
+}
diff --git a/libcxx/test/std/numerics/complex.number/complex.tuple/tuple_size.pass.cpp b/libcxx/test/std/numerics/complex.number/complex.tuple/tuple_size.pass.cpp
new file mode 100644
index 000000000000000..10c91f3acf839f6
--- /dev/null
+++ b/libcxx/test/std/numerics/complex.number/complex.tuple/tuple_size.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <complex>
+
+// template <class T>
+// struct tuple_size<complex<T>>;
+
+#include <complex>
+
+template <typename T>
+void test() {
+    static_assert(std::tuple_size<std::complex<T>>::value == 2);
+}
+
+int main() {
+    test<float>();
+    test<double>();
+    test<long double>();
+}



More information about the libcxx-commits mailing list