-
Notifications
You must be signed in to change notification settings - Fork 218
transform_iterator fails with callables whose arguments are passed by non-const l-value ref #792
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Can you share a bit more about the usecase here? Using |
I've been experimenting with creating a new fancy iterator that allows accessing a data member of an object. There have been several cases in the past where I've wanted to compute the result of something like a One way I was trying to do this was via a transform iterator that takes an l-value ref of the object and returns an l-value ref of the member: https://godbolt.org/z/jd1obbbWG Generally I'd agree with you that taking an l-value ref in a transform function is a bad idea as it almost certainly means side-effects. In this case there aren't any side-effects. It's moreso to create a different kind of "transform_output_iterator". |
I have ran into this issue from multiple directions by now and may propose a solution at some point in the future. |
For a transform_iterator with a projecting transformation, like: struct foo {
int x, y;
};
struct access_x {
_CCCL_HOST_DEVICE int& operator()(foo& f) const noexcept {
return f.x;
}
};
auto it = thrust::make_transform_iterator(base_it, access_x{});
auto& ref = *it; there are four scenarios to consider:
The problem is that whether the implementation is correct depends on where the iterator is dereferenced from. We could have divergent code paths for host and device code for the dereference implementation, but this would mean we call the transformation function with different types in host and device code. This in turn could lead to the transformation function to return different types as well (e.g. |
For iterators which are wrapped pointers, this auto it = thrust::make_transform_iterator(thrust::raw_pointer_cast(base_it), access_x{}); almost works. It just loses the device system property of This can be worked around by explicitly specifying the iterator system (requires exposing a auto it = thrust::transform_iterator<access_x, foo*, int&, int, thrust::device_system_tag>(base_it, access_x{}); |
For the purpose of projections, |
|
The following code works now: #include <thrust/iterator/counting_iterator.h>
#include <cuda/iterator>
#include <vector>
#include <algorithm>
struct foo{
int v;
int w;
};
int main(){
std::vector<foo> f(10);
auto it = cuda::make_transform_iterator(f.begin(), &foo::v);
std::copy(thrust::make_counting_iterator(0),
thrust::make_counting_iterator(10),
it);
assert(f[9].v == 9);
assert(f[9].w == 0);
} |
Minimal reproducer: https://godbolt.org/z/s4oPhqx3W
This fails because it cannot deduce the return type of the callable.
A workaround is to explicitly define the
result_type
in the callable: https://godbolt.org/z/MTM8b1zW9However, I believe a better solution is to fix the result type deduction.
It currently fails because it is ultimately attempting to do:
This fails due to using
int
as the argument type instead ofint&
.This stems from using
thrust::iterator_value
on the adapted iterator here: https://github.com/NVIDIA/thrust/blob/d50708f1be248f4a42e81fdce360cf368990582e/thrust/iterator/detail/transform_iterator.inl#L39-L42Instead of getting the
value_type
of the iterator, I believe it should useiterator_reference
:Note the use of
raw_reference
to avoid issues withdevice_vector::iterator::reference
.Example: https://godbolt.org/z/8eePb5Kvc
The text was updated successfully, but these errors were encountered: