5

Let's say we have the following type

template <bool... Values>
struct foo{};

I want to create a variadic template from a constexpr array bool tab[N]. In other words, I want to do something like:

constexpr bool tab[3] = {true,false,true};
using ty1 = foo<tab[0], tab[1], tab[2]>;

But I want to do it programmatically. For now, I've tried the following:

template <std::size_t N, std::size_t... I>
auto
mk_foo_ty(const bool (&tab)[N], std::index_sequence<I...>)
{
  // error: template argument for template type parameter must be a type
  return foo<tab[I]...>{};
}

// error (see mk_foo_ty)
using ty2 = decltype(mk_ty(tab, std::make_index_sequence<3>{}));

// error: expected '(' for function-style cast or type construction
using ty3 = foo<(tab[std::make_index_sequence<3>])...>;

I'm not even sure if it's possible. Maybe resorting to something like Boost.Preprocessor, but I don't like the idea. So, does anyone have an idea? Thanks!

EDIT

I have on one side a framework of constexpr square matrices of booleans which can be created at compile-time using xor, negation, etc.

On the other side, I have a framework of templates which statically creates operations using informations encoded in variadic templates using boolean as parameters.

My goal is to bridge the gap between these two frameworks. Thus, I can't go with hardcoded solutions.

EDIT 2

I've found this question with the same problem and a nice answer, which is very close to the T.C.'s one (using a pointer). The extern linkage is also very important.

However, I realize I forgot a key element. My bool array is contained in a matrix structure to be able to overload operators ^, |, etc.:

template <std::size_t N>
struct matrix
{
  const bool data_[N*N];

  template<typename... Values>
  constexpr matrix(Values... values) noexcept
    : data_{static_cast<bool>(values)...}
  {}

  constexpr bool operator [](std::size_t index) const noexcept
  {
    return data_[index];
  }
}

Thus, if we apply T.C's solution:

template<std::size_t N, const bool (&Tab)[N], class>
struct ty1_helper;

template<std::size_t N, const bool (&Tab)[N], std::size_t... Is>
struct ty1_helper<N, Tab, std::index_sequence<Is...>>
{
  using type = foo<Tab[Is]...>;
};

template<std::size_t N, const bool (&Tab)[N]>
using ty1 = typename ty1_helper<N, Tab, std::make_index_sequence<N>>::type;

Compilers complain about passing a non-type parameter :

// error: non-type template argument does not refer to any declaration
//        using t = make_output_template<m.data_, std::make_index_sequence<3>>;
//                                       ^~~~~~~
using t = ty1<3, m.data_>;
Community
  • 1
  • 1
Alexandre Hamez
  • 7,725
  • 2
  • 28
  • 39
  • How far do you want to push this? Is it OK to hardcode `tab` and its size or do you want something more general? – T.C. Dec 21 '14 at 13:36
  • 2
    http://coliru.stacked-crooked.com/a/1ed4f214047febac – T.C. Dec 21 '14 at 13:50
  • Which language are you using? C++11 or C++14? (It looks distinctly like the latter, to me...) – Lightness Races in Orbit Dec 21 '14 at 13:59
  • Are you looking for [the indices trick](http://loungecpp.wikidot.com/tips-and-tricks:indices)? – Lightness Races in Orbit Dec 21 '14 at 14:00
  • @LightnessRacesinOrbit I use C++14 in this example to simplify the creation of indices, but it could be in C++11 using the appropriate replacement. – Alexandre Hamez Dec 21 '14 at 14:37
  • @DietmarKühl As I have written in my last edit, I need to apply xor, and, etc. operations on matrices, which is so much easier on plain old arrays (or std::array) than on variadic templates. – Alexandre Hamez Dec 21 '14 at 14:38
  • @AlexandreHamez Huh? Xor/and'ing two bool parameter packs is quite straightforward. – T.C. Dec 21 '14 at 23:41
  • @T.C. Yeah, I know. However, I simplified the question to have only one parameter pack, but the real type contains one parameter pack of boolean parameter packs.Moreover, the real problem is that I want to make it accessible to people who are not familiar with C++. This is why I want to go with plain-old matrices. – Alexandre Hamez Dec 22 '14 at 06:33
  • You can't bind subobjects to template reference parameters, but you can bind the entire `matrix`. http://coliru.stacked-crooked.com/a/9eae771f24581b7d – T.C. Dec 22 '14 at 06:49
  • @T.C. Thank you, I didn't know it was possible to bind a type like this. Thus, it's exactly what I was looking for! I you post an answer, I will gladly accept it! – Alexandre Hamez Dec 22 '14 at 10:22

1 Answers1

2

The way I did it in the comments above, using a global constexpr variable with external linkage (necessary due to GCC's nonconforming external linkage requirement, see bug 52036), would probably blow up spectacularly at link time if you put it in a header and include the header in different translation units. A solution that's good for one translation unit only isn't much of a solution. One workaround is to store the matrix as a static data member of a class.

struct matrix_holder {
    static constexpr matrix<2> mat = {true, false, true, false};
};

template<std::size_t N, const matrix<N> &Mat, class> 
struct ty1_helper;

template<std::size_t N, const matrix<N> &Mat, std::size_t... Is>
struct ty1_helper<N, Mat, std::index_sequence<Is...>> { 
    using type = foo<Mat[Is]...>; 
};

template<std::size_t N, const matrix<N> &Mat>
using ty1 = typename ty1_helper<N, Mat, std::make_index_sequence<N*N>>::type;

static_assert(std::is_same<ty1<2, matrix_holder::mat>,
                           foo<true, false, true, false>>::value, "Oops");

Demo. Also, since using matrix_holder::mat in ty1<2, matrix_holder::mat> counts as an odr-use, to be fully conforming you should supply a definition:

constexpr matrix<2> matrix_holder::mat;
T.C.
  • 133,968
  • 17
  • 288
  • 421