Klaus
Klaus

Reputation: 25663

unpacking tuple for method argument

I have a syntax problem with expanding a tuple to its content.

The working code I have:

class Example
{
    public:
        static void Go( int i, float f)
        {
            std::cout << "p1: " << i << std::endl;
            std::cout << "p2: " << f << std::endl;
        }

        template <typename T, size_t ... I>
            static void Do( T parm )
        {
            Go( std::get<I>( parm)...);
        }
};

int main()
{
    using X = std::tuple<int, float>;
    Example::Do<X,0,1>( std::make_tuple( 1,2.2)) ;
}

But I want to call the expansion with something like

int main()
{
    using X = std::tuple<int, float>;
    using IDX = std::std::index_sequence_for<int, float>;
    Example::Do<X,IDX>( std::make_tuple( 1,2.2)) ;
}

So I am searching for something like ( which can not compiled... ):

template <typename T, size_t ... I>
static void Do<T, std::index_sequence<I...>>(T parm)
   {
            Go( std::get<I>( parm)...);
   }

Upvotes: 1

Views: 373

Answers (2)

Holt
Holt

Reputation: 37661

The problem is that your std::index_sequence (IDX) is not expanding in the template parameters to what you need:

Example::Do<X, IDX>(std::make_tuple(1, 2.2));

...will not "expand" to:

Example::Do<X, 0, 1>(std::make_tuple(1, 2.2)); // This works

What you need is to let the compiler deduce the template arguments for ...I, to do so change your static method to:

template <typename T, size_t ... I>
static void Do(T parm, std::index_sequence<I...>)
{
    Go(std::get<I>(parm)...);
}

And then call it with:

Example::Do(std::make_tuple(1, 2.2), IDX{});

The compiler will automatically deduce the template arguments and call Example::Do<X, 0, 1> as needed.

If you want to be able to call Do without the second arguments, you can add another layer of abstraction in Example:

class Example
{
public:
    static void Go(int i, float f) {
        std::cout << "p1: " << i << std::endl;
        std::cout << "p2: " << f << std::endl;
    }

    template <typename Tuple>
    static void Do(Tuple &&parm) {
        _Do(std::forward<Tuple>(parm),
            std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>{}>{});
    }

private:

    template <typename Tuple, size_t... I>
    static void _Do(Tuple &&parm, std::index_sequence<I...>) {
        Go(std::get<I>(std::forward<Tuple>(parm))...);
    }
};

Then:

Example::Do(std::make_tuple(1, 2.2));

Note that the three versions:

Example::Do<X, 0, 1> (std::make_tuple(1, 2.2));
Example::Do(std::make_tuple(1, 2.2), IDX{});
Example::Do(std::make_tuple(1, 2.2));

Will likely results in the same code after compiler optimization (on my machine with clang++-3.7 and -O1, the three assembly files are strictly identical).

Upvotes: 1

Vittorio Romeo
Vittorio Romeo

Reputation: 93364

Pass the index sequence by value:

template <typename T, size_t... I>
static void Do(T parm, std::index_sequence<I...>)
{
    Go(std::get<I>(parm)...);
}

Call the method like this:

Example::Do(std::make_tuple(1, 2.2), 
    std::index_sequence_for<int, float>{});

Upvotes: 1

Related Questions