Skip to content

Instantly share code, notes, and snippets.

@glukianets
Created June 1, 2020 12:59
Show Gist options
  • Save glukianets/312aa6ec33a13cdac03bb25bb416bd9f to your computer and use it in GitHub Desktop.
Save glukianets/312aa6ec33a13cdac03bb25bb416bd9f to your computer and use it in GitHub Desktop.
common functional* operations on C++ tuples
#include <iostream>
#include <type_traits>
#include <tuple>
#include <limits>
#include <functional>
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
template<typename T, T ... V>
using IntegerSequence = std::integer_sequence<T, V...>;
template<typename T, T N>
using MakeIntegerSequence = std::make_integer_sequence<T, N>;
template<typename>
struct IntegerSequenceType;
template<typename T, T ... V>
struct IntegerSequenceType<IntegerSequence<T, V...>> {
using type = T;
};
template<typename SequenceT>
using IntegerSequenceTypeT = typename IntegerSequenceType<SequenceT>::type;
template<typename T, auto A, T ... V>
auto _add_to_integer_sequence(IntegerSequence<T, V...>) -> IntegerSequence<T, A + V...> { return IntegerSequence<T, A + V...>{}; };
template<typename SeqT, auto A>
using AddToIntegerSequence = decltype(_add_to_integer_sequence<IntegerSequenceTypeT<SeqT>, A>(std::declval<SeqT>()));
template<typename T, T I, T L>
using MakeIntegerRange = AddToIntegerSequence<MakeIntegerSequence<T, L>, I>;
////////////////////////////////////////////////////////////////////////////////////////////////////
template<size_t ... V>
using IndexSequence = IntegerSequence<size_t, V...>;
template<size_t N>
using MakeIndexSequence = MakeIntegerSequence<size_t, N>;
template<typename SeqT, auto A>
using AddToIndexSequence = AddToIntegerSequence<SeqT, A>;
template<size_t I, size_t L>
using MakeIndexRange = MakeIntegerRange<size_t, I, L>;
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
template<typename TupleT>
constexpr size_t TupleSize = std::tuple_size_v<std::decay_t<TupleT>>;
////////////////////////////////////////////////////////////////////////////////////////////////////
template<typename CallableT, typename ... TuplesT>
constexpr decltype(auto) apply(CallableT &&callable, TuplesT && ... tuples) {
return std::apply(std::forward<CallableT>(callable), std::tuple_cat(std::forward<TuplesT>(tuples)...));
};
template<typename T, typename ... TuplesT>
constexpr T make(TuplesT && ... tuples) {
return std::make_from_tuple<T>(std::tuple_cat(std::forward<TuplesT>(tuples)...));
};
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
template<size_t ... I, typename TupleT>
constexpr decltype(auto) takeIndices(TupleT &&tuple, IndexSequence<I...> = IndexSequence<I...>()) {
static_assert(((I < TupleSize<TupleT>) && ...));
return std::tuple(std::get<I>(std::forward<TupleT>(tuple))...);
}
template<size_t I, size_t L, typename TupleT>
constexpr decltype(auto) takeRange(TupleT &&tuple) {
static_assert(I + L < TupleSize<TupleT>);
return takeIndices(std::forward<TupleT>(tuple), MakeIndexRange<I, L>{});
};
template<size_t N = 1, typename TupleT>
constexpr decltype(auto) takeLast(TupleT &&tuple) {
constexpr size_t size = TupleSize<TupleT>;
static_assert(N <= size);
return takeRange<size - N, N>(std::forward<TupleT>(tuple));
};
template<size_t N = 1, typename TupleT>
constexpr decltype(auto) takeFirst(TupleT &&tuple) {
constexpr size_t size = TupleSize<TupleT>;
static_assert(N <= size);
return takeRange<0, N>(std::forward<TupleT>(tuple));
};
////////////////////////////////////////////////////////////////////////////////////////////////////
template<size_t I = 0, size_t L = std::numeric_limits<size_t>::max(), typename AccT, typename TupleT, typename CallableT>
constexpr decltype(auto) foldLeft(AccT &&acc, TupleT &&tuple, CallableT &&callable, std::enable_if_t<(I >= std::min(L, TupleSize<TupleT>)), std::nullptr_t> = nullptr) {
return std::forward<AccT>(acc);
};
template<size_t I = 0, size_t L = std::numeric_limits<size_t>::max(), typename AccT, typename TupleT, typename CallableT>
constexpr decltype(auto) foldLeft(AccT &&acc, TupleT &&tuple, CallableT &&callable, std::enable_if_t<(I < std::min(L, TupleSize<TupleT>)), std::nullptr_t> = nullptr) {
return callable(foldLeft<I + 1, L>(std::forward<AccT>(acc), std::forward<TupleT>(tuple), std::forward<CallableT>(callable)), std::get<TupleSize<TupleT> - I - 1>(tuple));
};
////////////////////////////////////////////////////////////////////////////////////////////////////
template<size_t I = 0, size_t L = std::numeric_limits<size_t>::max(), typename AccT, typename TupleT, typename CallableT>
constexpr decltype(auto) foldRight(AccT &&acc, TupleT &&tuple, CallableT &&callable, std::enable_if_t<(I >= std::min(L, TupleSize<TupleT>)), std::nullptr_t> = nullptr) {
return std::forward<AccT>(acc);
};
template<size_t I = 0, size_t L = std::numeric_limits<size_t>::max(), typename AccT, typename TupleT, typename CallableT>
constexpr decltype(auto) foldRight(AccT &&acc, TupleT &&tuple, CallableT &&callable, std::enable_if_t<(I < std::min(L, TupleSize<TupleT>)), std::nullptr_t> = nullptr) {
return callable(foldRight<I + 1, L>(std::forward<AccT>(acc), std::forward<TupleT>(tuple), std::forward<CallableT>(callable)), std::get<I>(tuple));
};
////////////////////////////////////////////////////////////////////////////////////////////////////
template<size_t ... I, typename TupleT, typename CallableT>
constexpr decltype(auto) mapIndices(TupleT &&tuple, CallableT &&callable, IndexSequence<I...> = IndexSequence<I...>{}) {
static_assert(((I < TupleSize<TupleT>) && ...));
return std::tuple(callable(std::get<I>(std::forward<TupleT>(tuple)))...);
};
template<size_t I, size_t L, typename TupleT, typename CallableT>
constexpr decltype(auto) mapRange(TupleT &&tuple, CallableT &&callable) {
static_assert(I + L <= TupleSize<TupleT>);
return mapIndices(std::forward<TupleT>(tuple), std::forward<CallableT>(callable), MakeIndexRange<I, L>{});
}
template<typename TupleT, typename CallableT>
constexpr decltype(auto) map(TupleT &&tuple, CallableT &&callable) {
return mapRange<0, TupleSize<TupleT>>(std::forward<TupleT>(tuple), std::forward<CallableT>(callable));
};
////////////////////////////////////////////////////////////////////////////////////////////////////
template<size_t ... I, typename TupleT, typename CallableT>
constexpr decltype(auto) flatMapIndices(TupleT &&tuple, CallableT &&callable, IndexSequence<I...> = IndexSequence<I...>{}) {
static_assert(((I < TupleSize<TupleT>) && ...));
return std::tuple_cat(callable(std::get<I>(std::forward<TupleT>(tuple)))...);
};
template<size_t I, size_t L, typename TupleT, typename CallableT>
constexpr decltype(auto) flatMapRange(TupleT &&tuple, CallableT &&callable) {
static_assert(I + L <= TupleSize<TupleT>);
return flatMapIndices(std::forward<TupleT>(tuple), std::forward<CallableT>(callable), MakeIndexRange<I, L>{});
}
template<typename TupleT, typename CallableT>
constexpr decltype(auto) flatMap(TupleT &&tuple, CallableT &&callable) {
return flatMapRange<0, TupleSize<TupleT>>(std::forward<TupleT>(tuple), std::forward<CallableT>(callable));
};
////////////////////////////////////////////////////////////////////////////////////////////////////
template<size_t ... I, typename TupleT>
constexpr decltype(auto) flattenIndices(TupleT &&tuple, IndexSequence<I...> = IndexSequence<I...>{}) {
static_assert(((I < TupleSize<TupleT>) && ...));
return std::tuple_cat(std::get<I>(std::forward<TupleT>(tuple))...);
};
template<size_t I, size_t L, typename TupleT>
constexpr decltype(auto) flattenRange(TupleT &&tuple) {
static_assert(I + L <= TupleSize<TupleT>);
return flattenIndices(std::forward<TupleT>(tuple), MakeIndexRange<I, L>{});
}
template<typename TupleT>
constexpr decltype(auto) flatten(TupleT &&tuple) {
return flattenRange<0, TupleSize<TupleT>>(std::forward<TupleT>(tuple));
};
////////////////////////////////////////////////////////////////////////////////////////////////////
template<size_t I, typename CallableT, typename ... TuplesT>
constexpr decltype(auto) _zipHelper1(CallableT &&callable, TuplesT && ... tuples) {
return std::forward<CallableT>(callable)(std::get<I>(std::forward<TuplesT>(tuples))...);
};
template<size_t ... I, typename CallableT, typename ... TuplesT>
constexpr decltype(auto) _zip(CallableT &&callable, IndexSequence<I...>, TuplesT && ... tuples) {
return std::tuple(_zipHelper1<I>(callable, std::forward<TuplesT>(tuples)...)...);
};
template<typename CallableT, typename ... TuplesT>
constexpr decltype(auto) zipShortest(CallableT &&callable, TuplesT && ... tuples) {
constexpr size_t size = std::min({ TupleSize<TuplesT>... });
return _zip(std::forward<CallableT>(callable), MakeIndexSequence<size>{}, std::forward<TuplesT>(tuples)...);
};
template<typename CallableT, typename TupleT, typename ... TuplesT>
constexpr decltype(auto) zip(CallableT &&callable, TupleT && tuple, TuplesT && ... tuples) {
static_assert(((TupleSize<TuplesT> == TupleSize<TupleT>) && ... ));
return zipShortest(std::forward<CallableT>(callable), std::forward<TupleT>(tuple), std::forward<TuplesT>(tuples)...);
};
////////////////////////////////////////////////////////////////////////////////////////////////////
template<size_t ... I, typename TupleT, typename CallableT>
constexpr decltype(auto) allAtIndices(TupleT &&tuple, CallableT &&callable, IndexSequence<I...> = IndexSequence<I...>{}) {
return (callable(std::get<I>(std::forward<TupleT>(tuple))) && ...);
};
template<size_t I, size_t L, typename TupleT, typename CallableT>
constexpr decltype(auto) allInRange(TupleT &&tuple, CallableT &&callable) {
return allAtIndices(std::forward<TupleT>(tuple), std::forward<CallableT>(callable), MakeIndexRange<I, L>{});
};
template<typename TupleT, typename CallableT>
constexpr decltype(auto) all(TupleT &&tuple, CallableT &&callable) {
return allAtIndices(std::forward<TupleT>(tuple), std::forward<CallableT>(callable), MakeIndexSequence<TupleSize<TupleT>>{});
};
////////////////////////////////////////////////////////////////////////////////////////////////////
template<size_t ... I, typename TupleT, typename CallableT>
constexpr decltype(auto) anyAtIndices(TupleT &&tuple, CallableT &&callable, IndexSequence<I...> = IndexSequence<I...>{}) {
return (callable(std::get<I>(std::forward<TupleT>(tuple))) || ...);
};
template<size_t I, size_t L, typename TupleT, typename CallableT>
constexpr decltype(auto) anyInRange(TupleT &&tuple, CallableT &&callable) {
return anyAtIndices(std::forward<TupleT>(tuple), std::forward<CallableT>(callable), MakeIndexRange<I, L>{});
};
template<typename TupleT, typename CallableT>
constexpr decltype(auto) any(TupleT &&tuple, CallableT &&callable) {
return anyAtIndices(std::forward<TupleT>(tuple), std::forward<CallableT>(callable), MakeIndexSequence<TupleSize<TupleT>>{});
};
////////////////////////////////////////////////////////////////////////////////////////////////////
template<typename LTupleT, typename RTupleT>
constexpr auto equal(LTupleT &&lhs, RTupleT &&rhs) -> std::enable_if_t<TupleSize<LTupleT> == TupleSize<RTupleT>, bool> {
return all(zip([](auto &&l, auto &&r) { return l == r; }, std::forward<LTupleT>(lhs), std::forward<RTupleT>(rhs)), [](auto &&v) { return v; });
};
template<typename LTupleT, typename RTupleT>
constexpr auto equal(LTupleT &&lhs, RTupleT &&rhs) -> std::enable_if_t<TupleSize<LTupleT> != TupleSize<RTupleT>, bool> {
return false;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
int main(int argc, char **argv) {
constexpr auto tuple = std::make_tuple(0, 1, 2, 3, 4);
constexpr auto deepTuple = std::make_tuple(std::make_tuple(0, 1), std::make_tuple(2, 3), std::make_tuple(4));
static_assert(10 == foldLeft(0, tuple, std::plus{}));
static_assert(10 == foldRight(0, tuple, std::plus{}));
static_assert(equal(std::tuple(1, 2, 3, 4, 5), map(tuple, [](auto &&v) { return v + 1; })));
static_assert(equal(std::tuple(2, 3, 4), mapRange<2, 3>(tuple, [](auto &&v) { return v; })));
static_assert(equal(std::tuple(0, 4), mapIndices<0, 4>(tuple, [](auto &&v) { return v; })));
static_assert(equal(std::tuple(1, 2, 3, 4, 5), flatMap(tuple, [](auto &&v) { return std::make_tuple(v + 1); })));
static_assert(equal(std::tuple(2, 3, 4), flatMapRange<2, 3>(tuple, [](auto &&v) { return std::make_tuple(v); })));
static_assert(equal(std::tuple(0, 4), flatMapIndices<0, 4>(tuple, [](auto &&v) { return std::make_tuple(v); })));
static_assert(equal(std::tuple(0, 1, 2, 3, 4), flatten(deepTuple)));
static_assert(equal(std::tuple(2, 3, 4), flattenRange<1, 2>(deepTuple)));
static_assert(equal(std::tuple(0, 1, 4), flattenIndices<0, 2>(deepTuple)));
static_assert(equal(std::tuple(1, 3), takeIndices<1, 3>(tuple)));
static_assert(equal(std::tuple(1, 3), takeIndices<1, 3>(tuple)));
static_assert(equal(std::tuple(0, 1, 2), takeIndices(tuple, std::make_index_sequence<3>{})));
static_assert(equal(std::tuple(1, 2), takeRange<1, 2>(tuple)));
static_assert(equal(std::tuple(0, 3, 6, 9, 12), zip([](auto &&l, auto &&m, auto &&r) { return l + m + r; }, tuple, tuple, tuple)));
static_assert(all(tuple, [](auto &&val) { return val >= 0; }));
static_assert(allAtIndices<0, 2, 4>(tuple, [](auto &&val) { return val % 2 == 0; }));
static_assert(allInRange<1, 3>(tuple, [](auto &&val) { return val > 0 && val < 4; }));
static_assert(any(tuple, [](auto &&val) { return val == 2; }));
static_assert(anyAtIndices<0, 2, 4>(tuple, [](auto &&val) { return val == 2; }));
static_assert(anyInRange<1, 3>(tuple, [](auto &&val) { return val == 2; }));
static_assert(equal(tuple, tuple));
static_assert(!equal(std::tuple(1, 2, 3), std::tuple(1, 2, 4)));
static_assert(!equal(std::tuple(1, 2, 3), std::tuple(1, 2, 3, 4)));
static_assert(3 == foldLeft(0, std::pair(1, 2), std::plus{}));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment