4

Let's say I have a template class over one size a one type:

template<size_t N, typename T>
class C
{};

I want to generate a boost::variant which is capable of holding this class over several sizes and types, e.g. for sizes 1 and 2 and types int and unsigned int it will be

typedef boost::variant<
    C<1, int>, 
    C<1, unsigned int>, 
    C<2, int>, 
    C<2, unsigned int>
> my_variant;

The problem is that I need this setting in several places and each time with different sizes and types. Is there some template meta-programming magic to generate these variants from the list of possible values, something along the lines of

typedef generate_variant<C, 1, 2, int, unsigned int>::type my_variant;
Dvir Yitzchaki
  • 487
  • 4
  • 13
  • i think it is possible with variadic templates. – zapredelom Mar 10 '16 at 15:06
  • No. You won't be able to provide a template parameter pack which can be both integral and type. – SergeyA Mar 10 '16 at 15:08
  • maybe using std::integral_constant then? – Dvir Yitzchaki Mar 10 '16 at 15:17
  • 1
    I would approach this by wrapping all sizes in a compile-time list of integral constants, and all types in a compile-time list of types. I would generate index sequences for both lists and pass everything to an implementation struct/function. The implementation function would perform something similar to a nested for loop by expanding the index sequences one inside the other, using the indices to get the sizes/types from the compile time lists and placing them in your wrapper template class. Using some sort of fold/recursion you can append all computations to achieve your result. – Vittorio Romeo Mar 10 '16 at 15:25
  • You may need to make `C` accept types (integral constants) instead of a `size_t`, or to create an adaptor type that takes an integral constant and instantiates a `C` by getting the value of the integral constant. – Vittorio Romeo Mar 10 '16 at 15:26

1 Answers1

4

OK, I did it. Here's how:

#include <boost/variant.hpp>
#include <tuple>
#include <iostream>
#include <typeinfo>

template<size_t N, typename T>
class C
{};

template<template <size_t, class> class T>
struct combine
{
    template<size_t... Ns> struct sizes;

    template<size_t N>
    struct sizes<N>
    {
        template<typename... Types>
        struct types
        {
            typedef std::tuple<T<N, Types>...> type;
        };
    };

    template<size_t N, size_t... Ns>
    struct sizes<N, Ns...>
    {
        template<typename... Types>
        struct types
        {
            typedef typename sizes<N>::template types<Types...>::type head;
            typedef typename sizes<Ns...>::template types<Types...>::type tail;
            typedef decltype(std::tuple_cat<head, tail>(std::declval<head>(), std::declval<tail>())) type;
        };
    };
};

template<typename... Types>
auto to_variant(const std::tuple<Types...>&)
{
    return boost::variant<Types...>();
}

struct typename_visitor : public boost::static_visitor<>
{
    template<size_t N, typename T>
    void operator()(const C<N, T>& c)
    {
        std::cout << "C<" << N << ", " << typeid(T).name() << ">\n";
    }
};

int main()
{
    combine<C>::sizes<1, 2>::types<int, unsigned int>::type v;
    auto var = to_variant(v);
    var = C<1, int>();
    // var = C<3, int>(); // doesn't compile
    // var = C<1, short>(); // doesn't compile
    var.apply_visitor(typename_visitor()); // prints C<1, int>
}
Dvir Yitzchaki
  • 487
  • 4
  • 13