Initializing variadic-template-constructed tuple of types with constructor requiring an argument

60 Views Asked by At

I apologize already having asked a similar question here when I should have modified the question I asked first.

I have the class Level which constructor requires an argument. Using variadic templates I create a type LevelType for a member of the class Tree which is a tuple of elements of type Level.

How can I avoid repeating the value data in the initialization list of Tree?

As a further constraint, it should be possible for objects of class Tree to be constructed at compile time, so as far as I know I need some kind of template metaprogramming-fu.

Thank you!

Here's a code snippet:

#include<utility>
#include<array>
#include<iostream>

class LevelData
{};

template <typename LevelType>
class Level
{            
    public:
    Level(LevelData & data_):
    data(data_)
    {};

    LevelData & data ;
};

template <std::size_t nLevels,typename LevelType>
class Tree
{            
    public:
    Tree(LevelData & data):
    level(data,data,data,data,data)
    {};

    LevelType level ;                                                                                                                                        
};

template <std::size_t nLevels,auto... Is>
auto make_tree_impl(std::index_sequence<Is...>)
    -> Tree<nLevels,std::tuple<Level<std::array<double, std::size_t{1} << Is>>...>>;

template <std::size_t nLevels>
using make_tree = decltype(make_tree_impl<nLevels>(std::make_index_sequence<nLevels>{}));

int main()
{
    const unsigned int nLevels = 5;
    LevelData data;
    auto tree = make_tree<nLevels>(data);
    std::cout << std::is_same<decltype(tree),
    Tree<nLevels,std::tuple<Level<std::array<double,1>>,
                            Level<std::array<double,2>>,
                            Level<std::array<double,4>>,
                            Level<std::array<double,8>>,
                            Level<std::array<double,16>>>>>::value << std::endl ;
    return 0;
}
2

There are 2 best solutions below

0
Ted Lyngmo On BEST ANSWER

You could make use of make_index_sequence again, only this time it's used to repeat data as many times as you have types in your tuple.

Unfolding can be done by using (static_cast<void>(Is), data)... since the left part of the , operator will be discarded, leaving only data, sizeof...(Is) number of times.

template <std::size_t nLevels, typename LevelType>
class Tree {
public:
    Tree(LevelData& data)
        : level{[&]<std::size_t... Is>(std::index_sequence<Is...>) {
              return LevelType{(static_cast<void>(Is), data)...};
          }(std::make_index_sequence<std::tuple_size_v<LevelType>>())} {}

    LevelType level;
};
5
n. m. could be an AI On

Perhaps you can use this tuple wrapper:

// Like a tuple but has a one-argument ctor
// that replicates its arg to all tuple elements
template <class ... T>
struct tuple_wrapper : public std::tuple<T...>
{
  template <class P>
  tuple_wrapper(P&& p) : std::tuple<T...>((static_cast<std::void_t<T>>(0), 
                                           std::forward<P>(p))...) {}
  using std::tuple<T...>::tuple;
};

(I personally hate the hoops I have to jump through every time I need to use std::index_sequence).