Search code examples
c++c++11templatesvariadic-templatestemplate-meta-programming

Compare two sets of types for equality


How can one check if two parameter packs are the same, ignoring their internal order?

So far I only have the frame (using std::tuple), but no functionality.

#include <tuple>
#include <type_traits>

template <typename, typename>
struct type_set_eq : std::false_type
{
};

template <typename ... Types1, typename ... Types2>
struct type_set_eq<std::tuple<Types1...>, std::tuple<Types2...>>
    : std::true_type
{
    // Should only be true_type if the sets of types are equal
};

int main() {
    using t1 = std::tuple<int, double>;
    using t2 = std::tuple<double, int>;
    using t3 = std::tuple<int, double, char>;

    static_assert(type_set_eq<t1, t1>::value, "err");
    static_assert(type_set_eq<t1, t2>::value, "err");
    static_assert(!type_set_eq<t1, t3>::value, "err");
}

Every type is not allowed to occur more than once in a set.


Solution

  • If types in tuples are unique you could make use of inheritance to answer if all types from the first tuple are involved as a base of the helper struct. E.g. (C++11 approach):

    #include <tuple>
    #include <type_traits>
    
    template <class T>
    struct tag { };
    
    template <class... Ts>
    struct type_set_eq_helper: tag<Ts>... { };
    
    template <class, class, class = void>
    struct type_set_eq: std::false_type { };
    
    template <bool...>
    struct bool_pack { };
    
    template <bool... Bs>
    using my_and = std::is_same<bool_pack<Bs..., true>, bool_pack<true, Bs...>>;
    
    template <class... Ts1, class... Ts2>
    struct type_set_eq<std::tuple<Ts1...>, std::tuple<Ts2...>, typename std::enable_if< (sizeof...(Ts1) == sizeof...(Ts2)) && my_and< std::is_base_of<tag<Ts2>, type_set_eq_helper<Ts1...>>::value...  >::value  >::type  >:
       std::true_type { };
    
    int main() {
        using t1 = std::tuple<int, double>;
        using t2 = std::tuple<double, int>;
        using t3 = std::tuple<int, double, char>;
    
        static_assert(type_set_eq<t1, t1>::value, "err");
        static_assert(type_set_eq<t1, t2>::value, "err");
        static_assert(!type_set_eq<t1, t3>::value, "err");
    }
    

    [Live demo]