diff options
author | Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> | 2025-02-27 22:47:27 +0100 |
---|---|---|
committer | Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> | 2025-03-06 12:24:47 +0100 |
commit | de231924b73bc120bf2b7ada4eeccd884c249ee1 (patch) | |
tree | cadf4d023406d46b4bb1d55b2fef8d1f0d3595ee /libstdc++-v3/testsuite | |
parent | e836d80374aa03a5ea5bd6cca00d826020c461da (diff) | |
download | gcc-de231924b73bc120bf2b7ada4eeccd884c249ee1.zip gcc-de231924b73bc120bf2b7ada4eeccd884c249ee1.tar.gz gcc-de231924b73bc120bf2b7ada4eeccd884c249ee1.tar.bz2 |
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.
Diffstat (limited to 'libstdc++-v3/testsuite')
-rw-r--r-- | libstdc++-v3/testsuite/20_util/tuple/tuple_like_ftm.cc | 17 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/26_numerics/complex/tuple_like.cc | 179 |
2 files changed, 196 insertions, 0 deletions
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 0000000..d7399b5 --- /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 0000000..7d8d2ee --- /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) |