Skip to content

Instantly share code, notes, and snippets.

@Garciat
Last active August 29, 2015 14:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Garciat/c7e4bef299ee5c607948 to your computer and use it in GitHub Desktop.
Save Garciat/c7e4bef299ee5c607948 to your computer and use it in GitHub Desktop.
Function currying in C++14 (function_traits.cpp: https://gist.github.com/Garciat/cafe27d04cfdff0e891e)
#include <type_traits>
#include <utility>
#include <tuple>
#include "function_traits.cpp"
// ---
template <typename Args, typename Params>
struct apply_args;
template <typename HeadArgs, typename... Args, typename HeadParams, typename... Params>
struct apply_args<std::tuple<HeadArgs, Args...>, std::tuple<HeadParams, Params...>>
: std::enable_if<
std::is_constructible<HeadParams, HeadArgs>::value,
apply_args<std::tuple<Args...>, std::tuple<Params...>>
>::type
{ };
template <typename... Params>
struct apply_args<std::tuple<>, std::tuple<Params...>> {
using type = std::tuple<Params...>;
};
// ---
using empty_tuple_t = std::tuple<>;
template <typename TupleType>
struct is_empty_tuple : std::false_type { };
template <>
struct is_empty_tuple<empty_tuple_t> : std::true_type { };
// ----
template <typename FType, typename Env, typename Params>
struct currying;
template <typename FType, typename... Env, typename... Params>
struct currying<FType, std::tuple<Env...>, std::tuple<Params...>> {
using function_type = FType;
using closure_env_type = std::tuple<Env...>;
using param_tuple_type = std::tuple<Params...>;
template <typename Func, typename... Args>
constexpr
currying(Func&& func, Args&&... args) :
func(std::move(func)),
env(std::forward<Args>(args)...)
{ }
template <typename... Args>
constexpr
auto operator() (Args&&... args) const& {
using ArgsTuple = std::tuple<Args...>;
using NewEnv = std::tuple<Env..., Args...>;
using NewParams = typename apply_args<ArgsTuple, param_tuple_type>::type;
using NewCurrying = currying<FType, NewEnv, NewParams>;
using EnvIndices = std::make_index_sequence<sizeof...(Env)>;
using CanExecute = is_empty_tuple<NewParams>;
return apply<NewCurrying, CanExecute>(EnvIndices{}, std::forward<Args>(args)...);
}
template <typename... Args>
constexpr
auto operator() (Args&&... args) && {
using ArgsTuple = std::tuple<Args...>;
using NewEnv = std::tuple<Env..., Args...>;
using NewParams = typename apply_args<ArgsTuple, param_tuple_type>::type;
using NewCurrying = currying<FType, NewEnv, NewParams>;
using EnvIndices = std::make_index_sequence<sizeof...(Env)>;
using CanExecute = is_empty_tuple<NewParams>;
return std::move(*this).template apply<NewCurrying, CanExecute>(EnvIndices{}, std::forward<Args>(args)...);
}
private:
template <typename NewCurrying, typename CanExecute, typename... Args, size_t... Indices>
constexpr
auto apply(std::index_sequence<Indices...>, Args&&... args) const& {
return apply<NewCurrying>(CanExecute{}, std::get<Indices>(env)..., std::forward<Args>(args)...);
}
template <typename NewCurrying, typename CanExecute, typename... Args, size_t... Indices>
constexpr
auto apply(std::index_sequence<Indices...>, Args&&... args) && {
return std::move(*this).template apply<NewCurrying>(CanExecute{}, std::get<Indices>(std::move(env))..., std::forward<Args>(args)...);
}
template <typename NewCurrying, typename... Args>
constexpr
auto apply(std::false_type, Args&&... args) const& {
return NewCurrying{ func, std::forward<Args>(args)... };
}
template <typename NewCurrying, typename... Args>
constexpr
auto apply(std::false_type, Args&&... args) && {
return NewCurrying{ std::move(func), std::forward<Args>(args)... };
}
template <typename NewCurrying, typename... Args>
constexpr
auto apply(std::true_type, Args&&... args) const {
return func(std::forward<Args>(args)...);
}
function_type func;
closure_env_type env;
};
// ---
template <typename FType>
constexpr
auto curry(FType&& func) {
using ArgsTuple = function_arg_tuple_t<FType>;
using CurryType = currying<FType, empty_tuple_t, ArgsTuple>;
return CurryType{ std::move(func) };
}
#include "curry.cpp"
#include "meow.cpp"
#include <iostream>
int main() {
{
auto add = curry([](meow, int a, int b, int c) { return a + b + c; });
std::cout << add(meow{})(5)(5)(5) << std::endl;
}
meow::report();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment