Question about transform_iterator of zip_iterator!

So, maybe I’m trying to be too fancy for my own good but I’m trying to use make_transform_iterator on a zip_iterator and I have some questions about what seems like black magic performed by Thrust.

Namely, I’m instantiating the zip iterator like this:

auto const zip_begin = thrust::make_zip_iterator(
  thrust::make_tuple(
    thrust::device_ptr<int const>{pa},
    thrust::device_ptr<int const>{la}));

where pa and la are defined as:

int const* __restrict__ pa,
int const* __restrict__ la

My function also has a constant int restricted pointer nm that I’m capturing in an extended lambda. My lambda is defined as:

auto fs = [=] __device__ (thrust::tuple<int const, int const> tup) -> int
{
  int const pa_id = thrust::get<0>(tup);
  int const la = thrust::get<1>(tup);
    
  int const fracture_size = __popc(static_cast<unsigned int>(la)) - 1;
  return nm[pa_id] * fracture_size;
};

I’m using this transformation so that I can do a super awesome exclusive_scan over a transformed pair of ranges of values like this:

auto const zip_begin = thrust::make_zip_iterator(
  thrust::make_tuple(
    thrust::device_ptr<int const>{pa},
    thrust::device_ptr<int const>{la}));
  
auto const begin = thrust::make_transform_iterator(zip_begin, fs);
  
// perform our exclusive_scan and write the result to fl
thrust::exclusive_scan(
  thrust::device,
  begin, begin + assoc_size,
  fl);

My surprise comes from the fact that I’m defining the zip iterator with a tuple of device_ptr but then in my lambda, the argument I’m accepting is a tuple of values! How does Thrust know how to do that?

For reference, here’s my source file and here’s the test that proves it gives the output I want.

This is pretty much the way all thrust algorithms work. We declare the iterators involved, and thrust dereferences those iterators to produce the values that are consumed by the algorithms.

When you dereference a zip iterator, you produce a tuple (of values, or references to values).

In the case of a transform iterator as an argument to a thrust algorithm, the process is to dereference the iterator provided to the transform iterator, pass that tuple to the functor of the transform iterator (or in this case, the lambda), and then pass the value (or tuple) that results from the application of the functor, to the algorithm.

Maybe I am not understanding the question.

i think that abswer is overloading - thrust overloads the exclusive_scan for tuple and in this particular code it dereferences both parts of the tuple

I think I’m just amazed that it all… Well, that it all works. I do a lot of JavaScript development by day and there, I kind of type things and sort of just see what happens. I tried that here and I was able to ultimately do what I want.

I’m just surprised that Thrust does so much for you! I’m happy I got significantly better at C++ because, wow, I couldn’t imagine doing like half this code if it weren’t for Thrust to help me out along way.

Maybe this is actually just a fanboyism thread?