Created
June 1, 2020 12:59
-
-
Save glukianets/312aa6ec33a13cdac03bb25bb416bd9f to your computer and use it in GitHub Desktop.
common functional* operations on C++ tuples
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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