0

I have to check if given two tuples one is the subset of another. I found this elegant solution Check if one set of types is a subset of the other.

But the issue with this solution is that it does not consider subtypes for example

using t1 = std::tuple<int, double>;
using t2 = std::tuple<double, int>;
using t3 = std::tuple<t1, t2>;

This would fail the subset test.

#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...> && ...);

The reason being that if we do subset on t1 and t3, the contains expression compares int with t1 which fails. So the change needed is for contains function to search subtypes.

P.S This code would only work in C++17

coder
  • 119
  • 1
  • 2
  • 10

1 Answers1

2

You can "flatten" the nested tuples if you are not interested in the structure of tuple nesting. This would imply

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

  static_assert(
    std::is_same<
      flatten<t3>,
      std::tuple<int, double, double, int>
    >{}
  );

  static_assert(false == is_subset_of<t1, t3>);
  static_assert(true == is_subset_of< t1, flatten<t3> >);

  return 0;
}

A quick-and-dirty implementation of flatten could look like the following.

namespace detail {

template<class... Ts>
struct flatten {
  static_assert(sizeof...(Ts) == 0, "recursion break (see specializations)");

  template<class... Result>// flattened types are accumulated in this pack
  using type = std::tuple<Result...>;
};

template<template<class...> class Nester, class... Nested, class... Ts>
struct flatten<Nester<Nested...>, Ts...> {// if first arg is nested then unpack
  template<class... Result>
  using type = typename flatten<Nested..., Ts...>::template type<Result...>;
};

template<class T0, class... Ts>
struct flatten<T0, Ts...> {// if `T0` is flat then just append it to `Result...`
  template<class... Result>
  using type = typename flatten<Ts...>::template type<Result..., T0>;
};

};// detail

template<class... Ts>
using flatten = typename detail::flatten<Ts...>::template type<>;
Julius
  • 1,816
  • 10
  • 14