Last active
November 30, 2015 12:37
-
-
Save mkurdej/8567e9b2d02ad2d2b541 to your computer and use it in GitHub Desktop.
for_each_adjacent is a for_each equivalent for adjacent container elements
This file contains 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 <algorithm> | |
#include <array> | |
#include <cassert> | |
// #include <experimental/apply> for std::experimental::apply | |
// #include <functional> // for std::invoke | |
#include <iostream> // example only | |
#include <iterator> | |
#include <string> // example only | |
#include <vector> // example only | |
template <typename FwdIter> | |
void checked_advance(FwdIter & it, FwdIter last, typename std::iterator_traits<FwdIter>::difference_type n = 1) | |
{ | |
using diff_type = typename std::iterator_traits<FwdIter>::difference_type; | |
for (diff_type i = 0; i < n; ++i) | |
{ | |
if (it == last) break; | |
std::advance(it, 1); | |
} | |
} | |
template <typename It, typename FwdIter> | |
void advance_all(It first, It last, FwdIter check_end) | |
{ | |
using value_type = typename std::iterator_traits<It>::value_type; | |
static_assert(std::is_same<value_type, FwdIter>::value, "advance_all should be given a collection of iterators of the same type that `check_end`"); | |
std::for_each(first, last, [check_end](FwdIter & fit) -> void | |
{ | |
checked_advance(fit, check_end); | |
}); | |
} | |
namespace detail { | |
template <class F, class... Args> | |
inline auto INVOKE(F&& f, Args&&... args) -> | |
decltype(std::forward<F>(f)(std::forward<Args>(args)...)) { | |
return std::forward<F>(f)(std::forward<Args>(args)...); | |
} | |
template <class Base, class T, class Derived> | |
inline auto INVOKE(T Base::*pmd, Derived&& ref) -> | |
decltype(std::forward<Derived>(ref).*pmd) { | |
return std::forward<Derived>(ref).*pmd; | |
} | |
template <class PMD, class Pointer> | |
inline auto INVOKE(PMD pmd, Pointer&& ptr) -> | |
decltype((*std::forward<Pointer>(ptr)).*pmd) { | |
return (*std::forward<Pointer>(ptr)).*pmd; | |
} | |
template <class Base, class T, class Derived, class... Args> | |
inline auto INVOKE(T Base::*pmf, Derived&& ref, Args&&... args) -> | |
decltype((std::forward<Derived>(ref).*pmf)(std::forward<Args>(args)...)) { | |
return (std::forward<Derived>(ref).*pmf)(std::forward<Args>(args)...); | |
} | |
template <class PMF, class Pointer, class... Args> | |
inline auto INVOKE(PMF pmf, Pointer&& ptr, Args&&... args) -> | |
decltype(((*std::forward<Pointer>(ptr)).*pmf)(std::forward<Args>(args)...)) { | |
return ((*std::forward<Pointer>(ptr)).*pmf)(std::forward<Args>(args)...); | |
} | |
} // namespace detail | |
template< class F, class... ArgTypes> | |
decltype(auto) invoke(F&& f, ArgTypes&&... args) { | |
return detail::INVOKE(std::forward<F>(f), std::forward<ArgTypes>(args)...); | |
} | |
namespace detail { | |
template <typename T = void> | |
struct identity { | |
decltype(auto) operator()(T&& t) { | |
return std::forward<T>(t); | |
} | |
}; | |
template <> | |
struct identity<void> { | |
template <typename T> | |
decltype(auto) operator()(T&& t) | |
{ | |
return std::forward<T>(t); | |
} | |
}; | |
template <typename T = void> | |
struct deref { | |
decltype(auto) operator()(T&& t) { | |
return *std::forward<T>(t); | |
} | |
}; | |
template <> | |
struct deref<void> { | |
template <typename T> | |
decltype(auto) operator()(T&& t) | |
{ | |
return *std::forward<T>(t); | |
} | |
}; | |
template <class F, class Tuple, class EachF, std::size_t... I> | |
constexpr decltype(auto) apply_impl( F&& f, Tuple&& t, EachF&& each_f, std::index_sequence<I...> ) | |
{ | |
// Note: std::invoke is a C++17 feature | |
// using std::invoke; | |
return invoke(std::forward<F>(f), each_f(std::get<I>(std::forward<Tuple>(t)))...); | |
} | |
} // namespace detail | |
// Note: works for tuples and arrays | |
template <class F, class Tuple, class EachF = detail::identity<>> | |
constexpr decltype(auto) apply(F&& f, Tuple&& t, EachF&& each_f = EachF{}) | |
{ | |
return detail::apply_impl(std::forward<F>(f), std::forward<Tuple>(t), each_f, | |
std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>{}); | |
} | |
template <size_t N, typename FwdIter, typename Func> | |
Func for_each_adjacent(FwdIter first, FwdIter last, Func func) | |
{ | |
if (first == last) return func; | |
std::array<FwdIter, N> its; | |
its.fill(first); | |
auto start = std::next(its.begin()); | |
for (std::size_t i = 1u; i < N; ++i) | |
{ | |
advance_all(start, its.end(), last); | |
std::advance(start, 1); | |
} | |
for (; its.back() != last; advance_all(its.begin(), its.end(), last)) | |
{ | |
apply(func, its, detail::deref<>{}); | |
} | |
return func; | |
} | |
int main() | |
{ | |
std::vector<std::string> vs = {"a", "b", "c", "d", "e"}; | |
for_each_adjacent<2>(vs.begin(), vs.end(), [](const auto & v1, const auto & v2) { | |
std::cout << "(" << v1 << "-" << v2 << ")" << "\n"; | |
}); | |
for_each_adjacent<3>(vs.begin(), vs.end(), [](const auto & v1, const auto & v2, const auto & v3) { | |
std::cout << "(" << v1 << "-" << v2 << "-" << v3 << ")" << "\n"; | |
}); | |
for_each_adjacent<4>(vs.begin(), vs.end(), [](const auto & v1, const auto & v2, const auto & v3, const auto & v4) { | |
std::cout << "(" << v1 << "-" << v2 << "-" << v3 << "-" << v4 << ")" << "\n"; | |
}); | |
for_each_adjacent<5>(vs.begin(), vs.end(), [](const auto & v1, const auto & v2, const auto & v3, const auto & v4, const auto & v5) { | |
std::cout << "(" << v1 << "-" << v2 << "-" << v3 << "-" << v4 << "-" << v5 << ")" << "\n"; | |
}); | |
for_each_adjacent<6>(vs.begin(), vs.end(), [](const auto & v1, const auto & v2, const auto & v3, const auto & v4, const auto & v5, const auto & v6) { | |
std::cout << "SHOULD NOT HAPPEN\n"; | |
assert(!"SHOULD NOT HAPPEN"); | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Live demo at http://coliru.stacked-crooked.com/a/13a812488d60c048.
Tested with:
invoke
to::invoke
due to ambiguity