c++metaprogrammingboost-mpl

Can I use metaprogramming to transform a list of types into a new type that has specific implicit-conversion behavior to each type in the list?


I have a boost::mpl::vector containing several types, e.g.

typedef boost::mpl::vector<T1, T2, T3, T4> list_type;

For some known types T1, T2, T3, T4. Is there any way to use metaprogramming to transform this list into a type that represents direct linear inheritance, in the order of the types in the vector? I would like to synthesize a type that has the behavior that would be consistent with this:

struct T1 { };
struct T2 : T1 { };
struct T3 : T2 { };
struct T4 : T3 { };
struct synthesized_type : T4 { };

There is boost::mpl::inherit_linearly that gives me similar behavior, but not exactly what I want. The type that results from using that behaves more like:

struct synthesized_type : T1, T2, T3, T4 { };

which behaves differently in some cases. For instance, say you have a function that is overloaded on some subset of the types T1, T2, T3, T4:

void foo(T1) { }
void foo(T3) { }

foo(synthesized_type()); // I would like to be able to do this

With the first (desired) hierarchy that I gave above, there are no ambiguities in overload resolution; I can pass synthesized_type to foo() and it will invoke foo(T3), because T3 is the most derived type in the ancestry of synthesized_type.

In the case where I use boost::mpl::inherit_linearly, however, this results in a compiler error due to ambiguity in overload resolution, because there is no priority specified between the parent types; the compiler can't choose between foo(T1) and foo(T3).

Is there any way to get the effect that I want? Stated more formally:

Given a list of types, I would like to use metaprogramming to synthesize some type that has the properties of the first hierarchy I described above (namely, that the synthesized type is implicitly convertible to each type T1, T2, T3, T4, with priority in that order, so T4 is preferred to T3, which is preferred to T2, and so on). Is this possible?


Solution

  • If your types are all CRTP types you can do this sorcery:

    #include <iostream>
    
    template< template<class> class... Ts >
    struct TVec { };
    
    struct EmptyClass { };
    
    template< class T >
    struct Inheritor;
    
    template< template<template<class> class> class U, template<class> class T>
    struct Inheritor< U<T> >
      : public T<EmptyClass> { };
    
    template< template<template<class> class...> class U, template<class> class T, template<class> class... Ts>
    struct Inheritor< U<T, Ts... > >
      : public T<Inheritor< U<Ts...> > > { } ;
    
    template< typename Base >
    struct T1
      : public Base { };
    
    template< typename Base >
    struct T2
      : public Base { };
    
    template< typename Base >
    struct T3 
      : public Base { };
    
    template<typename X>
    void foo(T1<X> t) {
      std::cout << "T1" << std::endl;
    }
    
    template<typename X>
    void foo(T2<X> t) {
      std::cout << "T2" << std::endl;
    }
    
    template<typename X>
    void foo(T3<X> t) {
      std::cout << "T3" << std::endl;
    }
    
    int main() {
      using Types = TVec< T1, T2, T3 >;
      using Types2 = TVec< T3, T2, T1 >;
    
      using Derived = Inheritor<Types>;
      using Derived2 = Inheritor<Types2>;
    
      //Inheritor<TVec< T1, T2, T3>> x;
      Derived x;
      Derived2 x2;
    
      foo(x); // T1 overload
      foo(x2); // T3 overload
    }
    

    You can't get that exact behavior, since it would involve redefining T1, T2... Is this close enough?

    template< class T >
    struct Inheritor { };
    
    template< class T, template<class> class U >
    struct Inheritor< U<T> >
      : public T { };
    
    template< template<class...> class U, class T, class... Ts >
    struct Inheritor< U<T, Ts...> >
      : public T
      , public Inheritor< U< Ts... > > { };