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.
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]
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
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
}
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.
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;
};
#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