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.
This commit is contained in:
parent
e836d80374
commit
de231924b7
6 changed files with 316 additions and 1 deletions
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
17
libstdc++-v3/testsuite/20_util/tuple/tuple_like_ftm.cc
Normal file
17
libstdc++-v3/testsuite/20_util/tuple/tuple_like_ftm.cc
Normal file
|
@ -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
|
||||
|
179
libstdc++-v3/testsuite/26_numerics/complex/tuple_like.cc
Normal file
179
libstdc++-v3/testsuite/26_numerics/complex/tuple_like.cc
Normal file
|
@ -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)
|
Loading…
Add table
Reference in a new issue