Skip to content

Commit 52a8bda

Browse files
committed
Use transparent functionals in placeholder expressions.
Fixes and adds regression tests for NVIDIA#1178 & NVIDIA#1229.
1 parent ec5baea commit 52a8bda

File tree

11 files changed

+604
-501
lines changed

11 files changed

+604
-501
lines changed

testing/find.cu

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include <unittest/unittest.h>
2+
#include <thrust/sequence.h>
23
#include <thrust/find.h>
34
#include <thrust/iterator/retag.h>
45

@@ -338,3 +339,35 @@ void TestFindWithBigIndexes()
338339
TestFindWithBigIndexesHelper(33);
339340
}
340341
DECLARE_UNITTEST(TestFindWithBigIndexes);
342+
343+
namespace
344+
{
345+
346+
class Weird
347+
{
348+
int value;
349+
350+
public:
351+
__host__ __device__ Weird(int val, int)
352+
: value(val)
353+
{}
354+
355+
friend __host__ __device__
356+
bool operator==(int x, Weird y)
357+
{
358+
return x == y.value;
359+
}
360+
};
361+
362+
} // end anon namespace
363+
364+
void TestFindAsymmetricEquality()
365+
{ // Regression test for thrust/thrust#1229
366+
thrust::host_vector<int> v(1000);
367+
thrust::sequence(v.begin(), v.end());
368+
thrust::device_vector<int> dv(v);
369+
auto result = thrust::find(dv.begin(), dv.end(), Weird(333, 0));
370+
ASSERT_EQUAL(*result, 333);
371+
ASSERT_EQUAL(result - dv.begin(), 333);
372+
}
373+
DECLARE_UNITTEST(TestFindAsymmetricEquality);

testing/inner_product.cu

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
#include <unittest/unittest.h>
22
#include <thrust/inner_product.h>
3+
4+
#include <thrust/functional.h>
35
#include <thrust/iterator/retag.h>
46
#include <thrust/device_malloc.h>
57
#include <thrust/device_free.h>
8+
#include <thrust/device_vector.h>
69

710
template <class Vector>
811
void TestInnerProductSimple(void)
@@ -153,3 +156,18 @@ void TestInnerProductWithBigIndexes()
153156
TestInnerProductWithBigIndexesHelper(33);
154157
}
155158
DECLARE_UNITTEST(TestInnerProductWithBigIndexes);
159+
160+
void TestInnerProductPlaceholders()
161+
{ // Regression test for thrust/thrust#1178
162+
using namespace thrust::placeholders;
163+
164+
thrust::device_vector<float> v1(100, 1.f);
165+
thrust::device_vector<float> v2(100, 1.f);
166+
167+
auto result = thrust::inner_product(v1.begin(), v1.end(), v2.begin(), 0.0f,
168+
thrust::plus<float>{},
169+
_1 * _2 + 1.0f);
170+
171+
ASSERT_ALMOST_EQUAL(result, 200.f);
172+
}
173+
DECLARE_UNITTEST(TestInnerProductPlaceholders);

thrust/detail/functional/actor.h

Lines changed: 12 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <thrust/detail/functional/value.h>
3131
#include <thrust/detail/functional/composite.h>
3232
#include <thrust/detail/functional/operators/assignment_operator.h>
33+
#include <thrust/detail/raw_reference_cast.h>
3334
#include <thrust/detail/type_traits/result_of_adaptable_function.h>
3435

