Skip to content

Instantly share code, notes, and snippets.

@elbeno
Last active October 4, 2016 12:38
Show Gist options
  • Save elbeno/95e710c04d56d8cc72ade2e6b2bbe9d5 to your computer and use it in GitHub Desktop.
Save elbeno/95e710c04d56d8cc72ade2e6b2bbe9d5 to your computer and use it in GitHub Desktop.
Folding over a tuple
#include <cstddef>
#include <cstring>
#include <cwctype>
#include <functional>
#include <iostream>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
using namespace std;
template <typename F, typename Z, typename... Ts, std::size_t... Is>
decltype(auto) fold(std::index_sequence<Is...>,
const std::tuple<Ts...>& t, Z&& z, F&& f)
{
// The result type is the same for each function, so just take the first.
using result_type = decltype(f(z, get<0>(t)));
// The "handler_type" will be a lambda that doesn't capture, so decays to a
// function pointer. We will wrap the index in a type (integral_constant)
// and unwrap its value inside the lambda to access the right fields.
using handler_type = result_type (*)(const std::tuple<Ts...>&, Z&&, F&&);
auto make_handler = [] (auto int_const) {
return [] (const std::tuple<Ts...>& t, Z&& z, F&& f) -> result_type {
constexpr auto I = decltype(int_const)::value;
return std::forward<F>(f)(std::forward<Z>(z), std::get<I>(t));
};
};
static handler_type handlers[] = {
make_handler(std::integral_constant<size_t, Is>{})...
};
for (std::size_t i = 0; i < sizeof...(Is); ++i)
{
z = handlers[i](t, std::forward<Z>(z), std::forward<F>(f));
}
return std::forward<Z>(z);
}
template <typename F, typename Z, typename... Ts, std::size_t... Is>
decltype(auto) fold(std::index_sequence<Is...>,
const std::tuple<Ts...>& t, Z& z, F&& f)
{
// The result type is the same for each function, so just take the first.
using result_type = decltype(f(z, get<0>(t)));
// The "handler_type" will be a lambda that doesn't capture, so decays to a
// function pointer. We will wrap the index in a type (integral_constant)
// and unwrap its value inside the lambda to access the right fields.
using handler_type = result_type (*)(const std::tuple<Ts...>&, Z&, F&&);
auto make_handler = [] (auto int_const) {
return [] (const std::tuple<Ts...>& t, Z& z, F&& f) -> result_type {
constexpr auto I = decltype(int_const)::value;
return std::forward<F>(f)(z, std::get<I>(t));
};
};
static handler_type handlers[] = {
make_handler(std::integral_constant<size_t, Is>{})...
};
for (std::size_t i = 0; i < sizeof...(Is); ++i)
{
handlers[i](t, z, std::forward<F>(f));
}
return z;
}
template <typename F, typename Z, typename... Ts>
decltype(auto) fold(const tuple<Ts...>& t, Z&& z, F&& f)
{
return fold(std::index_sequence_for<Ts...>{}, t,
std::forward<Z>(z),
std::forward<F>(f));
}
size_t string_len(const string& s) { return s.size(); }
size_t string_len(const char* s) { return strlen(s); }
size_t string_len(bool) { return 1; }
size_t string_len(char) { return 1; }
template <typename T>
size_t string_len(T t) { return to_string(t).size(); }
int main()
{
auto t = make_tuple("Hello", 3.14, 1729, 'a');
{
auto f = [](ostream& s, const auto& x) -> ostream& {
return s << x << '\n';
};
decltype(auto) s = fold(t, cout, f);
s << "done" << endl;
}
{
auto f = [](size_t l, const auto& x) {
return l + string_len(x);
};
decltype(auto) s = fold(t, size_t{}, f);
cout << s << endl;
}
}
@Voultapher
Copy link

Voultapher commented Oct 4, 2016

Maybe I am missing something, but this seems like an overly complicated tuple fold.

Have the user bind things such way that all the fold function needs is the tuple and a function that can be called with each tuple element.

#include <iostream>
#include <tuple>

template<
    typename F,
    typename... Ts,
    std::size_t... Is
>
void for_each_in_tuple(
    std::tuple<Ts...>&& tuple,
    F&& func,
    std::index_sequence<Is...>
)
{
    // execution order matters
    static_cast<void>(
        std::initializer_list<int>
        {
            (std::forward<F>(func)(
                std::get<Is>(std::forward<std::tuple<Ts...>>(tuple))
            ), 0)...
        }
    );
}

template<typename F, typename... Ts> void fold(
    std::tuple<Ts...> tuple,
    F&& func
)
{
    for_each_in_tuple(
        std::forward<std::tuple<Ts...>>(tuple),
        std::forward<F>(func),
        std::make_index_sequence<sizeof...(Ts)>()
    );
}

int main()
{
    auto tuple = std::make_tuple(2, "cat", 5.f, 24.1);

    auto print = [](std::ostream& s, const auto& x) -> void
    {
        s << x << "\n";
    };

    auto cprint = [&print](const auto& x) -> void
    {
        print(std::cout, x);
    };

    fold(tuple, cprint);

    return 0;
}

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