Variadic tuple generation

I want to write C ++ - code based on integer value and coordinate generates a set of tuples and function calls like this:

// dimension = 1, input = (i) generated tuples: (i-1) (i), (i+1)
// dimension = 2, input = (i,j) generated tuples: (i-1, j), (i, j-1), (i, j),(i+1, j), (i, j+1)
<typename Index, int dimension>
void my_function(Index input)
{
    // some magic here that generates the following code or sth like this
    get_value(generated_tuple0);
    get_value(generated_tuple1);
    ....
    ....
    get_value(generated_tupleN);

}

      

I don't know how to program templates, maybe with variants in C ++ 11 it is possible.

+3


source to share


2 answers


Here is a compilation solution. It generates the required tuples on the fly and calls the function passed to it.

template<typename TupleType, typename FunctionType, size_t N, typename = std::enable_if_t<N != std::tuple_size<std::decay_t<TupleType> >::value> >  //why SFINAE required?
void tuple_caller_impl(TupleType&& t, FunctionType&& f, std::integral_constant<size_t, N>)
{
    --std::get<N>(t); f(t); ++std::get<N>(t);
    ++std::get<N>(t); f(t); --std::get<N>(t);

    tuple_caller_impl(std::forward<TupleType>(t), std::forward<FunctionType>(f), std::integral_constant<size_t, N+1>());
}

template<typename TupleType, typename FunctionType>
void tuple_caller_impl(TupleType&& t, FunctionType&& f, typename std::tuple_size<std::decay_t<TupleType> >::type)
{
    f(std::forward<TupleType>(t)); // finally call function on the unchanged tuple
                                   // could also be done by specializing the 0-th call to tuple_caller_impl
}

template<typename TupleType, typename FunctionType>
void tuple_caller(TupleType&& t, FunctionType&& f)
{
    tuple_caller_impl(std::forward<TupleType>(t), std::forward<FunctionType>(f), std::integral_constant<size_t, 0>());
}

      

Apply it like

int main()
{
    auto f = [](auto const& t) { std::cout<<"call function with ("<<std::get<0>(t)<<","<<std::get<1>(t)<<","<<std::get<2>(t)<<")"<<std::endl; };
    auto t= std::make_tuple(1,5,3);

    tuple_caller(t,f);
}

      



The previous displays the following:

call function with (0,5,3)
call function with (2,5,3)
call function with (1,4,3)
call function with (1,6,3)
call function with (1,5,2)
call function with (1,5,4)
call function with (1,5,3)

      

DEMO

Note that the order of the calls is different from the order in your example.

+1


source


Assuming the index is a tuple, this is a possible solution:

template <class Index, int I, int dimension>
struct tuple_builder_t
{
    static void build(std::vector<Index> &result, Index center)
    {
        Index iter = center;
        for (int i = -1; i < 2; i++)
        {
            std::get<I - 1>(iter) = std::get<I - 1>(center) +i;
            tuple_builder_t<Index, I, dimension>::build(result,  iter);
        }
    }
};

template <class Index, int dimension>
struct tuple_builder_t<Index, dimension, dimension>
{
    static std::vector<Index> build(std::vector<Index> &result, Index center)
    {
        Index iter = center;
        for (int i = -1; i < 2; i++)
        {
            std::get<dimension - 1>(iter) = std::get<dimension - 1>(center) +i;
            result.push_back(iter);
        }
    }
};

template <class Index, int dimension>
void my_function(Index index)
{
    std::vector<Index> result;
    tuple_builder_t<Index, 1, dimension>::build(result, index);
}

      



This is a neat problem, I already found myself in front of something similar (iterating over the size of the hypercube)

+2


source







All Articles