3536
namespace thrust
@@ -39,6 +40,14 @@ namespace detail
3940
namespace functional
4041
{
4142

43+
// eval_ref<T> is
44+
// - T when T is a subclass of thrust::reference
45+
// - T& otherwise
46+
// This is used to let thrust::references pass through actor evaluations.
47+
template <typename T>
48+
using eval_ref = typename std::conditional<
49+
thrust::detail::is_wrapped_reference<T>::value, T, T&>::type;
50+
4251
template<typename Action, typename Env>
4352
struct apply_actor
4453
{
@@ -61,55 +70,10 @@ template<typename Eval>
6170
typename apply_actor<eval_type, thrust::null_type >::type
6271
operator()(void) const;
6372

64-
template<typename T0>
65-
__host__ __device__
66-
typename apply_actor<eval_type, thrust::tuple<T0&> >::type
67-
operator()(T0 &_0) const;
68-
69-
template<typename T0, typename T1>
70-
__host__ __device__
71-
typename apply_actor<eval_type, thrust::tuple<T0&,T1&> >::type
72-
operator()(T0 &_0, T1 &_1) const;
73-
74-
template<typename T0, typename T1, typename T2>
75-
__host__ __device__
76-
typename apply_actor<eval_type, thrust::tuple<T0&,T1&,T2&> >::type
77-
operator()(T0 &_0, T1 &_1, T2 &_2) const;
78-
79-
template<typename T0, typename T1, typename T2, typename T3>
80-
__host__ __device__
81-
typename apply_actor<eval_type, thrust::tuple<T0&,T1&,T2&,T3&> >::type
82-
operator()(T0 &_0, T1 &_1, T2 &_2, T3 &_3) const;
83-
84-
template<typename T0, typename T1, typename T2, typename T3, typename T4>
85-
__host__ __device__
86-
typename apply_actor<eval_type, thrust::tuple<T0&,T1&,T2&,T3&,T4&> >::type
87-
operator()(T0 &_0, T1 &_1, T2 &_2, T3 &_3, T4 &_4) const;
88-
89-
template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5>
90-
__host__ __device__
91-
typename apply_actor<eval_type, thrust::tuple<T0&,T1&,T2&,T3&,T4&,T5&> >::type
92-
operator()(T0 &_0, T1 &_1, T2 &_2, T3 &_3, T4 &_4, T5 &_5) const;
93-
94-
template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
95-
__host__ __device__
96-
typename apply_actor<eval_type, thrust::tuple<T0&,T1&,T2&,T3&,T4&,T5&,T6&> >::type
97-
operator()(T0 &_0, T1 &_1, T2 &_2, T3 &_3, T4 &_4, T5 &_5, T6 &_6) const;
98-
99-
template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
100-
__host__ __device__
101-
typename apply_actor<eval_type, thrust::tuple<T0&,T1&,T2&,T3&,T4&,T5&,T6&,T7&> >::type
102-
operator()(T0 &_0, T1 &_1, T2 &_2, T3 &_3, T4 &_4, T5 &_5, T6 &_6, T7 &_7) const;
103-
104-
template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
105-
__host__ __device__
106-
typename apply_actor<eval_type, thrust::tuple<T0&,T1&,T2&,T3&,T4&,T5&,T6&,T7&,T8&> >::type
107-
operator()(T0 &_0, T1 &_1, T2 &_2, T3 &_3, T4 &_4, T5 &_5, T6 &_6, T7 &_7, T8 &_8) const;
108-
109-
template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9>
73+
template <typename... Ts>
11074
__host__ __device__
111-
typename apply_actor<eval_type, thrust::tuple<T0&,T1&,T2&,T3&,T4&,T5&,T6&,T7&,T8&,T9&> >::type
112-
operator()(T0 &_0, T1 &_1, T2 &_2, T3 &_3, T4 &_4, T5 &_5, T6 &_6, T7 &_7, T8 &_8, T9 &_9) const;
75+
typename apply_actor<eval_type, thrust::tuple<eval_ref<Ts>...>>::type
76+
operator()(Ts&&... ts) const;
11377

11478
template<typename T>
11579
__host__ __device__

thrust/detail/functional/actor.inl

Lines changed: 32 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
#include <thrust/detail/functional/composite.h>
2828
#include <thrust/detail/functional/operators/assignment_operator.h>
2929
#include <thrust/functional.h>
30+
#include <thrust/type_traits/logical_metafunctions.h>
31+
32+
#include <type_traits>
3033

3134
namespace thrust
3235
{
@@ -62,135 +65,38 @@ template<typename Eval>
6265
return eval_type::eval(thrust::null_type());
6366
} // end basic_environment::operator()
6467

65-
template<typename Eval>
66-
template<typename T0>
67-
__host__ __device__
68-
typename apply_actor<
69-
typename actor<Eval>::eval_type,
70-
typename thrust::tuple<T0&>
71-
>::type
72-
actor<Eval>
73-
::operator()(T0 &_0) const
74-
{
75-
return eval_type::eval(thrust::tie(_0));
76-
} // end basic_environment::operator()
68+
// actor::operator() needs to construct a tuple of references to its
69+
// arguments. To make this work with thrust::reference<T>, we need to
70+
// detect thrust proxy references and store them as T rather than T&.
71+
// This check ensures that the forwarding references passed into
72+
// actor::operator() are either:
73+
// - T&& if and only if T is a thrust::reference<U>, or
74+
// - T& for any other types.
75+
// This struct provides a nicer diagnostic for when these conditions aren't
76+
// met.
77+
template <typename T>
78+
using actor_check_ref_type =
79+
thrust::detail::integral_constant<bool,
80+
( std::is_lvalue_reference<T>::value ||
81+
thrust::detail::is_wrapped_reference<T>::value )>;
82+
83+
template <typename... Ts>
84+
using actor_check_ref_types =
85+
thrust::conjunction<actor_check_ref_type<Ts>...>;
7786

7887
template<typename Eval>
79-
template<typename T0, typename T1>
80-
__host__ __device__
81-
typename apply_actor<
82-
typename actor<Eval>::eval_type,
83-
typename thrust::tuple<T0&,T1&>
84-
>::type
85-
actor<Eval>
86-
::operator()(T0 &_0, T1 &_1) const
88+
template<typename... Ts>
89+
__host__ __device__
90+
typename apply_actor<typename actor<Eval>::eval_type,
91+
thrust::tuple<eval_ref<Ts>...>>::type
92+
actor<Eval>::operator()(Ts&&... ts) const
8793
{
88-
return eval_type::eval(thrust::tie(_0,_1));
89-
} // end basic_environment::operator()
90-
91-
template<typename Eval>
92-
template<typename T0, typename T1, typename T2>
93-
__host__ __device__
94-
typename apply_actor<
95-
typename actor<Eval>::eval_type,
96-
typename thrust::tuple<T0&,T1&,T2&>
97-
>::type
98-
actor<Eval>
99-
::operator()(T0 &_0, T1 &_1, T2 &_2) const
100-
{
101-
return eval_type::eval(thrust::tie(_0,_1,_2));
102-
} // end basic_environment::operator()
103-
104-
template<typename Eval>
105-
template<typename T0, typename T1, typename T2, typename T3>
106-
__host__ __device__
107-
typename apply_actor<
108-
typename actor<Eval>::eval_type,
109-
typename thrust::tuple<T0&,T1&,T2&,T3&>
110-
>::type
111-
actor<Eval>
112-
::operator()(T0 &_0, T1 &_1, T2 &_2, T3 &_3) const
113-
{
114-
return eval_type::eval(thrust::tie(_0,_1,_2,_3));
115-
} // end basic_environment::operator()
116-
117-
template<typename Eval>
118-
template<typename T0, typename T1, typename T2, typename T3, typename T4>
119-
__host__ __device__
120-
typename apply_actor<
121-
typename actor<Eval>::eval_type,
122-
typename thrust::tuple<T0&,T1&,T2&,T3&,T4&>
123-
>::type
124-
actor<Eval>
125-
::operator()(T0 &_0, T1 &_1, T2 &_2, T3 &_3, T4 &_4) const
126-
{
127-
return eval_type::eval(thrust::tie(_0,_1,_2,_3,_4));
128-
} // end basic_environment::operator()
129-
130-
template<typename Eval>
131-
template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5>
132-
__host__ __device__
133-
typename apply_actor<
134-
typename actor<Eval>::eval_type,
135-
typename thrust::tuple<T0&,T1&,T2&,T3&,T4&,T5&>
136-
>::type
137-
actor<Eval>
138-
::operator()(T0 &_0, T1 &_1, T2 &_2, T3 &_3, T4 &_4, T5 &_5) const
139-
{
140-
return eval_type::eval(thrust::tie(_0,_1,_2,_3,_4,_5));
141-
} // end basic_environment::operator()
142-
143-
template<typename Eval>
144-
template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
145-
__host__ __device__
146-
typename apply_actor<
147-
typename actor<Eval>::eval_type,
148-
typename thrust::tuple<T0&,T1&,T2&,T3&,T4&,T5&,T6&>
149-
>::type
150-
actor<Eval>
151-
::operator()(T0 &_0, T1 &_1, T2 &_2, T3 &_3, T4 &_4, T5 &_5, T6 &_6) const
152-
{
153-
return eval_type::eval(thrust::tie(_0,_1,_2,_3,_4,_5,_6));
154-
} // end basic_environment::operator()
155-
156-
template<typename Eval>
157-
template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
158-
__host__ __device__
159-
typename apply_actor<
160-
typename actor<Eval>::eval_type,
161-
typename thrust::tuple<T0&,T1&,T2&,T3&,T4&,T5&,T6&,T7&>
162-
>::type
163-
actor<Eval>
164-
::operator()(T0 &_0, T1 &_1, T2 &_2, T3 &_3, T4 &_4, T5 &_5, T6 &_6, T7 &_7) const
165-
{
166-
return eval_type::eval(thrust::tie(_0,_1,_2,_3,_4,_5,_6,_7));
167-
} // end basic_environment::operator()
168-
169-
template<typename Eval>
170-
template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
171-
__host__ __device__
172-
typename apply_actor<
173-
typename actor<Eval>::eval_type,
174-
typename thrust::tuple<T0&,T1&,T2&,T3&,T4&,T5&,T6&,T7&,T8&>
175-
>::type
176-
actor<Eval>
177-
::operator()(T0 &_0, T1 &_1, T2 &_2, T3 &_3, T4 &_4, T5 &_5, T6 &_6, T7 &_7, T8 &_8) const
178-
{
179-
return eval_type::eval(thrust::tie(_0,_1,_2,_3,_4,_5,_6,_7,_8));
180-
} // end basic_environment::operator()
181-
182-
template<typename Eval>
183-
template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9>
184-
__host__ __device__
185-
typename apply_actor<
186-
typename actor<Eval>::eval_type,
187-
typename thrust::tuple<T0&,T1&,T2&,T3&,T4&,T5&,T6&,T7&,T8&,T9&>
188-
>::type
189-
actor<Eval>
190-
::operator()(T0 &_0, T1 &_1, T2 &_2, T3 &_3, T4 &_4, T5 &_5, T6 &_6, T7 &_7, T8 &_8, T9 &_9) const
191-
{
192-
return eval_type::eval(thrust::tie(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9));
193-
} // end basic_environment::operator()
94+
static_assert(actor_check_ref_types<Ts...>::value,
95+
"Actor evaluations only support rvalue references to "
96+
"thrust::reference subclasses.");
97+
using tuple_type = thrust::tuple<eval_ref<Ts>...>;
98+
return eval_type::eval(tuple_type(THRUST_FWD(ts)...));
99+
} // end actor<Eval>::operator()
194100

195101
template<typename Eval>
196102
template<typename T>

0 commit comments

Comments
 (0)