Ad

Compile-Time Tuple Utilities

Here we have introduced two new useful functions:

  • peel_front
  • print_tuple
  • tuple_split

These basic utilities allow us to perform compile time type manipulations and extend the stl.

peel_front will remove a single element from the front of a tuple and return it.
tuple_split takes either a type parameter to split on or a list of indexes to split on.
print_tuple takes an abitrary tuple and prints it.

template <typename... TYPES, typename INTTYPE, INTTYPE... Idxs>
constexpr auto peel_front(std::tuple<TYPES...> tuple, std::integer_sequence<INTTYPE, Idxs...>) {
  return std::make_tuple(std::get<Idxs + 1>(tuple)...);
}


template <typename FIRST, typename... TYPES>
constexpr std::tuple<TYPES...> peel_front(std::tuple<FIRST, TYPES...> val) {
  return peel_front(val, std::make_integer_sequence<int, sizeof...(TYPES)>());
}

namespace _detail {

    struct tag{};

  template <typename TYPE_TO_SPLIT_ON, typename... TYPES, typename... RESULT_TYPES>
  constexpr auto tuple_split(std::tuple<TYPES...> so_far, std::tuple<RESULT_TYPES...> result, std::tuple<>) {
    return std::tuple_cat(so_far, std::make_tuple(result));
  }
  
  template <typename TYPE_TO_SPLIT_ON, typename FIRST, typename... TYPES, typename... TYPES2, typename... RESULT_TYPES>
  constexpr auto tuple_split(std::tuple<TYPES...> so_far, std::tuple<RESULT_TYPES...> result, std::tuple<FIRST, TYPES2...> to_process) {
    if constexpr(std::is_same_v<FIRST, TYPE_TO_SPLIT_ON>) {
      return tuple_split<TYPE_TO_SPLIT_ON>(std::tuple_cat(so_far, std::make_tuple(result)), std::tuple<>{}, peel_front(to_process));
    }
    else {
      return tuple_split<TYPE_TO_SPLIT_ON>(so_far, std::tuple_cat(result, std::make_tuple(std::get<0>(to_process))), peel_front(to_process));
    }
  }
  
  template <typename INTTYPE, INTTYPE... IdxsLeft, INTTYPE... IdxsRight, typename... TYPES>
  constexpr auto tuple_split(std::tuple<TYPES...> tup, std::integer_sequence<INTTYPE, IdxsLeft...>, std::integer_sequence<INTTYPE, IdxsRight...>) {
    std::cout << (( std::to_string(IdxsRight) + " " ) + ...) << std::endl;
    return std::make_tuple(std::make_tuple(std::get<IdxsLeft>(tup)...), std::make_tuple(std::get<IdxsRight + sizeof...(IdxsLeft) + 1>(tup)...));
  }

  template <int FIRST, typename... TYPES>
  constexpr auto tuple_split(std::tuple<TYPES...> tup, tag) {
    const auto& [left, right] = tuple_split(tup, std::make_integer_sequence<int, FIRST - 1>(), std::make_integer_sequence<int, sizeof...(TYPES) - FIRST>());
    return std::tuple_cat(std::make_tuple(left), std::make_tuple(right));
  }
  
  template <int FIRST, int ...REST, typename... TYPES, typename = std::enable_if_t<(sizeof...(REST) > 0)>>
  constexpr auto tuple_split(std::tuple<TYPES...> tup, tag) {
    static_assert(sizeof...(REST) > 0, "Must have at least one element in the parameter pack!");
    const auto& [left, right] = tuple_split(tup, std::make_integer_sequence<int, FIRST - 1>(), std::make_integer_sequence<int, sizeof...(TYPES) - FIRST>());
    return std::tuple_cat(std::make_tuple(left), tuple_split<(REST - FIRST)...>(right, tag{}));
  }
}

namespace std{

template <typename TYPE_TO_SPLIT_ON, typename... TYPES>
constexpr auto tuple_split(std::tuple<TYPES...> tuple) {
  return _detail::tuple_split<TYPE_TO_SPLIT_ON>(std::tuple<>{}, std::tuple<>{}, tuple);
} 
  
template <int ...Idxs, typename... TYPES>
constexpr auto tuple_split(std::tuple<TYPES...> tuple) {
  static_assert((( Idxs >= 0) && ...), "All indexes should be positive!");
  return _detail::tuple_split<Idxs...>(tuple, _detail::tag{});
} 
  
}


template <typename... TYPES, typename INTTYPE, INTTYPE ...Idxs>
inline std::ostream& print_tuple(std::ostream& os, std::tuple<TYPES...> tup, std::integer_sequence<INTTYPE, Idxs...>) {
    os << "std::tuple<";
    int dummy1[] = { ( (os << (Idxs == 0 ? "" : ", ") << typeid(std::get<Idxs>(tup)).name()) ,0)... }; 
    os << ">(";
    int dummy2[] = { ( (os << (Idxs == 0 ? "" : ", ") << std::get<Idxs>(tup)),0)... }; 
    os << ")";
    return os;
}

template <typename... TYPES>
inline std::ostream& operator<<(std::ostream& os, std::tuple<TYPES...> tup) {
    return print_tuple(os, tup, std::make_integer_sequence<int, sizeof...(TYPES)>());
}