The iterators for transform views (views::transform, views::zip_transform,
and views::adjacent_transform) now store a __detail::__func_handle instead
of a pointer to the view object (_M_parent).
The behavior of the __func_handle specialization depends on the _FuncHandleType
template parameter:
* _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 specialization is
the cv-qualification of the functor template parameter (_Fn) relevant, and
both const and mutable specializations are generated.
As a consequence of these changes, the iterators of transform views no longer
depend on the view object when __func_handle is specialized for values other
than _ViaPointer. 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 standard functors, as arbitrary
empty functor may still depend on value of this pointer in their operator(),
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 __func_handle 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/bits/utility.h (__is_specialization_of): Moved from
std/format.
* include/std/format (__is_specialization_of): Moved to
bits/utility.
* include/std/ranges (__detail::__is_std_op_wrapper)
(__detail::_FuncHandleType, __detail::__func_handle)
(__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 cosntruct _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.
Signed-off-by: Tomasz Kamiński <[email protected]>
---
v2:
- stores inside of iterator selected set of standard operators
- renames the _FunctorType enum to _FuncHandleEnum and corresponding
enumertor values (_Inplace is also used for empty functors)
- do not define __func_handle primary template, and provide separate
specialization for _ViaTemplate (previously _Stateful)
- add test for all functors
Tessted on x86_64-linux linux. *range*transform* additionally tested
with all standard modes.
libstdc++-v3/include/bits/utility.h | 9 +
libstdc++-v3/include/std/format | 5 -
libstdc++-v3/include/std/ranges | 249 ++++++++++++++++--
.../ranges/adaptors/adjacent_transform/1.cc | 41 +++
.../std/ranges/adaptors/transform.cc | 185 ++++++++++++-
.../testsuite/std/ranges/zip_transform/1.cc | 101 +++++++
6 files changed, 548 insertions(+), 42 deletions(-)
diff --git a/libstdc++-v3/include/bits/utility.h
b/libstdc++-v3/include/bits/utility.h
index 96ac69883f1..522a7a1d884 100644
--- a/libstdc++-v3/include/bits/utility.h
+++ b/libstdc++-v3/include/bits/utility.h
@@ -46,6 +46,15 @@ namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
+/// @cond undocumented
+#if __cplusplus >= 201703L
+ template<typename _Tp, template<typename...> class _Class>
+ constexpr bool __is_specialization_of = false;
+ template<template<typename...> class _Class, typename... _Args>
+ constexpr bool __is_specialization_of<_Class<_Args...>, _Class> = true;
+#endif
+/// @endcond
+
/// Finds the size of a given tuple type.
template<typename _Tp>
struct tuple_size;
diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
index f64f35a202e..1e1ec1fb98f 100644
--- a/libstdc++-v3/include/std/format
+++ b/libstdc++-v3/include/std/format
@@ -417,11 +417,6 @@ namespace __format
};
/// @cond undocumented
- template<typename _Tp, template<typename...> class _Class>
- constexpr bool __is_specialization_of = false;
- template<template<typename...> class _Class, typename... _Args>
- constexpr bool __is_specialization_of<_Class<_Args...>, _Class> = true;
-
namespace __format
{
// pre: first != last
diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index ae57b9a0809..b384e5e45d3 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -286,6 +286,173 @@ namespace ranges
operator->() const noexcept
{ return std::__addressof(_M_value); }
};
+
+ enum class _FuncHandleType
+ {
+ _Inplace,
+ _InplaceMemPtr,
+ _ViaPointer,
+ _StaticCall,
+ };
+
+ template<typename _Fn>
+ concept __is_std_op_wrapper = is_empty_v<_Fn> && (
+ is_same_v<_Fn, std::identity>
+ || __is_specialization_of<_Fn, std::equal_to>
+ || __is_specialization_of<_Fn, std::not_equal_to>
+ || __is_specialization_of<_Fn, std::greater>
+ || __is_specialization_of<_Fn, std::less>
+ || __is_specialization_of<_Fn, std::greater_equal>
+ || __is_specialization_of<_Fn, std::less_equal>
+ || is_same_v<_Fn, std::compare_three_way>
+ || is_same_v<_Fn, std::ranges::equal_to>
+ || is_same_v<_Fn, std::ranges::not_equal_to>
+ || is_same_v<_Fn, std::ranges::greater>
+ || is_same_v<_Fn, std::ranges::less>
+ || is_same_v<_Fn, std::ranges::greater_equal>
+ || is_same_v<_Fn, std::ranges::less_equal>
+ || __is_specialization_of<_Fn, std::plus>
+ || __is_specialization_of<_Fn, std::minus>
+ || __is_specialization_of<_Fn, std::multiplies>
+ || __is_specialization_of<_Fn, std::divides>
+ || __is_specialization_of<_Fn, std::modulus>
+ || __is_specialization_of<_Fn, std::negate>
+ || __is_specialization_of<_Fn, std::logical_and>
+ || __is_specialization_of<_Fn, std::logical_or>
+ || __is_specialization_of<_Fn, std::logical_not>
+ || __is_specialization_of<_Fn, std::bit_and>
+ || __is_specialization_of<_Fn, std::bit_or>
+ || __is_specialization_of<_Fn, std::bit_xor>
+ || __is_specialization_of<_Fn, std::bit_not>);
+
+ template<typename _Fn, _FuncHandleType __ft>
+ struct __func_handle;
+
+ template<typename _Fn>
+ struct __func_handle<_Fn, _FuncHandleType::_Inplace>
+ {
+ __func_handle() = default;
+
+ constexpr explicit
+ __func_handle(_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 __func_handle<_Fn, _FuncHandleType::_InplaceMemPtr>
+ {
+ __func_handle() = default;
+
+ constexpr explicit
+ __func_handle(_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 __func_handle<_Fn, _FuncHandleType::_ViaPointer>
+ {
+ __func_handle() = default;
+
+ constexpr explicit
+ __func_handle(_Fn& __func) noexcept
+ : _M_ptr(std::addressof(__func))
+ { }
+
+ template<typename _Un>
+ requires (!is_const_v<_Un>) && is_same_v<const _Un, _Fn>
+ constexpr
+ __func_handle(__func_handle<_Un, _FuncHandleType::_ViaPointer>
__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, _FuncHandleType>
+ friend struct __func_handle;
+ };
+
+ template<typename _Fn>
+ struct __func_handle<_Fn, _FuncHandleType::_StaticCall>
+ {
+ __func_handle() = default;
+
+ constexpr explicit
+ __func_handle(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)]...); }
+ };
+
+ 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<_Fd, _FuncHandleType::_InplaceMemPtr>();
+ else if constexpr (is_function_v<remove_pointer_t<_Fd>>)
+ return __func_handle<_Fd, _FuncHandleType::_Inplace>();
+ else if constexpr (__is_std_op_wrapper<_Fd>)
+ return __func_handle<_Fd, _FuncHandleType::_Inplace>();
+ else if constexpr (requires (const _Iters&... __iters)
+ { _Fd::operator()(*__iters...); })
+ return __func_handle<_Fd, _FuncHandleType::_StaticCall>();
+ else
+ return __func_handle<_Fn, _FuncHandleType::_ViaPointer>();
+ }());
} // namespace __detail
/// A view that contains exactly one element.
@@ -1874,6 +2041,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 +2059,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 +2073,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 +2099,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 +2153,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 +2191,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 +5299,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 +5331,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 +5394,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 +5411,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 +5988,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 +6045,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 +6104,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 +6141,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 772e4b3b6a0..6890618754b 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 1788db1ce8d..7a0c344cc5c 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,131 @@ 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
+test02h()
+{
+ int&(*fptr)(X&) = [](X& x) -> int& { return x.i; };
+ test02<sizeof(void(*)())>(fptr);
+}
+
void
test03()
{
@@ -227,11 +329,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 +407,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 9a0ad3814e6..d4bd7db26cb 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,14 @@ main()
static_assert(test02());
static_assert(test03());
test04();
+ static_assert(test01());
+ 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());
}
--
2.51.1