Skip to content

Instantly share code, notes, and snippets.

@mkurdej
Last active November 30, 2015 12:37
Show Gist options
  • Save mkurdej/8567e9b2d02ad2d2b541 to your computer and use it in GitHub Desktop.
Save mkurdej/8567e9b2d02ad2d2b541 to your computer and use it in GitHub Desktop.
for_each_adjacent is a for_each equivalent for adjacent container elements
#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");
});
}
@mkurdej
Copy link
Author

mkurdej commented Nov 10, 2015

Live demo at http://coliru.stacked-crooked.com/a/13a812488d60c048.
Tested with:

  • clang version 3.7.0 (tags/RELEASE_370/final 246979)
  • g++ (GCC) 5.2.0
  • MSVC 2015 RTM (19.00.23026)
  • MSVC 2015 Update 1 RC (19.00.23419)
  • MSVC 2015 Update 1 web compiler (19.00.23427): need to change line 110 invoke to ::invoke due to ambiguity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment