diff options
author | Nikolas Klauser <nikolasklauser@berlin.de> | 2023-05-15 07:07:45 -0700 |
---|---|---|
committer | Nikolas Klauser <nikolasklauser@berlin.de> | 2023-05-15 09:57:20 -0700 |
commit | 6851d078c54eb091235cf8e2916eefc7882278dd (patch) | |
tree | 779860b10d68c6552c6dc5a8026343cf84fdb551 /libcxx | |
parent | cc7dc90481d93734a56098470879189cb2e2c127 (diff) | |
download | llvm-6851d078c54eb091235cf8e2916eefc7882278dd.tar.gz |
[libc++][PSTL] Implement std::transform
Reviewed By: ldionne, #libc
Spies: libcxx-commits
Differential Revision: https://reviews.llvm.org/D149615
Diffstat (limited to 'libcxx')
13 files changed, 388 insertions, 44 deletions
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index 4dd363de4d17..ead9693b5f7d 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -78,10 +78,12 @@ set(files __algorithm/pstl_backends/cpu_backends/find_if.h __algorithm/pstl_backends/cpu_backends/for_each.h __algorithm/pstl_backends/cpu_backends/serial.h + __algorithm/pstl_backends/cpu_backends/transform.h __algorithm/pstl_fill.h __algorithm/pstl_find.h __algorithm/pstl_for_each.h __algorithm/pstl_frontend_dispatch.h + __algorithm/pstl_transform.h __algorithm/push_heap.h __algorithm/ranges_adjacent_find.h __algorithm/ranges_all_of.h diff --git a/libcxx/include/__algorithm/pstl_backend.h b/libcxx/include/__algorithm/pstl_backend.h index 55f2dc4b9f4e..9ac8002a3d27 100644 --- a/libcxx/include/__algorithm/pstl_backend.h +++ b/libcxx/include/__algorithm/pstl_backend.h @@ -32,6 +32,16 @@ A PSTL parallel backend is a tag type to which the following functions are assoc template <class _ExecutionPolicy, class _Iterator, class _Predicate> _Iterator __pstl_find_if(_Backend, _Iterator __first, _Iterator __last, _Predicate __pred); + template <class _ExecutionPolicy, class _InIterator, class _OutIterator, class _UnaryOperation> + _OutIterator __pstl_transform(_InIterator __first, _InIterator __last, _OutIterator __result, _UnaryOperation __op); + + template <class _ExecutionPolicy, class _InIterator1, class _InIterator2, class _OutIterator, class _BinaryOperation> + _OutIterator __pstl_transform(_InIterator1 __first1, + _InIterator1 __last1, + _InIterator2 __first2, + _OutIterator __result, + _BinaryOperation __op); + // TODO: Complete this list The following functions are optional but can be provided. If provided, they are used by the corresponding diff --git a/libcxx/include/__algorithm/pstl_backends/cpu_backend.h b/libcxx/include/__algorithm/pstl_backends/cpu_backend.h index 98e6eb76df4c..a15e7f8f1acd 100644 --- a/libcxx/include/__algorithm/pstl_backends/cpu_backend.h +++ b/libcxx/include/__algorithm/pstl_backends/cpu_backend.h @@ -27,5 +27,6 @@ #include <__algorithm/pstl_backends/cpu_backends/fill.h> #include <__algorithm/pstl_backends/cpu_backends/find_if.h> #include <__algorithm/pstl_backends/cpu_backends/for_each.h> +#include <__algorithm/pstl_backends/cpu_backends/transform.h> #endif // _LIBCPP___ALGORITHM_PSTL_BACKENDS_CPU_BACKEND_H diff --git a/libcxx/include/__algorithm/pstl_backends/cpu_backends/transform.h b/libcxx/include/__algorithm/pstl_backends/cpu_backends/transform.h new file mode 100644 index 000000000000..f1aac24607c2 --- /dev/null +++ b/libcxx/include/__algorithm/pstl_backends/cpu_backends/transform.h @@ -0,0 +1,132 @@ +//===----------------------------------------------------------------------===// +// +// 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___ALGORITHM_PSTL_BACKENDS_CPU_BACKENDS_TRANSFORM_H +#define _LIBCPP___ALGORITHM_PSTL_BACKENDS_CPU_BACKENDS_TRANSFORM_H + +#include <__algorithm/pstl_backends/cpu_backends/backend.h> +#include <__algorithm/transform.h> +#include <__config> +#include <__iterator/iterator_traits.h> +#include <__type_traits/enable_if.h> +#include <__type_traits/is_execution_policy.h> +#include <__type_traits/remove_cvref.h> +#include <__utility/terminate_on_exception.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +#if !defined(_LIBCPP_HAS_NO_INCOMPLETE_PSTL) && _LIBCPP_STD_VER >= 17 + +_LIBCPP_BEGIN_NAMESPACE_STD + +template <class _Iterator1, class _DifferenceType, class _Iterator2, class _Function> +_LIBCPP_HIDE_FROM_ABI _Iterator2 +__simd_walk_2(_Iterator1 __first1, _DifferenceType __n, _Iterator2 __first2, _Function __f) noexcept { + _PSTL_PRAGMA_SIMD + for (_DifferenceType __i = 0; __i < __n; ++__i) + __f(__first1[__i], __first2[__i]); + return __first2 + __n; +} + +template <class _ExecutionPolicy, class _ForwardIterator, class _ForwardOutIterator, class _UnaryOperation> +_LIBCPP_HIDE_FROM_ABI _ForwardOutIterator __pstl_transform( + __cpu_backend_tag, + _ForwardIterator __first, + _ForwardIterator __last, + _ForwardOutIterator __result, + _UnaryOperation __op) { + if constexpr (__is_parallel_execution_policy_v<_ExecutionPolicy> && + __is_cpp17_random_access_iterator<_ForwardIterator>::value && + __is_cpp17_random_access_iterator<_ForwardOutIterator>::value) { + std::__terminate_on_exception([&] { + std::__par_backend::__parallel_for( + __first, __last, [__op, __first, __result](_ForwardIterator __brick_first, _ForwardIterator __brick_last) { + return std::__pstl_transform<__remove_parallel_policy_t<_ExecutionPolicy>>( + __cpu_backend_tag{}, __brick_first, __brick_last, __result + (__brick_first - __first), __op); + }); + }); + return __result + (__last - __first); + } else if constexpr (__is_unsequenced_execution_policy_v<_ExecutionPolicy> && + __is_cpp17_random_access_iterator<_ForwardIterator>::value && + __is_cpp17_random_access_iterator<_ForwardOutIterator>::value) { + return std::__simd_walk_2( + __first, + __last - __first, + __result, + [&](__iter_reference<_ForwardIterator> __in_value, __iter_reference<_ForwardOutIterator> __out_value) { + __out_value = __op(__in_value); + }); + } else { + return std::transform(__first, __last, __result, __op); + } +} + +template <class _Iterator1, class _DifferenceType, class _Iterator2, class _Iterator3, class _Function> +_LIBCPP_HIDE_FROM_ABI _Iterator3 __simd_walk_3( + _Iterator1 __first1, _DifferenceType __n, _Iterator2 __first2, _Iterator3 __first3, _Function __f) noexcept { + _PSTL_PRAGMA_SIMD + for (_DifferenceType __i = 0; __i < __n; ++__i) + __f(__first1[__i], __first2[__i], __first3[__i]); + return __first3 + __n; +} +template <class _ExecutionPolicy, + class _ForwardIterator1, + class _ForwardIterator2, + class _ForwardOutIterator, + class _BinaryOperation, + enable_if_t<is_execution_policy_v<__remove_cvref_t<_ExecutionPolicy>>, int> = 0> +_LIBCPP_HIDE_FROM_ABI _ForwardOutIterator __pstl_transform( + __cpu_backend_tag, + _ForwardIterator1 __first1, + _ForwardIterator1 __last1, + _ForwardIterator2 __first2, + _ForwardOutIterator __result, + _BinaryOperation __op) { + if constexpr (__is_parallel_execution_policy_v<_ExecutionPolicy> && + __is_cpp17_random_access_iterator<_ForwardIterator1>::value && + __is_cpp17_random_access_iterator<_ForwardIterator2>::value && + __is_cpp17_random_access_iterator<_ForwardOutIterator>::value) { + std::__terminate_on_exception([&] { + std::__par_backend::__parallel_for( + __first1, + __last1, + [__op, __first1, __first2, __result](_ForwardIterator1 __brick_first, _ForwardIterator1 __brick_last) { + return std::__pstl_transform<__remove_parallel_policy_t<_ExecutionPolicy>>( + __cpu_backend_tag{}, + __brick_first, + __brick_last, + __first2 + (__brick_first - __first1), + __result + (__brick_first - __first1), + __op); + }); + }); + return __result + (__last1 - __first1); + } else if constexpr (__is_unsequenced_execution_policy_v<_ExecutionPolicy> && + __is_cpp17_random_access_iterator<_ForwardIterator1>::value && + __is_cpp17_random_access_iterator<_ForwardIterator2>::value && + __is_cpp17_random_access_iterator<_ForwardOutIterator>::value) { + return std::__simd_walk_3( + __first1, + __last1 - __first1, + __first2, + __result, + [&](__iter_reference<_ForwardIterator1> __in1, + __iter_reference<_ForwardIterator2> __in2, + __iter_reference<_ForwardOutIterator> __out_value) { __out_value = __op(__in1, __in2); }); + } else { + return std::transform(__first1, __last1, __first2, __result, __op); + } +} + +_LIBCPP_END_NAMESPACE_STD + +#endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_PSTL) && _LIBCPP_STD_VER >= 17 + +#endif // _LIBCPP___ALGORITHM_PSTL_BACKENDS_CPU_BACKENDS_TRANSFORM_H diff --git a/libcxx/include/__algorithm/pstl_transform.h b/libcxx/include/__algorithm/pstl_transform.h new file mode 100644 index 000000000000..9d2d731eeb15 --- /dev/null +++ b/libcxx/include/__algorithm/pstl_transform.h @@ -0,0 +1,66 @@ +//===----------------------------------------------------------------------===// +// +// 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___ALGORITHM_PSTL_TRANSFORM_H +#define _LIBCPP___ALGORITHM_PSTL_TRANSFORM_H + +#include <__algorithm/pstl_backend.h> +#include <__config> +#include <__iterator/iterator_traits.h> +#include <__type_traits/is_execution_policy.h> +#include <__utility/terminate_on_exception.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +#if !defined(_LIBCPP_HAS_NO_INCOMPLETE_PSTL) && _LIBCPP_STD_VER >= 17 + +_LIBCPP_BEGIN_NAMESPACE_STD + +template <class _ExecutionPolicy, + class _ForwardIterator, + class _ForwardOutIterator, + class _UnaryOperation, + class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>, + enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0> +_LIBCPP_HIDE_FROM_ABI _ForwardOutIterator transform( + _ExecutionPolicy&&, + _ForwardIterator __first, + _ForwardIterator __last, + _ForwardOutIterator __result, + _UnaryOperation __op) { + using _Backend = typename __select_backend<_RawPolicy>::type; + return std::__pstl_transform<_RawPolicy>( + _Backend{}, std::move(__first), std::move(__last), std::move(__result), std::move(__op)); +} + +template <class _ExecutionPolicy, + class _ForwardIterator1, + class _ForwardIterator2, + class _ForwardOutIterator, + class _BinaryOperation, + class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>, + enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0> +_LIBCPP_HIDE_FROM_ABI _ForwardOutIterator transform( + _ExecutionPolicy&&, + _ForwardIterator1 __first1, + _ForwardIterator1 __last1, + _ForwardIterator2 __first2, + _ForwardOutIterator __result, + _BinaryOperation __op) { + using _Backend = typename __select_backend<_RawPolicy>::type; + return std::__pstl_transform<_RawPolicy>( + _Backend{}, std::move(__first1), std::move(__last1), std::move(__first2), std::move(__result), std::move(__op)); +} + +_LIBCPP_END_NAMESPACE_STD + +#endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_PSTL) && _LIBCPP_STD_VER >= 17 + +#endif // _LIBCPP___ALGORITHM_PSTL_TRANSFORM_H diff --git a/libcxx/include/__pstl/internal/glue_algorithm_defs.h b/libcxx/include/__pstl/internal/glue_algorithm_defs.h index 82bb3f508d5a..de4501e56b2c 100644 --- a/libcxx/include/__pstl/internal/glue_algorithm_defs.h +++ b/libcxx/include/__pstl/internal/glue_algorithm_defs.h @@ -134,29 +134,6 @@ template <class _ExecutionPolicy, class _ForwardIterator1, class _ForwardIterato __pstl::__internal::__enable_if_execution_policy<_ExecutionPolicy, _ForwardIterator2> swap_ranges( _ExecutionPolicy&& __exec, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2); -// [alg.transform] - -template <class _ExecutionPolicy, class _ForwardIterator1, class _ForwardIterator2, class _UnaryOperation> -__pstl::__internal::__enable_if_execution_policy<_ExecutionPolicy, _ForwardIterator2> -transform(_ExecutionPolicy&& __exec, - _ForwardIterator1 __first, - _ForwardIterator1 __last, - _ForwardIterator2 __result, - _UnaryOperation __op); - -template <class _ExecutionPolicy, - class _ForwardIterator1, - class _ForwardIterator2, - class _ForwardIterator, - class _BinaryOperation> -__pstl::__internal::__enable_if_execution_policy<_ExecutionPolicy, _ForwardIterator> -transform(_ExecutionPolicy&& __exec, - _ForwardIterator1 __first1, - _ForwardIterator1 __last1, - _ForwardIterator2 __first2, - _ForwardIterator __result, - _BinaryOperation __op); - // [alg.replace] template <class _ExecutionPolicy, class _ForwardIterator, class _UnaryPredicate, class _Tp> diff --git a/libcxx/include/__pstl/internal/glue_algorithm_impl.h b/libcxx/include/__pstl/internal/glue_algorithm_impl.h index db62705233b9..bae5efa7d057 100644 --- a/libcxx/include/__pstl/internal/glue_algorithm_impl.h +++ b/libcxx/include/__pstl/internal/glue_algorithm_impl.h @@ -251,27 +251,6 @@ __pstl::__internal::__enable_if_execution_policy<_ExecutionPolicy, _ForwardItera // [alg.transform] -template <class _ExecutionPolicy, class _ForwardIterator1, class _ForwardIterator2, class _UnaryOperation> -__pstl::__internal::__enable_if_execution_policy<_ExecutionPolicy, _ForwardIterator2> -transform(_ExecutionPolicy&& __exec, - _ForwardIterator1 __first, - _ForwardIterator1 __last, - _ForwardIterator2 __result, - _UnaryOperation __op) { - typedef typename iterator_traits<_ForwardIterator1>::reference _InputType; - typedef typename iterator_traits<_ForwardIterator2>::reference _OutputType; - - auto __dispatch_tag = __pstl::__internal::__select_backend(__exec, __first, __result); - - return __pstl::__internal::__pattern_walk2( - __dispatch_tag, - std::forward<_ExecutionPolicy>(__exec), - __first, - __last, - __result, - [__op](_InputType __x, _OutputType __y) mutable { __y = __op(__x); }); -} - template <class _ExecutionPolicy, class _ForwardIterator1, class _ForwardIterator2, diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm index 469bf1706628..18a89eb1a4dc 100644 --- a/libcxx/include/algorithm +++ b/libcxx/include/algorithm @@ -1792,6 +1792,7 @@ template <class BidirectionalIterator, class Compare> #include <__algorithm/pstl_fill.h> #include <__algorithm/pstl_find.h> #include <__algorithm/pstl_for_each.h> +#include <__algorithm/pstl_transform.h> #include <__algorithm/push_heap.h> #include <__algorithm/ranges_adjacent_find.h> #include <__algorithm/ranges_all_of.h> diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index fcf793f0e63f..d6419daff675 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -341,6 +341,9 @@ module std [system] { module pstl_backends_cpu_backends_serial { private header "__algorithm/pstl_backends/cpu_backends/serial.h" } + module pstl_backends_cpu_backends_transform { + private header "__algorithm/pstl_backends/cpu_backends/transform.h" + } module push_heap { private header "__algorithm/push_heap.h" } module ranges_adjacent_find { private header "__algorithm/ranges_adjacent_find.h" } module ranges_all_of { private header "__algorithm/ranges_all_of.h" } diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp index 33cc828b9218..cb80edfd0694 100644 --- a/libcxx/test/libcxx/private_headers.verify.cpp +++ b/libcxx/test/libcxx/private_headers.verify.cpp @@ -121,6 +121,7 @@ END-SCRIPT #include <__algorithm/pstl_backends/cpu_backends/find_if.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/pstl_backends/cpu_backends/find_if.h'}} #include <__algorithm/pstl_backends/cpu_backends/for_each.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/pstl_backends/cpu_backends/for_each.h'}} #include <__algorithm/pstl_backends/cpu_backends/serial.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/pstl_backends/cpu_backends/serial.h'}} +#include <__algorithm/pstl_backends/cpu_backends/transform.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/pstl_backends/cpu_backends/transform.h'}} #include <__algorithm/push_heap.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/push_heap.h'}} #include <__algorithm/ranges_adjacent_find.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_adjacent_find.h'}} #include <__algorithm/ranges_all_of.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_all_of.h'}} diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.transform/pstl.transform.binary.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.transform/pstl.transform.binary.pass.cpp new file mode 100644 index 000000000000..b2b98619fb96 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.transform/pstl.transform.binary.pass.cpp @@ -0,0 +1,79 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// UNSUPPORTED: libcpp-has-no-incomplete-pstl + +// <algorithm> + +// template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2, +// class ForwardIterator, class BinaryOperation> +// ForwardIterator +// transform(ExecutionPolicy&& exec, +// ForwardIterator1 first1, ForwardIterator1 last1, +// ForwardIterator2 first2, ForwardIterator result, +// BinaryOperation binary_op); + +#include <algorithm> +#include <vector> + +#include "test_macros.h" +#include "test_execution_policies.h" +#include "test_iterators.h" + +EXECUTION_POLICY_SFINAE_TEST(transform); + +static_assert(sfinae_test_transform<int, int*, int*, int*, int*, bool (*)(int)>); +static_assert(!sfinae_test_transform<std::execution::parallel_policy, int*, int*, int*, int*, int (*)(int, int)>); + +template <class Iter1, class Iter2, class Iter3> +struct Test { + template <class Policy> + void operator()(Policy&& policy) { + // simple test + for (const int size : {0, 1, 2, 100, 350}) { + std::vector<int> a(size); + std::vector<int> b(size); + for (int i = 0; i != size; ++i) { + a[i] = i + 1; + b[i] = i - 3; + } + + std::vector<int> out(std::size(a)); + decltype(auto) ret = std::transform( + policy, + Iter1(std::data(a)), + Iter1(std::data(a) + std::size(a)), + Iter2(std::data(b)), + Iter3(std::data(out)), + [](int i, int j) { return i + j + 3; }); + static_assert(std::is_same_v<decltype(ret), Iter3>); + assert(base(ret) == std::data(out) + std::size(out)); + for (int i = 0; i != size; ++i) { + assert(out[i] == i * 2 + 1); + } + } + } +}; + +int main(int, char**) { + types::for_each( + types::forward_iterator_list<int*>{}, types::apply_type_identity{[](auto v) { + using Iter3 = typename decltype(v)::type; + types::for_each( + types::forward_iterator_list<int*>{}, types::apply_type_identity{[](auto v2) { + using Iter2 = typename decltype(v2)::type; + types::for_each( + types::forward_iterator_list<int*>{}, + TestIteratorWithPolicies<types::partial_instantiation<Test, Iter2, Iter3>::template apply>{}); + }}); + }}); + + return 0; +} diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.transform/pstl.transform.unary.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.transform/pstl.transform.unary.pass.cpp new file mode 100644 index 000000000000..03985c7d3673 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.transform/pstl.transform.unary.pass.cpp @@ -0,0 +1,65 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// UNSUPPORTED: libcpp-has-no-incomplete-pstl + +// <algorithm> + +// template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2, +// class UnaryOperation> +// ForwardIterator2 +// transform(ExecutionPolicy&& exec, +// ForwardIterator1 first1, ForwardIterator1 last1, +// ForwardIterator2 result, UnaryOperation op); + +#include <algorithm> +#include <cassert> +#include <vector> + +#include "test_macros.h" +#include "test_execution_policies.h" +#include "test_iterators.h" + +// We can't test the constraint on the execution policy, because that would conflict with the binary +// transform algorithm that doesn't take an execution policy, which is not constrained at all. + +template <class Iter1, class Iter2> +struct Test { + template <class Policy> + void operator()(Policy&& policy) { + // simple test + for (const int size : {0, 1, 2, 100, 350}) { + std::vector<int> a(size); + for (int i = 0; i != size; ++i) + a[i] = i + 1; + + std::vector<int> out(std::size(a)); + decltype(auto) ret = std::transform( + policy, Iter1(std::data(a)), Iter1(std::data(a) + std::size(a)), Iter2(std::data(out)), [](int i) { + return i + 3; + }); + static_assert(std::is_same_v<decltype(ret), Iter2>); + assert(base(ret) == std::data(out) + std::size(out)); + for (int i = 0; i != size; ++i) + assert(out[i] == i + 4); + } + } +}; + +int main(int, char**) { + types::for_each(types::forward_iterator_list<int*>{}, types::apply_type_identity{[](auto v) { + using Iter = typename decltype(v)::type; + types::for_each( + types::forward_iterator_list<int*>{}, + TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{}); + }}); + + return 0; +} diff --git a/libcxx/test/support/type_algorithms.h b/libcxx/test/support/type_algorithms.h index 95a282b7b0bc..46e658624f82 100644 --- a/libcxx/test/support/type_algorithms.h +++ b/libcxx/test/support/type_algorithms.h @@ -52,6 +52,34 @@ TEST_CONSTEXPR_CXX14 void for_each(type_list<Types...>, Functor f) { swallow((f.template operator()<Types>(), 0)...); } +template <class T> +struct type_identity { + using type = T; +}; + +#if TEST_STD_VER >= 17 +template <class Func> +struct apply_type_identity { + Func func_; + + apply_type_identity(Func func) : func_(func) {} + + template <class... Args> + decltype(auto) operator()() const { + return func_(type_identity<Args>{}...); + } +}; + +template <class T> +apply_type_identity(T) -> apply_type_identity<T>; + +#endif +template <template <class...> class T, class... Args> +struct partial_instantiation { + template <class Other> + using apply = T<Args..., Other>; +}; + // type categories defined in [basic.fundamental] plus extensions (without CV-qualifiers) using character_types = |