Search code examples
c++c++11template-meta-programmingenable-if

How to detect if a type is one of a list of generic types


If I have

template <typename T> struct A;
template <typename T> struct B;
template <typename T> struct C;
template <typename T> struct D;

what is the most compact way of testing if some candidate type X is one of them? I'm looking for something like

boost::enable_if< is_instantiation_of_any<X,A,B,C,D> >

but A,B,C and D are templates so I'm not sure how to construct the above.


Solution

  • Not sure if there exists a std::is_instantiation_of, though if all templates have same number of parameters, it is straightforward to implement it (if they don't it is more complicated). To check if a type is an instantiation of any of given templates you just need to fold it (requires C++17):

    #include<iostream>
    #include<type_traits>
    
    
    template <typename T> struct A;
    template <typename T> struct B;
    template <typename T> struct C;
    template <typename T> struct D;
    
    
    template <typename T,template<typename> typename X>
    struct is_instantiation_of : std::false_type {};
    
    template <typename A,template<typename> typename X>
    struct is_instantiation_of<X<A>,X> : std::true_type {};
    
    template <typename T,template<typename> typename...X>
    struct is_instantiation_of_any {
        static const bool value = ( ... || is_instantiation_of<T,X>::value);
    };
    
    int main(){
        std::cout << is_instantiation_of< A<int>, A>::value;
        std::cout << is_instantiation_of< A<double>, B>::value;
        std::cout << is_instantiation_of_any< A<int>,A,B>::value;
    }
    

    Output:

    101
    

    To get a C++11 compliant solution, we can use this neat trick from one of Jarod42s answers:

    template <bool ... Bs>
    using meta_bool_and = std::is_same<std::integer_sequence<bool, true, Bs...>,
                                       std::integer_sequence<bool, Bs..., true>>;
    

    Its rather clever, true,a,b,c and a,b,c,true are only the same when a, b and c are all true. std::integer_sequence is C++14, but all we need here is a type that has the bools as part of its definition:

    namespace my {
        template <typename T,T ...t>
        struct integer_sequence {};
    }
    

    Using that we can rewrite the above to:

    template <bool ... Bs>
    using my_all = std::is_same<my::integer_sequence<bool, true, Bs...>,
                                my::integer_sequence<bool, Bs..., true>>;
    

    And as "ANY(a,b,c,d,...)" is just "! ALL( !a, !b, !c, !d,...)" we can use:

    template <bool ... Bs>
    struct my_any { static constexpr bool value = ! my_all< ! Bs...>::value; };
    

    to write is_instantiation_of_any in a C++11 friendly way:

    template <typename T,template<typename> typename...X>
    struct is_instantiation_of_any {
        static const bool value = my_any< is_instantiation_of<T,X>::value ...>::value;
    };
    

    Complete C++11 example