My goal was to replicate Ruby's each_cons
and each_slice
:
(0..5).each_cons(3) {|a, b, c| print a, b, c, "\n"}
012
123
234
345
I made two functions, for_each_cons
, and for_each_slice
. They work similarly to their Ruby counterparts, except the slice is always passed to separate arguments. The slice size is deducted from the passed function's number of arguments.
const std::vector<int> vec = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
for_each_cons(vec, [](int a, int b, int c){
printf("%d %d %d\n", a, b, c);
});
for_each_slice(vec, [](int a, int b, int c){
printf("%d %d %d\n", a, b, c);
});
Outputs:
0 1 2 1 2 3 2 3 4 3 4 5 0 1 2 3 4 5
And here's the implementation. For simple code it seems to generate similar assembly to manual implementations, although it's largely because it requires random access iterators.
#include <utility>
namespace detail
{
template <typename Fun>
struct function_params
: public function_params<decltype(&Fun::operator())>
{};
template <typename Class, typename Ret, typename... Args> // for lambdas
struct function_params<Ret(Class::*)(Args...) const> {
static constexpr int n_args = sizeof...(Args);
};
template <typename Class, typename Ret, typename... Args> // for mutable lambdas
struct function_params<Ret(Class::*)(Args...)> {
static constexpr int n_args = sizeof...(Args);
};
template <typename Ret, typename... Args> // for function pointers
struct function_params<Ret(*)(Args...)> {
static constexpr int n_args = sizeof...(Args);
};
template<typename It, typename Fun, int N = function_params<Fun>::n_args, std::size_t... I>
void for_each_cons_impl(It first, It last, Fun f, std::index_sequence<I...>) {
for(auto it = first; it + N - 1 < last; ++it)
f(*(it + I)...);
}
template<typename It, typename Fun, int N = function_params<Fun>::n_args, typename Indices = std::make_index_sequence<N>>
void for_each_cons(It first, It last, Fun f) {
for_each_cons_impl(first, last, f, Indices());
}
template<typename It, typename Fun, int N = function_params<Fun>::n_args, std::size_t... I>
void for_each_slice_impl(It first, It last, Fun f, std::index_sequence<I...>) {
for(auto it = first; it + N - 1 < last; it += N)
f(*(it + I)...);
}
template<typename It, typename Fun, int N = function_params<Fun>::n_args, typename Indices = std::make_index_sequence<N>>
void for_each_slice(It first, It last, Fun f) {
for_each_slice_impl(first, last, f, Indices());
}
}
template<typename It, typename Fun>
void for_each_cons(It first, It last, Fun f) {
detail::for_each_cons(first, last, f);
}
template<typename Rng, typename Fun>
void for_each_cons(Rng &&rng, Fun f) {
for_each_cons(rng.begin(), rng.end(), f);
}
template<typename It, typename Fun>
void for_each_slice(It first, It last, Fun f) {
detail::for_each_slice(first, last, f);
}
template<typename Rng, typename Fun>
void for_each_slice(Rng &&rng, Fun f) {
detail::for_each_slice(rng.begin(), rng.end(), f);
}
Any hints and criticism are welcome.