https://gcc.gnu.org/g:9ed821d107f7a183f75b4b5d25b32d7f34ca60b5
commit r16-5625-g9ed821d107f7a183f75b4b5d25b32d7f34ca60b5 Author: Tomasz Kamiński <[email protected]> Date: Thu Nov 13 14:54:11 2025 +0100 libstdc++: Optimize functor storage for transform views iterators. The iterators for transform views (views::transform, views::zip_transform, and views::adjacent_transform) now store a function handle from (from __detail::__func_handle namespace) instead of a pointer to the view object (_M_parent). The following handle templates are defined in __func_handle namespace: * _Inplace: Used if the functor is a function pointer or standard operator wrapper (std::less<>, etc). The functor is stored directly in __func_handle and the iterator. This avoid double indirection through a pointer to the function pointer, and reduce the size of iterator for std wrappers. * _InplaceMemPtr: Used for data or function member pointers. This behaves similarly to _Inplace, but uses __invoke for invocations. * _StaticCall: Used if the operator() selected by overload resolution for the iterator reference is static. In this case, __func_handle is empty, reducing the iterator size. * _ViaPointer: Used for all remaining cases. __func_handle stores a pointer to the functor object stored within the view. Only for this template the cv-qualification of the functor template parameter (_Fn) relevant, and specialization for both const and mutable types are generated. As a consequence of these changes, the iterators of transform views no longer depend on the view object when handle other than __func_handle::_ViaPointer is used. The corresponding views are not marked as borrowed_range, as they are not marked as such in the standard. The use of _Inplace is limited to only set of pre-C++20 standard functors, as for once introduced later operator() was retroactively made static. We do not extent to to any empty fuctor, as it's oprator may still depend on value of this pointer as illustrated by test12 in std/ranges/adaptors/transform.cc test file. Storing function member pointers directly increases the iterator size in that specific case, but this is deemed beneficial for consistent treatment of function and data member pointers. To avoid materializing temporaries when the underlying iterator(s) return a prvalue, the _M_call_deref and _M_call_subscript methods of handles are defined to accept the iterator(s), which are then dereferenced as arguments of the functor. Using _Fd::operator()(*__iters...) inside requires expression is only supported since clang-20, however at the point of GCC-16 release, clang-22 should be already available. libstdc++-v3/ChangeLog: * include/std/ranges (__detail::__is_std_op_template) (__detail::__is_std_op_wrapper, __func_handle::_Inplace) (__func_handle::_InplaceMemPtr, __func_handle::_ViaPointer) (__func_handle::_StaticCall, __detail::__func_handle_t): Define. (transform_view::_Iterator, zip_transform_view::_Iterator) (adjacent_tranform_view::_Iterator): Replace pointer to view (_M_parent) with pointer to functor (_M_fun). Update constructors to construct _M_fun from *__parent->_M_fun. Define operator* and operator[] in terms of _M_call_deref and _M_call_subscript. * testsuite/std/ranges/adaptors/adjacent_transform/1.cc: New tests. * testsuite/std/ranges/adaptors/transform.cc: New tests. * testsuite/std/ranges/zip_transform/1.cc: New tests. Reviewed-by: Jonathan Wakely <[email protected]> Reviewed-by: Patrick Palka <[email protected]> Signed-off-by: Tomasz Kamiński <[email protected]> Diff: --- libstdc++-v3/include/std/ranges | 261 ++++++++++++++++++--- .../std/ranges/adaptors/adjacent_transform/1.cc | 41 ++++ .../testsuite/std/ranges/adaptors/transform.cc | 178 +++++++++++++- .../testsuite/std/ranges/zip_transform/1.cc | 97 ++++++++ 4 files changed, 540 insertions(+), 37 deletions(-) diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index ae57b9a08098..7c5ac931e313 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -286,6 +286,185 @@ namespace ranges operator->() const noexcept { return std::__addressof(_M_value); } }; + + template<template<typename> class> + constexpr bool __is_std_op_template = false; + + template<> + inline constexpr bool __is_std_op_template<std::equal_to> = true; + template<> + inline constexpr bool __is_std_op_template<std::not_equal_to> = true; + template<> + inline constexpr bool __is_std_op_template<std::greater> = true; + template<> + inline constexpr bool __is_std_op_template<std::less> = true; + template<> + inline constexpr bool __is_std_op_template<std::greater_equal> = true; + template<> + inline constexpr bool __is_std_op_template<std::less_equal> = true; + template<> + inline constexpr bool __is_std_op_template<std::plus> = true; + template<> + inline constexpr bool __is_std_op_template<std::minus> = true; + template<> + inline constexpr bool __is_std_op_template<std::multiplies> = true; + template<> + inline constexpr bool __is_std_op_template<std::divides> = true; + template<> + inline constexpr bool __is_std_op_template<std::modulus> = true; + template<> + inline constexpr bool __is_std_op_template<std::negate> = true; + template<> + inline constexpr bool __is_std_op_template<std::logical_and> = true; + template<> + inline constexpr bool __is_std_op_template<std::logical_or> = true; + template<> + inline constexpr bool __is_std_op_template<std::logical_not> = true; + template<> + inline constexpr bool __is_std_op_template<std::bit_and> = true; + template<> + inline constexpr bool __is_std_op_template<std::bit_or> = true; + template<> + inline constexpr bool __is_std_op_template<std::bit_xor> = true; + template<> + inline constexpr bool __is_std_op_template<std::bit_not> = true; + + + template<typename _Fn> + constexpr bool __is_std_op_wrapper = false; + + template<template<typename> class _Ft, typename _Tp> + constexpr bool __is_std_op_wrapper<_Ft<_Tp>> + = __is_std_op_template<_Ft>; + + namespace __func_handle + { + template<typename _Fn> + struct _Inplace + { + _Inplace() = default; + + constexpr explicit + _Inplace(_Fn __func) noexcept + : _M_ptr(__func) + { } + + template<typename... _Iters> + constexpr decltype(auto) + _M_call_deref(const _Iters&... __iters) const + noexcept(noexcept(_M_ptr(*__iters...))) + { return _M_ptr(*__iters...); } + + template<typename _DistType, typename... _Iters> + constexpr decltype(auto) + _M_call_subscript(const _DistType __n, const _Iters&... __iters) const + noexcept(noexcept(_M_ptr(__iters[iter_difference_t<_Iters>(__n)]...))) + { return _M_ptr(__iters[iter_difference_t<_Iters>(__n)]...); } + + private: + [[no_unique_address]] _Fn _M_ptr = _Fn(); + }; + + template<typename _Fn> + struct _InplaceMemPtr + { + _InplaceMemPtr() = default; + + constexpr explicit + _InplaceMemPtr(_Fn __func) noexcept + : _M_ptr(__func) + {} + + template<typename... _Iters> + constexpr decltype(auto) + _M_call_deref(const _Iters&... __iters) const + noexcept(noexcept(std::__invoke(_M_ptr, *__iters...))) + { return std::__invoke(_M_ptr, *__iters...); } + + template<typename _DistType, typename... _Iters> + constexpr decltype(auto) + _M_call_subscript(const _DistType __n, const _Iters&... __iters) const + noexcept(noexcept(std::__invoke(_M_ptr, __iters[iter_difference_t<_Iters>(__n)]...))) + { return std::__invoke(_M_ptr, __iters[iter_difference_t<_Iters>(__n)]...); } + + private: + _Fn _M_ptr = nullptr; + }; + + template<typename _Fn> + struct _ViaPointer + { + _ViaPointer() = default; + + constexpr explicit + _ViaPointer(_Fn& __func) noexcept + : _M_ptr(std::addressof(__func)) + { } + + template<typename _Un> + requires (!is_const_v<_Un>) && is_same_v<const _Un, _Fn> + constexpr + _ViaPointer(_ViaPointer<_Un> __other) noexcept + : _M_ptr(__other._M_ptr) + { } + + template<typename... _Iters> + constexpr decltype(auto) + _M_call_deref(const _Iters&... __iters) const + noexcept(noexcept((*_M_ptr)(*__iters...))) + { return (*_M_ptr)(*__iters...); } + + template<typename _DistType, typename... _Iters> + constexpr decltype(auto) + _M_call_subscript(const _DistType __n, const _Iters&... __iters) const + noexcept(noexcept((*_M_ptr)(__iters[iter_difference_t<_Iters>(__n)]...))) + { return (*_M_ptr)(__iters[iter_difference_t<_Iters>(__n)]...); } + + private: + _Fn* _M_ptr = nullptr; + + template<typename> + friend struct _ViaPointer; + }; + + template<typename _Fn> + struct _StaticCall + { + _StaticCall() = default; + + constexpr explicit + _StaticCall(const _Fn&) noexcept + {} + + template<typename... _Iters> + static constexpr decltype(auto) + _M_call_deref(const _Iters&... __iters) + noexcept(noexcept(_Fn::operator()(*__iters...))) + { return _Fn::operator()(*__iters...); } + + template<typename _DistType, typename... _Iters> + static constexpr decltype(auto) + _M_call_subscript(_DistType __n, const _Iters&... __iters) + noexcept(noexcept(_Fn::operator()(__iters[iter_difference_t<_Iters>(__n)]...))) + { return _Fn::operator()(__iters[iter_difference_t<_Iters>(__n)]...); } + }; + } // __func_handle + + template<typename _Fn, typename... _Iters> + using __func_handle_t = decltype([] { + using _Fd = remove_cv_t<_Fn>; + if constexpr (is_member_pointer_v<_Fd>) + return __func_handle::_InplaceMemPtr<_Fd>(); + else if constexpr (is_function_v<remove_pointer_t<_Fd>>) + return __func_handle::_Inplace<_Fd>(); + else if constexpr (__is_std_op_wrapper<_Fd>) + return __func_handle::_Inplace<_Fd>(); + else if constexpr (requires (const _Iters&... __iters) + { _Fd::operator()(*__iters...); }) + return __func_handle::_StaticCall<_Fd>(); + else + return __func_handle::_ViaPointer<_Fn>(); + }()); } // namespace __detail /// A view that contains exactly one element. @@ -1874,6 +2053,10 @@ namespace views::__adaptor private: using _Parent = __detail::__maybe_const_t<_Const, transform_view>; using _Base = transform_view::_Base<_Const>; + using _Base_iter = iterator_t<_Base>; + using _Func_handle = __detail::__func_handle_t< + __detail::__maybe_const_t<_Const, _Fp>, + _Base_iter>; static auto _S_iter_concept() @@ -1888,10 +2071,8 @@ namespace views::__adaptor return input_iterator_tag{}; } - using _Base_iter = iterator_t<_Base>; - _Base_iter _M_current = _Base_iter(); - _Parent* _M_parent = nullptr; + [[no_unique_address]] _Func_handle _M_fun; public: using iterator_concept = decltype(_S_iter_concept()); @@ -1904,16 +2085,20 @@ namespace views::__adaptor _Iterator() requires default_initializable<_Base_iter> = default; constexpr - _Iterator(_Parent* __parent, _Base_iter __current) - : _M_current(std::move(__current)), - _M_parent(__parent) + _Iterator(_Func_handle __fun, _Base_iter __current) + : _M_current(std::move(__current)), _M_fun(__fun) { } + constexpr + _Iterator(_Parent* __parent, _Base_iter __current) + : _M_current(std::move(__current)), _M_fun(*__parent->_M_fun) + {} + constexpr _Iterator(_Iterator<!_Const> __i) requires _Const && convertible_to<iterator_t<_Vp>, _Base_iter> - : _M_current(std::move(__i._M_current)), _M_parent(__i._M_parent) + : _M_current(std::move(__i._M_current)), _M_fun(__i._M_fun) { } constexpr const _Base_iter& @@ -1926,8 +2111,8 @@ namespace views::__adaptor constexpr decltype(auto) operator*() const - noexcept(noexcept(std::__invoke(*_M_parent->_M_fun, *_M_current))) - { return std::__invoke(*_M_parent->_M_fun, *_M_current); } + noexcept(noexcept(_M_fun._M_call_deref(_M_current))) + { return _M_fun._M_call_deref(_M_current); } constexpr _Iterator& operator++() @@ -1980,7 +2165,7 @@ namespace views::__adaptor constexpr decltype(auto) operator[](difference_type __n) const requires random_access_range<_Base> - { return std::__invoke(*_M_parent->_M_fun, _M_current[__n]); } + { return _M_fun._M_call_subscript(__n, _M_current); } friend constexpr bool operator==(const _Iterator& __x, const _Iterator& __y) @@ -2018,17 +2203,17 @@ namespace views::__adaptor friend constexpr _Iterator operator+(_Iterator __i, difference_type __n) requires random_access_range<_Base> - { return {__i._M_parent, __i._M_current + __n}; } + { return {__i._M_fun, __i._M_current + __n}; } friend constexpr _Iterator operator+(difference_type __n, _Iterator __i) requires random_access_range<_Base> - { return {__i._M_parent, __i._M_current + __n}; } + { return {__i._M_fun, __i._M_current + __n}; } friend constexpr _Iterator operator-(_Iterator __i, difference_type __n) requires random_access_range<_Base> - { return {__i._M_parent, __i._M_current - __n}; } + { return {__i._M_fun, __i._M_current - __n}; } // _GLIBCXX_RESOLVE_LIB_DEFECTS // 3483. transform_view::iterator's difference is overconstrained @@ -5126,13 +5311,21 @@ namespace views::__adaptor class zip_transform_view<_Fp, _Vs...>::_Iterator : public __iter_cat<_Const> { using _Parent = __detail::__maybe_const_t<_Const, zip_transform_view>; + using _Fun_handle = __detail::__func_handle_t< + __detail::__maybe_const_t<_Const, _Fp>, + iterator_t<__detail::__maybe_const_t<_Const, _Vs>>...>; - _Parent* _M_parent = nullptr; + [[no_unique_address]] _Fun_handle _M_fun; __ziperator<_Const> _M_inner; + constexpr + _Iterator(_Fun_handle __fun, __ziperator<_Const> __inner) + : _M_fun(__fun), _M_inner(std::move(__inner)) + { } + constexpr _Iterator(_Parent& __parent, __ziperator<_Const> __inner) - : _M_parent(std::__addressof(__parent)), _M_inner(std::move(__inner)) + : _M_fun(*__parent._M_fun), _M_inner(std::move(__inner)) { } friend class zip_transform_view; @@ -5150,14 +5343,14 @@ namespace views::__adaptor constexpr _Iterator(_Iterator<!_Const> __i) requires _Const && convertible_to<__ziperator<false>, __ziperator<_Const>> - : _M_parent(__i._M_parent), _M_inner(std::move(__i._M_inner)) + : _M_fun(__i._M_fun), _M_inner(std::move(__i._M_inner)) { } constexpr decltype(auto) operator*() const { return std::apply([&](const auto&... __iters) -> decltype(auto) { - return std::__invoke(*_M_parent->_M_fun, *__iters...); + return _M_fun._M_call_deref(__iters...); }, _M_inner._M_current); } @@ -5213,7 +5406,7 @@ namespace views::__adaptor operator[](difference_type __n) const requires random_access_range<_Base<_Const>> { return std::apply([&]<typename... _Is>(const _Is&... __iters) -> decltype(auto) { - return std::__invoke(*_M_parent->_M_fun, __iters[iter_difference_t<_Is>(__n)]...); + return _M_fun._M_call_subscript(__n, __iters...); }, _M_inner._M_current); } @@ -5230,17 +5423,17 @@ namespace views::__adaptor friend constexpr _Iterator operator+(const _Iterator& __i, difference_type __n) requires random_access_range<_Base<_Const>> - { return _Iterator(*__i._M_parent, __i._M_inner + __n); } + { return _Iterator(__i._M_fun, __i._M_inner + __n); } friend constexpr _Iterator operator+(difference_type __n, const _Iterator& __i) requires random_access_range<_Base<_Const>> - { return _Iterator(*__i._M_parent, __i._M_inner + __n); } + { return _Iterator(__i._M_fun, __i._M_inner + __n); } friend constexpr _Iterator operator-(const _Iterator& __i, difference_type __n) requires random_access_range<_Base<_Const>> - { return _Iterator(*__i._M_parent, __i._M_inner - __n); } + { return _Iterator(__i._M_fun, __i._M_inner - __n); } friend constexpr difference_type operator-(const _Iterator& __x, const _Iterator& __y) @@ -5807,13 +6000,23 @@ namespace views::__adaptor { using _Parent = __detail::__maybe_const_t<_Const, adjacent_transform_view>; using _Base = __detail::__maybe_const_t<_Const, _Vp>; + using _Fun_handle = decltype([]<size_t... _Ids>(std::index_sequence<_Ids...>) { + return __detail::__func_handle_t< + __detail::__maybe_const_t<_Const, _Fp>, + iterator_t<__detail::__maybe_const_t<(_Ids, _Const), _Vp>>...>(); + }(make_index_sequence<_Nm>())); - _Parent* _M_parent = nullptr; + [[no_unique_address]] _Fun_handle _M_fun; _InnerIter<_Const> _M_inner; + constexpr + _Iterator(_Fun_handle __fun, _InnerIter<_Const> __inner) + : _M_fun(__fun), _M_inner(std::move(__inner)) + { } + constexpr _Iterator(_Parent& __parent, _InnerIter<_Const> __inner) - : _M_parent(std::__addressof(__parent)), _M_inner(std::move(__inner)) + : _M_fun(*__parent._M_fun), _M_inner(std::move(__inner)) { } static auto @@ -5854,14 +6057,14 @@ namespace views::__adaptor constexpr _Iterator(_Iterator<!_Const> __i) requires _Const && convertible_to<_InnerIter<false>, _InnerIter<_Const>> - : _M_parent(__i._M_parent), _M_inner(std::move(__i._M_inner)) + : _M_fun(__i._M_fun), _M_inner(std::move(__i._M_inner)) { } constexpr decltype(auto) operator*() const { return std::apply([&](const auto&... __iters) -> decltype(auto) { - return std::__invoke(*_M_parent->_M_fun, *__iters...); + return _M_fun._M_call_deref(__iters...); }, _M_inner._M_current); } @@ -5913,7 +6116,7 @@ namespace views::__adaptor operator[](difference_type __n) const requires random_access_range<_Base> { return std::apply([&](const auto&... __iters) -> decltype(auto) { - return std::__invoke(*_M_parent->_M_fun, __iters[__n]...); + return _M_fun._M_call_subscript(__n, __iters...); }, _M_inner._M_current); } @@ -5950,17 +6153,17 @@ namespace views::__adaptor friend constexpr _Iterator operator+(const _Iterator& __i, difference_type __n) requires random_access_range<_Base> - { return _Iterator(*__i._M_parent, __i._M_inner + __n); } + { return _Iterator(__i._M_fun, __i._M_inner + __n); } friend constexpr _Iterator operator+(difference_type __n, const _Iterator& __i) requires random_access_range<_Base> - { return _Iterator(*__i._M_parent, __i._M_inner + __n); } + { return _Iterator(__i._M_fun, __i._M_inner + __n); } friend constexpr _Iterator operator-(const _Iterator& __i, difference_type __n) requires random_access_range<_Base> - { return _Iterator(*__i._M_parent, __i._M_inner - __n); } + { return _Iterator(__i._M_fun, __i._M_inner - __n); } friend constexpr difference_type operator-(const _Iterator& __x, const _Iterator& __y) diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc index 772e4b3b6a0d..6890618754b6 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc @@ -113,6 +113,47 @@ test04() static_assert( requires { x | views::pairwise_transform(move_only{}); } ); } +template<size_t FuncSize, typename Fn> +void +test05(Fn f) +{ + int x[] = {1,2,3,4,5,6}; + auto v = x | views::pairwise_transform(f); + static_assert(sizeof(v.begin()) == 2*sizeof(int*) + FuncSize); +} + +void +test05all() +{ + test05<0>(std::equal_to<>()); + test05<0>(std::equal_to<>()); + test05<0>(std::not_equal_to<>()); + test05<0>(std::greater<>()); + test05<0>(std::less<>()); + test05<0>(std::greater_equal<>()); + test05<0>(std::less_equal<>()); + + test05<0>(std::ranges::equal_to()); + test05<0>(std::ranges::not_equal_to()); + test05<0>(std::ranges::greater()); + test05<0>(std::ranges::less()); + test05<0>(std::ranges::greater_equal()); + test05<0>(std::ranges::less_equal()); + + test05<0>(std::plus<>()); + test05<0>(std::minus<>()); + test05<0>(std::multiplies<>()); + test05<0>(std::divides<>()); + test05<0>(std::modulus<>()); + + test05<0>(std::logical_and<>()); + test05<0>(std::logical_or<>()); + + test05<0>(std::bit_and<>()); + test05<0>(std::bit_or<>()); + test05<0>(std::bit_xor<>()); +} + int main() { diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc index 1788db1ce8d6..3a21a1f0f9c3 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc @@ -18,6 +18,7 @@ // { dg-do run { target c++20 } } #include <algorithm> +#include <cstdint> #include <ranges> #include <testsuite_hooks.h> #include <testsuite_iterators.h> @@ -28,12 +29,12 @@ using __gnu_test::random_access_iterator_wrapper; namespace ranges = std::ranges; namespace views = std::ranges::views; +template<typename Fn> void -test01() +test01(Fn f) { int x[] = {1,2,3,4,5}; - auto is_odd = [] (int i) { return i%2==1; }; - auto v = x | views::transform(is_odd); + auto v = x | views::transform(f); VERIFY( ranges::equal(v, (int[]){1,0,1,0,1}) ); using R = decltype(v); static_assert(std::same_as<bool, decltype(*ranges::begin(v))>); @@ -42,30 +43,124 @@ test01() static_assert(ranges::random_access_range<R>); } +void +test01a() +{ + auto is_odd = [] (int i) { return i%2==1; }; + test01(is_odd); +} + +void +test01b() +{ +#if __cpp_static_call_operator >= 202207L + auto is_odd = [] (int i) static { return i%2==1; }; + test01(is_odd); +#endif +} + +void +test01c() +{ + bool(*is_odd)(int) = [] (int i) { return i%2==1; }; + test01(is_odd); +} + struct X { int i,j; + int& first() { return i; } }; +template<size_t FuncSize, typename Fn> void -test02() +test02(Fn f) { X x[] = {{1,2},{3,4},{5,6},{7,8},{9,10}}; test_range<X, random_access_iterator_wrapper> rx(x); - auto v = rx | views::transform(&X::i); + auto v = rx | views::transform(f); VERIFY( ranges::size(v) == 5 ); VERIFY( ranges::distance(v.begin(), v.end()) == 5 ); VERIFY( ranges::equal(v, (int[]){1,3,5,7,9}) ); VERIFY( ranges::equal(v | views::reverse, (int[]){9,7,5,3,1}) ); using R = decltype(v); + using It = ranges::iterator_t<R>; static_assert(std::same_as<int&, decltype(*ranges::begin(v))>); - static_assert(std::same_as<int, std::iter_value_t<ranges::iterator_t<R>>>); + static_assert(std::same_as<int, std::iter_value_t<It>>); + static_assert(sizeof(It) == sizeof(rx.begin()) + FuncSize); static_assert(ranges::view<R>); static_assert(ranges::sized_range<R>); static_assert(!ranges::common_range<R>); static_assert(ranges::random_access_range<R>); } +void +test02a() +{ test02<sizeof(int X::*)>(&X::i); } + +void +test02b() +{ test02<sizeof(int(X::*)())>(&X::first); } + +void +test02c() +{ + auto first = [](X& x) -> int& { return x.i; }; + test02<sizeof(void*)>(first); +} + +void +test02d() +{ +#if __cpp_static_call_operator >= 202207L + auto first = [](X& x) static -> int& { return x.i; }; + test02<0>(first); +#endif +} + +void +test02e() +{ + int&(*fptr)(X&) = [](X& x) -> int& { return x.i; }; + test02<sizeof(void(*)())>(fptr); +} + +void +test02f() +{ +#if __cpp_static_call_operator >= 202207L + struct PickStatic + { + static constexpr int& + operator()(X& x) + { return x.i; } + + constexpr int + operator()(char*) const + { return 0; }; + }; + test02<0>(PickStatic{}); +#endif +} + +void +test02g() +{ +#if __cpp_static_call_operator >= 202207L + struct PickObject + { + constexpr int& + operator()(X& x) const + { return x.i; } + + static constexpr int + operator()(char*) + { return 0; }; + }; + test02<sizeof(void*)>(PickObject{}); +#endif +} + void test03() { @@ -227,11 +322,75 @@ test11() static_assert(std::same_as<cat, std::random_access_iterator_tag>); } +void +test12() +{ + struct Obfuscate + { + int operator()(int x) const + { return x + reinterpret_cast<std::uintptr_t>(this); } + }; + + int x[]{1, 2, 3, 4, 5}; + auto v = x | views::transform(Obfuscate{}); + VERIFY( ranges::equal(v, v) ); +}; + +void +test13() +{ +#if __cpp_static_call_operator >= 202207L + struct StaticWins { + static int operator()(int i) { return 0; } + int operator()(float f) const { return 1; } + }; + + int x[]{1, 2, 3, 4, 5}; + auto vs = x | views::transform(StaticWins{}); + VERIFY( vs.front() == 0 ); + static_assert( sizeof(vs.begin()) == sizeof(int*) ); + + struct MemberWins { + static int operator()(float f) { return 0; } + int operator()(int i) const { return 1; } + }; + + auto vm = x | views::transform(MemberWins{}); + VERIFY( vm.front() == 1 ); + static_assert( sizeof(vm.begin()) > sizeof(int*) ); +#endif +} + +template<size_t FuncSize, typename Fn> +void +test14(Fn f) +{ + int x[] = {1,2,3,4,5,6}; + auto v = x | views::transform(std::negate<>()); + static_assert(sizeof(v.begin()) == sizeof(int*) + FuncSize); +} + +void +test14all() +{ + test14<0>(std::identity()); + test14<0>(std::negate<>()); + test14<0>(std::bit_not<>()); +} + int main() { - test01(); - test02(); + test01a(); + test01b(); + test01c(); + test02a(); + test02b(); + test02c(); + test02d(); + test02e(); + test02f(); + test02g(); test03(); test04(); test05(); @@ -241,4 +400,7 @@ main() test09(); test10(); test11(); + test12(); + test13(); + test14all(); } diff --git a/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc b/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc index 9a0ad3814e66..8524a146eaa1 100644 --- a/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc +++ b/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc @@ -132,6 +132,97 @@ test04() static_assert( requires { views::zip_transform(move_only{}, x, x); } ); } +struct X +{ + int i; + constexpr int add(int b) const + { return i+b; } +}; + +template<size_t ExtraSize, typename Fn> +constexpr bool +test05(Fn f) +{ + using namespace __gnu_test; + X x[] = {{1},{2},{3},{4},{5}}; + int y[] = {500,400,300,200,100}; + test_range<X, random_access_iterator_wrapper> rx(x); + test_range<int, random_access_iterator_wrapper> ry(y); + + auto v = views::zip_transform(f, rx, ry); + VERIFY( ranges::size(v) == 5 ); + VERIFY( ranges::distance(v.begin(), v.end()) == 5 ); + VERIFY( ranges::equal(v, (int[]){501,402,303,204,105}) ); + VERIFY( ranges::equal(v | views::reverse, (int[]){105,204,303,402,501}) ); + using R = decltype(v); + using It = ranges::iterator_t<R>; + static_assert(std::same_as<int, decltype(*ranges::begin(v))>); + static_assert(std::same_as<int, std::iter_value_t<It>>); + static_assert(sizeof(It) == sizeof(rx.begin()) + sizeof(ry.begin()) + ExtraSize); + static_assert(ranges::view<R>); + static_assert(ranges::sized_range<R>); + static_assert(ranges::common_range<R>); + static_assert(ranges::random_access_range<R>); + return true; +} + +constexpr bool +test05a() +{ + auto add = [](const X& x, int v) { return x.i + v; }; + return test05<sizeof(void*)>(add); +} + +constexpr bool +test05b() +{ + auto add = [](const X& x, int v) static { return x.i + v; }; + return test05<0>(add); +} + +constexpr bool +test05c() +{ + int(*ptr)(const X&, int) = [](const X& x, int v) { return x.i + v; }; + return test05<sizeof(void(*)())>(ptr); +} + +constexpr bool +test05d() +{ return test05<sizeof(int(X::*)())>(&X::add); } + +constexpr bool +test05e() +{ + struct PickStatic + { + static constexpr int + operator()(const X& x1, int v) + { return x1.i + v; } + + constexpr int + operator()(int x, int y) const + { return x + y; }; + }; + return test05<0>(PickStatic{}); +} + +constexpr bool +test05f() +{ + struct PickObject + { + constexpr int + operator()(const X& x1, int v) const + { return x1.i + v; } + + static constexpr int + operator()(int x, int y) + { return x + y; }; + }; + return test05<sizeof(void*)>(PickObject{}); +} + int main() { @@ -139,4 +230,10 @@ main() static_assert(test02()); static_assert(test03()); test04(); + static_assert(test05a()); + static_assert(test05b()); + static_assert(test05c()); + static_assert(test05d()); + static_assert(test05e()); + static_assert(test05f()); }
