-
Notifications
You must be signed in to change notification settings - Fork 761
transform_output_iterator (and transform_input_output_iterator) unusable with reduce_by_key due to missing default constructor #1804
Description
I have been unable to use a thrust::transform_output_iterator as the output iterator for thrust::reduce_by_key. After much head scratching and digging, I found the cause:
Unlike all other Thrust iterators, transform_output_iterator and transform_input_output_iterator have no default constructor. And reduce_by_key constructs an empty pair or iterators for its result:
thrust/thrust/system/cuda/detail/reduce_by_key.h
Line 1087 in d3e6fa1
| pair<KeysOutputIt, ValuesOutputIt> result{}; |
If one of the types in the pair is a transform_output_iterator, compilation will fail with: error: no instance of constructor "thrust::transform_output_iterator<UnaryFunction, OutputIterator>::transform_output_iterator [with UnaryFunction = ...]" matches the argument list.
Here's a repro: https://godbolt.org/z/P98YYh1x1
We have found two ways to fix this. The simple way is to add the default constructor. Here's a demo of that. This has a namespace thrust_fix which has its own transform_output_iterator. https://godbolt.org/z/rK3Ec73bz
Another way is to change the scary self-referential macro THRUST_INDEX_TYPE_DISPATCH so that it doesn't require the "Initialize Then Modify" antipattern. Its approach of assigning to the result requires pre-declaring that result, which means auto can't be used.
thrust/thrust/system/cuda/detail/dispatch.h
Lines 29 to 37 in d3e6fa1
| #define THRUST_INDEX_TYPE_DISPATCH(status, call, count, arguments) \ | |
| if (count <= thrust::detail::integer_traits<thrust::detail::int32_t>::const_max) { \ | |
| auto THRUST_PP_CAT2(count, _fixed) = static_cast<thrust::detail::int32_t>(count); \ | |
| status = call arguments; \ | |
| } \ | |
| else { \ | |
| auto THRUST_PP_CAT2(count, _fixed) = static_cast<thrust::detail::int64_t>(count); \ | |
| status = call arguments; \ | |
| } |
(Not to mention that requiring the caller to inject the renamed count_fixed variable into the arguments list is uuuugly.)
This could be done differently using an IILE:
#define THRUST_INDEX_TYPE_DISPATCH_NEW(call, count, arguments) \
[&]() { \
if (count > thrust::detail::integer_traits<thrust::detail::int32_t>::const_max) { \
auto THRUST_PP_CAT2(count, _fixed) = static_cast<thrust::detail::int32_t>(count); \
return call arguments; \
} else { \
auto THRUST_PP_CAT2(count, _fixed) = static_cast<thrust::detail::int64_t>(count); \
return call arguments;
} \
}();
This means we no longer have to pre-declare a pair of iterators in reduce_by_key. We just return directly:
return THRUST_INDEX_TYPE_DISPATCH(reduce_by_key_dispatch,
num_items,
(policy,
keys_first,
num_items_fixed,
values_first,
keys_output,
values_output,
equality_op,
reduction_op));
There may be limitations of the lambda capture by reference used here that I haven't thought about, and this would impact a lot of Thrust code. So I recommend just adding the default constructors. :)
CC @isVoid
Metadata
Metadata
Assignees
Labels
Type
Projects
Status