Check if one set of types is a subset of the other

前端 未结 9 821
甜味超标
甜味超标 2021-01-01 17:54

How can one check if one parameter pack (interpreted as a set) is a subset of another one?

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

相关标签:
9条回答
  • 2021-01-01 18:17

    is_subset_of version of an answer from your previous question:

    #include <tuple>
    #include <type_traits>
    
    template <class T>
    struct tag { };
    
    template <class... Ts>
    struct is_subset_of_helper: tag<Ts>... { };
    
    template <class, class, class = void>
    struct is_subset_of: 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 is_subset_of<std::tuple<Ts1...>, std::tuple<Ts2...>, typename std::enable_if< my_and< std::is_base_of<tag<Ts1>, is_subset_of_helper<Ts2...>>::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(is_subset_of<t1, t1>::value, "err");
        static_assert(is_subset_of<t1, t2>::value, "err");
        static_assert(is_subset_of<t2, t1>::value, "err");
        static_assert(is_subset_of<t2, t3>::value, "err");
        static_assert(!is_subset_of<t3, t2>::value, "err");
    }
    

    [live demo]

    0 讨论(0)
  • 2021-01-01 18:24

    I suppose I'll throw my hat in the ring. This is a C++11 solution like the OP asked for, I realize that C++17 has much nicer features. It's a type-only solution (no explicit static const bool or the like, only true_type and false_type, which have their own internal bool)

    The downside is that this solution forced me to implement logical_or and logical_and, which we'd get for free in C++17 in the form of conjunction and disjunction).

    Miraculously the code is a tad shorter than Maartan Barnelis' solution, though arguably less readable

    namespace detail
    {
    template<class T, class U>
    struct logical_or : std::true_type{};
    
    template<>
    struct logical_or<std::false_type, std::false_type> : std::false_type{};
    
    template<class...>
    struct logical_and : std::false_type{};
    
    template<>
    struct logical_and<std::true_type, std::true_type> : std::true_type{};
    }
    
    template<class...>
    struct contains : std::false_type{};
    
    template<class T>
    struct contains<T, T> : std::true_type{};
    
    template<class Type, class Types2Head, class... Types2>
    struct contains<Type, Types2Head, Types2...> : detail::logical_or<typename std::is_same<Type, Types2Head>::type, typename contains<Type, Types2...>::type>{};
    
    template<class...>
    struct is_subset_of : std::false_type{};
    
    template<class Type1, class... Types2>
    struct is_subset_of<std::tuple<Type1>, std::tuple<Types2...>> : contains<Type1, Types2...>{};
    
    template<class Type1Head, class... Types1, class... Types2>
    struct is_subset_of<std::tuple<Type1Head, Types1...>, std::tuple<Types2...>> : detail::logical_and<typename contains<Type1Head, Types2...>::type, typename is_subset_of<std::tuple<Types1...>, std::tuple<Types2...>>::type>{};
    

    Demo

    0 讨论(0)
  • 2021-01-01 18:29

    Not exactly what you asked but... just for fun, using std::is_base_of you can create (in C++14, at least) a constexpr function that works like your struct.

    The following is a working example (only C++14)

    #include <tuple>
    #include <iostream>
    #include <type_traits>
    
    template <typename ... Ts>
    struct foo : std::tuple<Ts>...
     { };
    
    template <typename ... Ts1, typename ... Ts2>
    bool isSubsetOf (std::tuple<Ts1...> const &, std::tuple<Ts2...> const &)
     {
       bool ret { true };
    
       using un = int[];
       using d2 = foo<Ts2...>;
    
       (void)un { (ret &= std::is_base_of<std::tuple<Ts1>, d2>::value, 0)... };
    
       return ret;
     }
    
    
    int main()
     {
        using t1 = std::tuple<int, double>;
        using t2 = std::tuple<double, int>;
        using t3 = std::tuple<int, double, char>;
    
        std::cout << isSubsetOf(t1{}, t1{}) << std::endl;  // print 1
        std::cout << isSubsetOf(t1{}, t2{}) << std::endl;  // print 1
        std::cout << isSubsetOf(t2{}, t1{}) << std::endl;  // print 1
        std::cout << isSubsetOf(t1{}, t3{}) << std::endl;  // print 1
        std::cout << isSubsetOf(t3{}, t1{}) << std::endl;  // print 0
     }
    
    0 讨论(0)
  • 2021-01-01 18:30
    constexpr bool any_of() { return false; }
    template<class...Bools>
    constexpr bool any_of( bool b, Bools... bools ) {
      return b || any_of(bools...);
    }
    constexpr bool all_of() { return true; }
    template<class...Bools>
    constexpr bool all_of( bool b, Bools...bools ) {
      return b && all_of(bools...);
    }
    template<class T0, class...Ts>
    struct contains_t : std::integral_constant<bool,
      any_of( std::is_same<T0, Ts>::value... )
    > {};
    
    template<class Tuple0, class Tuple1>
    struct tuple_subset_of;
    
    template<class...T0s, class...T1s>
    struct tuple_subset_of< std::tuple<T0s...>, std::tuple<T1s...> >:
      std::integral_constant<bool,
        all_of( contains_t<T0s, T1s...>::value... )
      >
    {};
    

    Live example.

    This is designed to permit easy improvement post C++17 -- replace any_of and all_of recursive implementations with fold expressions.

    0 讨论(0)
  • 2021-01-01 18:34

    If you can use C++17 features, I highly recommend using Piotr Skotnicki's solution!

    I had to implement this functionality a while ago. I am just going to copy-paste the code I came up with at that point.

    I am not claiming that this is the best or most elegant way to implement this kind of check! I did not bother thinking about the edge cases too much; you may need to adapt the code to fit your requirements.

    To clarify: ContainsTypes<Lhs, Rhs> checks if Rhs is a subset of Lhs.


      template <typename Tuple, typename T>
      struct ContainsType;
    
      template <typename T, typename U, typename... Ts>
      struct ContainsType<std::tuple<T, Ts...>, U>
      {
          static const bool VALUE = ContainsType<std::tuple<Ts...>, U>::VALUE;
      };
    
      template <typename T, typename... Ts>
      struct ContainsType<std::tuple<T, Ts...>, T>
      {
          static const bool VALUE = true;
      };
    
      template <typename T>
      struct ContainsType<std::tuple<>, T>
      {
          static const bool VALUE = false;
      };
    
      // -----
    
      template <typename Lhs, typename Rhs>
      struct ContainsTypes;
    
      template <typename Tuple, typename T, typename... Ts>
      struct ContainsTypes<Tuple, std::tuple<T, Ts...>>
      {
          static const bool VALUE = ContainsType<Tuple, T>::VALUE && ContainsTypes<Tuple, std::tuple<Ts...>>::VALUE;
      };
    
      template <typename Tuple>
      struct ContainsTypes<Tuple, std::tuple<>>
      {
          static const bool VALUE = true;
      };
    
    0 讨论(0)
  • 2021-01-01 18:35
    #include <tuple>
    #include <type_traits>
    
    template <typename T, typename... Ts>
    constexpr bool contains = (std::is_same<T, Ts>{} || ...);
    
    template <typename Subset, typename Set>
    constexpr bool is_subset_of = false;
    
    template <typename... Ts, typename... Us>
    constexpr bool is_subset_of<std::tuple<Ts...>, std::tuple<Us...>>
               = (contains<Ts, Us...> && ...);
    

    DEMO

    0 讨论(0)
提交回复
热议问题