Defining a template package from multiple inheritance

Consider this code:

enum Brand {a,b,c,d,e,f,g,h,i,j};

template <Brand, int> struct A {};

struct B : A<a,4>, A<d,0>, A<i,3> {};
struct C : A<b,0>, A<c,5>, A<e,1>, A<h,4>, A<j,0> {};

template <typename, Brand...> void foo() {}

int main() {
    foo<B, a,d,i>();
    foo<C, b,c,e,h,j>();
}

      

foo<B, a,d,i>();

only makes sense in my (real) program if it has a,d,i

as parameters due to multiple inheritance B. However a,d,i

must be inferred in some way, otherwise there will be maintenance problems if I ever change inheritance B. Same can be said aboutfoo<C, b,c,e,h,j>();

I am stuck writing template <typename T> struct GetBrands

to get the outputted batch of items Brand

from T

. Any help would be greatly appreciated. Now let's say the highest int value in is 10.

+3


source to share


2 answers


What if we turned the problem around on our head and (slightly) changed the way we write class definitions B

and C

?

Below I borrowed the test code from your answer:



#include <iostream>

enum Brand {a,b,c,d,e,f,g,h,i,j};

template<Brand, int> struct A { };


template<typename...> struct A_base;
template<Brand... Brands, int... Is> struct A_base<A<Brands, Is>...> : A<Brands, Is>... { };


struct B : A_base<A<a,4>, A<d,0>, A<i,3>> { };
struct C : A_base<A<b,0>, A<c,5>, A<e,1>, A<h,4>, A<j,0>> { };

//template<typename, Brand...> void foo() { }

// *Overloading* foo in order to test the outputs.
template<typename, Brand x, Brand y, Brand z>
void foo() { std::cout << x << ' ' << y << ' ' << z << '\n'; }

template<typename, Brand x, Brand y, Brand z, Brand u, Brand v>
void foo() { std::cout << x << ' ' << y << ' ' << z << ' ' << u << ' ' << v << '\n'; }


template<typename S, Brand... Brands, int... Is> void foo_helper(A_base<A<Brands, Is>...>) 
{ 
    foo<S, Brands...>();
}

template<typename S> void bar() { foo_helper<S>(S()); }


int main()
{
    bar<B>();  // Supposed to be the same as foo<B, a,d,i>();  // 0 3 8
    bar<C>();  // Supposed be the same as foo<C, b,c,e,h,j>();  // 1 2 4 7 9
}

      

I think this could be adapted to handle the more general cases that you need.

+1


source


Ok, here is my second solution to replace the first one. By introducing a base class A, I now allowed the algorithm to work no matter how many parameters of template A (for now, we only focus on the first component A). The solution is now shorter too.

#include <iostream>
#include <type_traits>

enum Brand {a,b,c,d,e,f,g,h,i,j, FirstBrand = a, LastBrand = j};

template <Brand> struct HigherBase {};
template <Brand X, int> struct A : HigherBase<X> {};

struct B : A<a,4>, A<d,0>, A<i,3> {};
struct C : A<b,0>, A<c,5>, A<e,1>, A<h,4>, A<j,0> {};

template <typename T, Brand Current, typename Output> struct GetBrandsHelper;

template <typename T, Brand Current, template <Brand...> class P, Brand... Output>
struct GetBrandsHelper<T, Current, P<Output...>> :
    std::conditional<std::is_base_of<HigherBase<Current>, T>::value,
        GetBrandsHelper<T, static_cast<Brand>(Current + 1), P<Output..., Current>>,
        GetBrandsHelper<T, static_cast<Brand>(Current + 1), P<Output...>>
    >::type {};

template <typename Type> struct Identity { using type = Type; };

template <typename T, template <Brand...> class P, Brand... Output>
struct GetBrandsHelper<T, LastBrand, P<Output...>> :
    std::conditional<std::is_base_of<HigherBase<LastBrand>, T>::value,
        Identity<P<Output..., LastBrand>>,
        Identity<P<Output...>>
    >::type {};

template <Brand...> struct FooPack {};

template <typename T>
struct GetBrands : GetBrandsHelper<T, FirstBrand, FooPack<>> {};

//template <typename, Brand...>
//void foo() {}

// Specializing foo in order to test the outputs.
template <typename, Brand x, Brand y, Brand z>
void foo() {std::cout << x << ' ' << y << ' ' << z << '\n';}

template <typename, Brand x, Brand y, Brand z, Brand u, Brand v>
void foo() {std::cout << x << ' ' << y << ' ' << z << ' ' << u << ' ' << v << '\n';}

template <typename T, Brand... Is>
void fooHelper (const FooPack<Is...>&) {
    foo<T, Is...>();
}

template <typename T>
void bar() {
    fooHelper<T> (typename GetBrands<T>::type());
}

int main() {
    bar<B>();  // Supposed to be the same as foo<B, a,d,i>();  // 0 3 8
    bar<C>();  // Supposed be the same as foo<C, b,c,e,h,j>();  // 1 2 4 7 9
}

      

Some generalizations are needed:



1) What if items are allowed Brand

?

2) What if it A

has N template parameters and we want to do the above for any of N components? What would be the general algorithm for handling this in terms of N and the specified position of the P component (the above solution is a special case of N = 2, P = 0)? Thus, in case we want to do this for all N components A

, we will use the same algorithm, but just change the value of P each time.

0


source







All Articles