27

In C++14, what is a good way to curry functions or function objects?

In particular, I have an overloaded function foo with some random number of overloads: some overloads may be found via ADL, others may be defined in a myriad of places.

I have a helper object:

static struct {
  template<class...Args>
  auto operator()(Args&&...args)const
  -> decltype(foo(std::forward<Args>(args)...))
    { return (foo(std::forward<Args>(args)...));}
} call_foo;

that lets me pass the overload set around as a single object.

If I wanted to curry foo, how should I do so?

As curry and partial function application are often used interchangeably, by curry I mean if foo(a,b,c,d) is a valid call, then curry(call_foo)(a)(b)(c)(d) must be a valid call.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • 5
    Is this even possible? Consider having two functions `void foo(int)`, `void foo(int, int)`. `curry(call_foo)(5)` must be something. Either it must evaluate the first overload or create a temporary that waits for another argument. In the first case `curry(call_foo)(5)(6)` fails, in the latter case it is impossible to call the first overload `void foo(int)`. Nevertheless, I am quite curious. – Markus Mayr Oct 30 '14 at 14:36
  • 3
    If `foo(1,2)` and `foo(1)` are both valid, how do you determine what `curry(call_foo)(1)` returns? I guess you have to require all overloads to be unambiguous for every subset of arguments? – Barry Oct 30 '14 at 14:37
  • Damnit @MarkusMayr, 30 seconds! – Barry Oct 30 '14 at 14:37
  • 1
    @MarkusMayr good question. As we are in a language with overloading, should we require a trailing `()` to invoke the arguments? That would make things awkward sometimes. Maybe it should return an expression template, that only evaluates when used to produce a value? Then how should we deal with pre-curried functions? – Yakk - Adam Nevraumont Oct 30 '14 at 14:44
  • @Yakk: Returning an expression template doesn't work for functions that are returning void (but have side effects)? – Markus Mayr Oct 30 '14 at 14:47
  • @MarkusMayr True. So if we have `void foo(int)` and `int foo(int,int)`, what should `curry(foo)(3)` do? Probably it just has to call `foo(3)`. – Yakk - Adam Nevraumont Oct 30 '14 at 14:51
  • @Yakk that seems sensible to me. If you can call a function, call it, otherwise return a partial? – Barry Oct 30 '14 at 14:55
  • 2
    @MarkusMayr actually, technically for the `void` case the expression template could invoke upon self destruction and never being cast to something else. :/ but probably silly. – Yakk - Adam Nevraumont Oct 30 '14 at 15:14
  • I don't have the faintest idea how this is supposed to work at all, but apparently other people do, there's a couple of answers in http://stackoverflow.com/questions/152005/how-can-currying-be-done-in-c – Damon Oct 30 '14 at 16:21
  • If I were to implement `curry` in C++, I'd just use SFINAE to only allow non overloaded function objects to be curried, which makes this problem go away - I think this is a sound way to solve this, since I don't see any way to disambiguate the calls if overloaded function objects are allowed. – Griwes Oct 30 '14 at 16:53
  • @Yakk: You're asking "how do I do X, but I don't know what X is?" Sounds like I should "Close: Unclear what you're asking"? – Mooing Duck Oct 30 '14 at 17:01
  • I'm a fan of requiring the trailing `()`, as all other solutions I see here become ambiguous and/or unworkable. (Truthfully, the `()` is only required for the void case, the rest can be evaluated when someone tries to retrieve the result. – Mooing Duck Oct 30 '14 at 17:03
  • evaluating in the destructor for the void-expression-template is a excellent idea, until you remember that destructors cannot throw exceptions. If it's noexcept, then that would work though. – Mooing Duck Oct 30 '14 at 17:05
  • I played around with this [last year in C++11](https://github.com/tclamb/Curry/blob/master/curry.h), also implementing a corresponding `uncurry`. No implementation using recursive lambdas, type-erased into `std::function`s is going to be efficient. I started playing around with a functor containing a `std::tuple` of the arguments passed so far, but lost interest before finishing that version's `uncurry()`. :) – tclamb Oct 30 '14 at 17:23
  • @MooingDuck A problem with the trailing `()` is that you want to be able to `curry(print)(std::cout)` and then iterate it on a collection, for example. The trailing `()` breaks that. And destructors can throw, just not during stack unwinding (!): detecting that state is C++17 sadly. Out of order calls are also annoying `auto&& next=f(3)` gets evaluated at end of scope, instead of when called (!). – Yakk - Adam Nevraumont Oct 30 '14 at 18:12
  • @Yakk: That's a big reason people keep pushing for `operator auto()` – Mooing Duck Oct 30 '14 at 18:19
  • 1
    @tclamb so I **think** uncurry isn't needed *if* your curry auto-uncurries when fed more than one argument at a time -- does that sound right? – Yakk - Adam Nevraumont Oct 30 '14 at 20:11
  • @Yakk sounds like a reasonable interface. Especially since you have the motivating example of `curry_foo()`. This could allow `curry(call_foo)(1)` and `curry(call_foo)(1, 2)`, no need for a trailing `()`. :) – tclamb Oct 30 '14 at 20:16
  • @tclamb: I know nothing of currying, but wouldn't an uncurry method take two functions and return a single one? Why would you uncurry something that you curried? – Mooing Duck Oct 30 '14 at 20:37
  • 1
    Mm, delicious function curry... flagging for migration to [cooking.stackexchange.com](http://cooking.stackexchange.com) :-) – Nate Eldredge Oct 30 '14 at 20:52
  • @MooingDuck Uncurrying is the inverse operation of currying. So `f(x)(y)(z) => f(x,y,z)`. I don't know if any of this is _really_ useful, but it can be used to implement partial function application. For example, `std::bind1st(foo, arg)` might be implemented as `uncurry(curry(foo)(arg))`. – tclamb Oct 30 '14 at 21:30
  • @tclamb: Wikipedia makes it sound like uncurrying is `f(g(x))->h(x)`, so I misunderstood. – Mooing Duck Oct 30 '14 at 21:36
  • @MooingDuck It is for the one argument case. :) More generally it's `f(g(x)) -> h(x, args...)` – tclamb Oct 30 '14 at 21:40

4 Answers4

11

Here is my current best attempt.

#include <iostream>
#include <utility>
#include <memory>

SFINAE utility helper class:

template<class T>struct voider{using type=void;};
template<class T>using void_t=typename voider<T>::type;

A traits class that tells you if Sig is a valid invokation -- ie, if std::result_of<Sig>::type is defined behavior. In some C++ implementations simply checking std::result_of is enough, but that isn't required by the standard:

template<class Sig,class=void>
struct is_invokable:std::false_type {};
template<class F, class... Ts>
struct is_invokable<
  F(Ts...),
  void_t<decltype(std::declval<F>()(std::declval<Ts>()...))>
>:std::true_type {
  using type=decltype(std::declval<F>()(std::declval<Ts>()...));
};
template<class Sig>
using invoke_result=typename is_invokable<Sig>::type;
template<class T> using type=T;

Curry helper is sort of a manual lambda. It captures a function and one argument. It isn't written as a lambda so we can enable proper rvalue forwarding when it is used in an rvalue context, which is important when currying:

template<class F, class T>
struct curry_helper {
  F f;
  T t;
  template<class...Args>
  invoke_result< type<F const&>(T const&, Args...) >
  operator()(Args&&...args)const&
  {
    return f(t, std::forward<Args>(args)...);
  }
  template<class...Args>
  invoke_result< type<F&>(T const&, Args...) >
  operator()(Args&&...args)&
  {
    return f(t, std::forward<Args>(args)...);
  }
  template<class...Args>
  invoke_result< type<F>(T const&, Args...) >
  operator()(Args&&...args)&&
  {
    return std::move(f)(std::move(t), std::forward<Args>(args)...);
  }
};

The meat and potatoes:

template<class F>
struct curry_t {
  F f;
  template<class Arg>
  using next_curry=curry_t< curry_helper<F, std::decay_t<Arg> >;
  // the non-terminating cases.  When the signature passed does not evaluate
  // we simply store the value in a curry_helper, and curry the result:
  template<class Arg,class=std::enable_if_t<!is_invokable<type<F const&>(Arg)>::value>>
  auto operator()(Arg&& arg)const&
  {
    return next_curry<Arg>{{ f, std::forward<Arg>(arg) }};
  }
  template<class Arg,class=std::enable_if_t<!is_invokable<type<F&>(Arg)>::value>>
  auto operator()(Arg&& arg)&
  {
    return next_curry<Arg>{{ f, std::forward<Arg>(arg) }};
  }
  template<class Arg,class=std::enable_if_t<!is_invokable<F(Arg)>::value>>
  auto operator()(Arg&& arg)&&
  {
    return next_curry<Arg>{{ std::move(f), std::forward<Arg>(arg) }};
  }
  // These are helper functions that make curry(blah)(a,b,c) somewhat of a shortcut for curry(blah)(a)(b)(c)
  // *if* the latter is valid, *and* it isn't just directly invoked.  Not quite, because this can *jump over*
  // terminating cases...
  template<class Arg,class...Args,class=std::enable_if_t<!is_invokable<type<F const&>(Arg,Args...)>::value>>
  auto operator()(Arg&& arg,Args&&...args)const&
  {
    return next_curry<Arg>{{ f, std::forward<Arg>(arg) }}(std::forward<Args>(args)...);
  }
  template<class Arg,class...Args,class=std::enable_if_t<!is_invokable<type<F&>(Arg,Args...)>::value>>
  auto operator()(Arg&& arg,Args&&...args)&
  {
    return next_curry<Arg>{{ f, std::forward<Arg>(arg) }}(std::forward<Args>(args)...);
  }
  template<class Arg,class...Args,class=std::enable_if_t<!is_invokable<F(Arg,Args...)>::value>>
  auto operator()(Arg&& arg,Args&&...args)&&
  {
    return next_curry<Arg>{{ std::move(f), std::forward<Arg>(arg) }}(std::forward<Args>(args)...);
  }

  // The terminating cases.  If we run into a case where the arguments would evaluate, this calls the underlying curried function:
  template<class... Args,class=std::enable_if_t<is_invokable<type<F const&>(Args...)>::value>>
  auto operator()(Args&&... args,...)const&
  {
    return f(std::forward<Args>(args)...);
  }
  template<class... Args,class=std::enable_if_t<is_invokable<type<F&>(Args...)>::value>>
  auto operator()(Args&&... args,...)&
  {
    return f(std::forward<Args>(args)...);
  }
  template<class... Args,class=std::enable_if_t<is_invokable<F(Args...)>::value>>
  auto operator()(Args&&... args,...)&&
  {
    return std::move(f)(std::forward<Args>(args)...);
  }
};

template<class F>
curry_t<typename std::decay<F>::type> curry( F&& f ) { return {std::forward<F>(f)}; }

The final function is humorously short.

Note that no type erasure is done. Also note that the theoretical hand-crafted solution can have far fewer moves, but I don't think I needlessly copy anywhere.

Here is a test function object:

static struct {
  double operator()(double x, int y, std::nullptr_t, std::nullptr_t)const{std::cout << "first\n"; return x*y;}
  char operator()(char c, int x)const{std::cout << "second\n"; return c+x;}
  void operator()(char const*s)const{std::cout << "hello " << s << "\n";}
} foo;

And some test code to see how it works:

int main() {
  auto f = curry(foo);
  // testing the ability to "jump over" the second overload:
  std::cout << f(3.14,10,std::nullptr_t{})(std::nullptr_t{}) << "\n";
  // Call the 3rd overload:
  f("world");
  // call the 2nd overload:
  auto x =  f('a',2);
  std::cout << x << "\n";
  // again:
  x =  f('a')(2);
}

live example

The resulting code is more than a bit of a mess. Lots of methods had to be repeated 3 times to handle &, const& and && cases optimally. The SFINAE clauses are long and complex. I ended up using both variardic args and varargs, with the varargs there to ensure a non-important signature difference in the method (and lower priority I think, not that it matters, the SFINAE ensures only one overload is ever valid, except this qualifiers).

The result of curry(call_foo) is an object that can be called one argument at a time, or many arguments at a time. You can call it with 3 arguments, then 1, then 1, then 2, and it does mostly the right thing. No evidence is exposed telling you how many arguments it wants, other than just trying to feed it arguments and seeing if the call is valid. This is required to handle overloading cases.

A quirk of the multiple-argument case is that it won't partially pass the packet to one curry, and use the rest as arguments to the return value. I could change that relatively easily by changing:

    return curry_t< curry_helper<F, std::decay_t<Arg> >>{{ f, std::forward<Arg>(arg) }}(std::forward<Args>(args)...);

to

    return (*this)(std::forward<Arg>(arg))(std::forward<Args>(args)...);

and the other two similar ones. That would prevent the technique of "jumping over" an overload that would otherwise be valid. Thoughts? It would mean that curry(foo)(a,b,c) would be logically identical to curry(foo)(a)(b)(c) which seems elegant.

Thanks to @Casey whose answer inspired much of this.


Most recent revision. It makes (a,b,c) behave much like (a)(b)(c) unless it is call that works directly.

#include <type_traits>
#include <utility>

template<class...>
struct voider { using type = void; };
template<class...Ts>
using void_t = typename voider<Ts...>::type;

template<class T>
using decay_t = typename std::decay<T>::type;

template<class Sig,class=void>
struct is_invokable:std::false_type {};
template<class F, class... Ts>
struct is_invokable<
  F(Ts...),
  void_t<decltype(std::declval<F>()(std::declval<Ts>()...))>
>:std::true_type {};

#define RETURNS(...) decltype(__VA_ARGS__){return (__VA_ARGS__);}

template<class D>
class rvalue_invoke_support {
  D& self(){return *static_cast<D*>(this);}
  D const& self()const{return *static_cast<D const*>(this);}
public:
  template<class...Args>
  auto operator()(Args&&...args)&->
  RETURNS( invoke( this->self(), std::forward<Args>(args)... ) )

  template<class...Args>
  auto operator()(Args&&...args)const&->
  RETURNS( invoke( this->self(), std::forward<Args>(args)... ) )

  template<class...Args>
  auto operator()(Args&&...args)&&->
  RETURNS( invoke( std::move(this->self()), std::forward<Args>(args)... ) )

  template<class...Args>
  auto operator()(Args&&...args)const&&->
  RETURNS( invoke( std::move(this->self()), std::forward<Args>(args)... ) )
};

namespace curryDetails {
  // Curry helper is sort of a manual lambda.  It captures a function and one argument
  // It isn't written as a lambda so we can enable proper rvalue forwarding when it is
  // used in an rvalue context, which is important when currying:
  template<class F, class T>
  struct curry_helper: rvalue_invoke_support<curry_helper<F,T>> {
    F f;
    T t;

    template<class A, class B>
    curry_helper(A&& a, B&& b):f(std::forward<A>(a)), t(std::forward<B>(b)) {}

    template<class curry_helper, class...Args>
    friend auto invoke( curry_helper&& self, Args&&... args)->
    RETURNS( std::forward<curry_helper>(self).f( std::forward<curry_helper>(self).t, std::forward<Args>(args)... ) )
  };
}

namespace curryNS {
  // the rvalue-ref qualified function type of a curry_t:
  template<class curry>
  using function_type = decltype(std::declval<curry>().f);

  template <class> struct curry_t;

  // the next curry type if we chain given a new arg A0:
  template<class curry, class A0>
  using next_curry = curry_t<::curryDetails::curry_helper<decay_t<function_type<curry>>, decay_t<A0>>>;

  // 3 invoke_ overloads
  // The first is one argument when invoking f with A0 does not work:
  template<class curry, class A0>
  auto invoke_(std::false_type, curry&& self, A0&&a0 )->
  RETURNS(next_curry<curry, A0>{std::forward<curry>(self).f,std::forward<A0>(a0)})

  // This is the 2+ argument overload where invoking with the arguments does not work
  // invoke a chain of the top one:
  template<class curry, class A0, class A1, class... Args>
  auto invoke_(std::false_type, curry&& self, A0&&a0, A1&& a1, Args&&... args )->
  RETURNS(std::forward<curry>(self)(std::forward<A0>(a0))(std::forward<A1>(a1), std::forward<Args>(args)...))

  // This is the any number of argument overload when it is a valid call to f:
  template<class curry, class...Args>
  auto invoke_(std::true_type, curry&& self, Args&&...args )->
  RETURNS(std::forward<curry>(self).f(std::forward<Args>(args)...))

  template<class F>
  struct curry_t : rvalue_invoke_support<curry_t<F>> {
    F f;

    template<class... U>curry_t(U&&...u):f(std::forward<U>(u)...){}

    template<class curry, class...Args>
    friend auto invoke( curry&& self, Args&&...args )->
    RETURNS(invoke_(is_invokable<function_type<curry>(Args...)>{}, std::forward<curry>(self), std::forward<Args>(args)...))
  };
}

template<class F>
curryNS::curry_t<decay_t<F>> curry( F&& f ) { return {std::forward<F>(f)}; }

#include <iostream>

static struct foo_t {
  double operator()(double x, int y, std::nullptr_t, std::nullptr_t)const{std::cout << "first\n"; return x*y;}
  char operator()(char c, int x)const{std::cout << "second\n"; return c+x;}
  void operator()(char const*s)const{std::cout << "hello " << s << "\n";}
} foo;

int main() {

  auto f = curry(foo);
  using C = decltype((f));
  std::cout << is_invokable<curryNS::function_type<C>(const char(&)[5])>{} << "\n";
  invoke( f, "world" );
  // Call the 3rd overload:
  f("world");
  // testing the ability to "jump over" the second overload:
  std::cout << f(3.14,10,nullptr,nullptr) << "\n";
  // call the 2nd overload:
  auto x = f('a',2);
  std::cout << x << "\n";
  // again:
  x =  f('a')(2);
  std::cout << x << "\n";
  std::cout << is_invokable<decltype(foo)(double, int)>{} << "\n";
  std::cout << is_invokable<decltype(foo)(double)>{} << "\n";
  std::cout << is_invokable<decltype(f)(double, int)>{} << "\n";
  std::cout << is_invokable<decltype(f)(double)>{} << "\n";
  std::cout << is_invokable<decltype(f(3.14))(int)>{} << "\n";
  decltype(std::declval<decltype((foo))>()(std::declval<double>(), std::declval<int>())) y = {3};
  (void)y;
  // std::cout << << "\n";
}

live version

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • @casey well perfect forward `()` to free `invoke` with proper r/l value `*this` and do the work there? Like all of it? And maybe use crtp to write it once. – Yakk - Adam Nevraumont Oct 31 '14 at 01:28
  • [Made a github repo with the code as I'm leaving it](https://github.com/cartec/curry). I despise macros, but made one anyway to reduce the `-> decltype(a_really_long_expression) { return a_really_long_expression; }` duplication. [Now compiles as C++11](http://coliru.stacked-crooked.com/a/ae0253d18ab0c0fb). – Casey Oct 31 '14 at 04:44
  • @casey Here is what I meant by the CRTP trick: http://coliru.stacked-crooked.com/a/3ba6d0b0682acffb -- it saves some boilerplate. I tried doing enable_if as well in one clause, but your tag dispatching looks cleaner. Take a look at `forward_member`, which makes passing rvalue from object to member easy. – Yakk - Adam Nevraumont Oct 31 '14 at 14:54
  • [Here is the code with `forward_member` elided and tag dispatch for `curry_t::invoke`.](https://github.com/cartec/curry/blob/Yakk/main.cpp). I think the eradication of `enable_if` makes it more comprehensible. – Casey Oct 31 '14 at 17:03
  • @Casey added to post. Why did you you split the declaration and implementation of `invoke`? Gets rid of some boilerplate by putting them together. – Yakk - Adam Nevraumont Oct 31 '14 at 18:53
  • GCC 4.9.1 complained about `curryNS::invoke` being multiply-defined. I'm not sure which compiler is wrong, seemed easiest to move the definition to namespace scope and not worry about it. – Casey Oct 31 '14 at 20:23
7

Here's my attempt using eager semantics, i.e., returning as soon as enough arguments are accumulated for a valid call to the original function (Demo at Coliru):

namespace detail {
template <unsigned I>
struct priority_tag : priority_tag<I-1> {};
template <> struct priority_tag<0> {};

// High priority overload.
// If f is callable with zero args, return f()
template <typename F>
auto curry(F&& f, priority_tag<1>) -> decltype(std::forward<F>(f)()) {
    return std::forward<F>(f)();
}

// Low priority overload.
// Return a partial application
template <typename F>
auto curry(F f, priority_tag<0>) {
    return [f](auto&& t) {
        return curry([f,t=std::forward<decltype(t)>(t)](auto&&...args) ->
          decltype(f(t, std::forward<decltype(args)>(args)...)) {
            return f(t, std::forward<decltype(args)>(args)...);
        }, priority_tag<1>{});
    };
}
} // namespace detail

// Dispatch to the implementation overload set in namespace detail.
template <typename F>
auto curry(F&& f) {
    return detail::curry(std::forward<F>(f), detail::priority_tag<1>{});
}

and an alternate implementation without eager semantics that requires an extra () to invoke the partial application making it possible to access e.g. both f(int) and f(int, int) from the same overload set:

template <typename F>
class partial_application {
    F f_;
public:
    template <typename T>
    explicit partial_application(T&& f) :
        f_(std::forward<T>(f)) {}

    auto operator()() { return f_(); }

    template <typename T>
    auto operator()(T&&);
};

template <typename F>
auto curry_explicit(F&& f) {
    return partial_application<F>{std::forward<F>(f)};
}

template <typename F>
template <typename T>
auto partial_application<F>::operator()(T&& t) {
    return curry_explicit([f=f_,t=std::forward<T>(t)](auto&&...args) ->
      decltype(f_(t, std::forward<decltype(args)>(args)...)) {
        return f(t, std::forward<decltype(args)>(args)...);
    });
}
Casey
  • 41,449
  • 7
  • 95
  • 125
  • `[f=std::forward(f),t=std::forward(t)]` might be slightly more efficient? – Yakk - Adam Nevraumont Oct 30 '14 at 16:06
  • @Yakk Yeah, I've been trying to eliminate copies since posting this. [`forward`-capturing `t` is fine](http://coliru.stacked-crooked.com/a/5c203bfc0e891036), but things [seem to blow up when I `forward`-capture `f`](http://coliru.stacked-crooked.com/a/f50cfb2806b6873e). I hate to jump straight to "compiler bug" but the code seems sensible to me. – Casey Oct 30 '14 at 16:28
  • well you cannot forward it twice safely. The inner lambda's `f=f` can only be `f=std::move(f)` if the outer lambda was called in an rvalue context, which you cannot determine in a lambda. – Yakk - Adam Nevraumont Oct 30 '14 at 17:41
  • Take a [look at this](http://coliru.stacked-crooked.com/a/bc0c2043d641d764) -- I made the lambdas into objects, and supported fine-grained `move` based on rvalueness of the invoked object. Which lets us move the function object instead of copying it in many cases... (it also lets you do "final calls" with a variable number of arguments. Making that work for a variable amount of curry is trickier (requires messing around with `tuple`s) but should be doable.) Ideally with more than 1 arg, we'd find the longest sequence of args that would invoke, do that, then try the remaining args on the retval – Yakk - Adam Nevraumont Oct 30 '14 at 18:04
  • @Yakk I decided the only way to handle the move semantics properly was to make the functors explicit instead of lambdas, and decided that was more work than I was willing to invest. You should post [that](http://coliru.stacked-crooked.com/a/bc0c2043d641d764) as an answer. – Casey Oct 30 '14 at 19:10
  • I just did. Want to take a stab at it and see what could be improved? – Yakk - Adam Nevraumont Oct 30 '14 at 20:10
4

This isn't a trivial problem. Getting ownership semantics right is tricky. For comparison, let's consider a few lambdas and how they express ownership:

[=]() {}         // capture by value, can't modify values of captures
[=]() mutable {} // capture by value, can modify values of captures

[&]() {}     // capture by reference, can modify values of captures

[a, &b, c = std::move(foo)]() {} // mixture of value and reference, move-only if foo is
                                 // can't modify value of a or c, but can b

My implementation by default captures by value, captures by reference when passed a std::reference_wrapper<> (same behavior as std::make_tuple() with std::ref()), and forwards the invoking arguments as is (lvalues remain lvalues, rvalues remain rvalues). I couldn't decide on a satisfactory solution for mutable, so all value captures are effectively const.

Capture of a move-only type makes the functor move-only. This in turn means if c is a curry_t and d is a move-only type, and c(std::move(d)) doesn't invoke the captured functor, then binding c(std::move(d)) to a lvalue means subsequent calls either have to contain enough arguments to invoke the captured functor, or the lvalue must be converted to an rvalue (possibly via std::move()). This took some care with reference qualifiers. Keep in mind that *this is always an lvalue, so the ref-qualifiers had to be applied to operator().

There's no way to know how many arguments the captured functor needs as there can be any number of overloads, so be careful. No static_asserts that sizeof...(Captures) < max(N_ARGS).

Overall, the implementation takes about 70 lines of code. As discussed in the comments, I followed the convention of curry(foo)(a, b, c, d) and foo(a, b, c, d) being (roughly) equivalent, allowing access to every overload.


#include <cstddef>
#include <functional>
#include <type_traits>
#include <tuple>
#include <utility>

template<typename Functor>
auto curry(Functor&& f);

namespace curry_impl
{
    /* helper: typing using type = T; is tedious */
    template<typename T> struct identity { using type = T; };

    /* helper: SFINAE magic :) */
    template<typename...> struct void_t_impl : identity<void> {};
    template<typename... Ts> using void_t = typename void_t_impl<Ts...>::type;

    /* helper: true iff F(Args...) is invokable */
    template<typename Signature, typename = void> struct is_invokable                                                                    : std::false_type {};
    template<typename F, typename... Args> struct is_invokable<F(Args...), void_t<decltype(std::declval<F>()(std::declval<Args>()...))>> : std::true_type  {};

    /* helper: unwraps references wrapped by std::ref() */
    template<typename T> struct unwrap_reference                            : identity<T>  {};
    template<typename T> struct unwrap_reference<std::reference_wrapper<T>> : identity<T&> {};
    template<typename T> using unwrap_reference_t = typename unwrap_reference<T>::type;

    /* helper: same transformation as applied by std::make_tuple() *
    *         decays to value type unless wrapped with std::ref() *
    *    use: modeling value & reference captures                 *
    *         e.g. c(a) vs c(std::ref(a))                         */
    template<typename T> struct decay_reference : unwrap_reference<std::decay_t<T>> {};
    template<typename T> using decay_reference_t = typename decay_reference<T>::type;

    /* helper: true iff all template arguments are true */
    template<bool...> struct all : std::true_type {};
    template<bool... Booleans> struct all<false, Booleans...> : std::false_type {};
    template<bool... Booleans> struct all<true, Booleans...> : all<Booleans...> {};

    /* helper: std::move(u) iff T is not an lvalue                       *
    *    use: save on copies when curry_t is an rvalue                  *
    *         e.g. c(a)(b)(c) should only copy functor, a, b, etc. once */
    template<bool = false> struct move_if_value_impl       { template<typename U> auto&& operator()(U&& u) { return std::move(u); } };
    template<>             struct move_if_value_impl<true> { template<typename U> auto&  operator()(U&  u) { return u; } };
    template<typename T, typename U> auto&& move_if_value(U&& u) { return move_if_value_impl<std::is_lvalue_reference<T>{}>{}(std::forward<U>(u)); }

    /* the curry wrapper/functor */
    template<typename Functor, typename... Captures>
    struct curry_t {
        /* unfortunately, ref qualifiers don't change *this (always lvalue),   *
        * so qualifiers have to be on operator(),                             *
        * even though only invoke_impl(std::false_type, ...) needs qualifiers */
    #define OPERATOR_PARENTHESES(X, Y) \
        template<typename... Args> \
        auto operator()(Args&&... args) X { \
            return invoke_impl_##Y(is_invokable<Functor(Captures..., Args...)>{}, std::index_sequence_for<Captures...>{}, std::forward<Args>(args)...); \
        }

        OPERATOR_PARENTHESES(&,  lv)
        OPERATOR_PARENTHESES(&&, rv)
    #undef OPERATOR_PARENTHESES

        Functor functor;
        std::tuple<Captures...> captures;

    private:
        /* tag dispatch for when Functor(Captures..., Args...) is an invokable expression *
        * see above comment about duplicate code                             */
    #define INVOKE_IMPL_TRUE(X) \
        template<typename... Args, std::size_t... Is> \
        auto invoke_impl_##X(std::true_type, std::index_sequence<Is...>, Args&&... args) { \
            return functor(std::get<Is>(captures)..., std::forward<Args>(args)...); \
        }

        INVOKE_IMPL_TRUE(lv)
        INVOKE_IMPL_TRUE(rv)
    #undef INVOKE_IMPL_TRUE

        /* tag dispatch for when Functor(Capture..., Args...) is NOT an invokable expression *
        * lvalue ref qualifier version copies all captured values/references     */
        template<typename... Args, std::size_t... Is>
        auto invoke_impl_lv(std::false_type, std::index_sequence<Is...>, Args&&... args) {
            static_assert(all<std::is_copy_constructible<Functor>{}, std::is_copy_constructible<Captures>{}...>{}, "all captures must be copyable or curry_t must an rvalue");
            return curry_t<Functor, Captures..., decay_reference_t<Args>...>{
                functor,
                std::tuple<Captures..., decay_reference_t<Args>...>{
                    std::get<Is>(captures)...,
                    std::forward<Args>(args)...
                }
            };
        }

        /* tag dispatch for when F(As..., Args...) is NOT an invokable expression        *
        * rvalue ref qualifier version moves all captured values, copies all references */
        template<typename... Args, std::size_t... Is>
        auto invoke_impl_rv(std::false_type, std::index_sequence<Is...>, Args&&... args) {
            return curry_t<Functor, Captures..., decay_reference_t<Args>...>{
                move_if_value<Functor>(functor),
                std::tuple<Captures..., decay_reference_t<Args>...>{
                    move_if_value<Captures>(std::get<Is>(captures))...,
                    std::forward<Args>(args)...
                }
            };
        }
    };
}

/* helper function for creating curried functors */
template<typename Functor>
auto curry(Functor&& f) {
    return curry_impl::curry_t<curry_impl::decay_reference_t<Functor>>{std::forward<Functor>(f), {}};
}

Live demo on Coliru demonstrating reference qualifier semantics.

tclamb
  • 1,469
  • 13
  • 17
  • Nice `std::ref` support. From what I can tell, the "recurse and uncurry one argument at a time" solution (above) gives also solves the move only argument problem? We could also store the `std::ref`s and unpack them when we invoke instead of turning them into references for storage as well. – Yakk - Adam Nevraumont Nov 03 '14 at 03:56
  • Your answer as posted does exactly that. Since you decay your arguments when you store them, the `reference_wrapper<>&&` gets decayed to a... `reference_wrapper<>`. :) When you do finally invoke, `operator T&()` implicitly converts the `reference_wrapper` to its contained `T&`. – tclamb Nov 03 '14 at 15:40
  • There's nothing in the standard that states `sizeof(reference_wrapper) == sizeof(T&)`, so I'm a bit wary of doing that myself. However, it does save code and complexity by replacing `move_if_value()` and `decay_reference<>` with unconditional `std::move()` and `std::decay_t<>`. Thoughts? – tclamb Nov 03 '14 at 15:47
  • GCC seems to be choking on optimizing out the curry machinery with your implementation. Compare the resulting assembly of [yours](http://goo.gl/X4br1C) to [mine](http://goo.gl/Er4n3y), both at -O3. – tclamb Nov 03 '14 at 16:10
0

Here is code i have been playing with while learning variadic templates.
It is toy example for ATD for function pointers, and ATD on std::function.
I have made example on lambdas, but havent find way to extract aruments, so no ATD for lamnda (yet)

#include <iostream>
#include <tuple>
#include <type_traits>
#include <functional>
#include <algorithm>


//this is helper class for calling (variadic) func from std::tuple
template <typename F,typename ...Args>
struct TupleCallHelper
{
    template<int ...> struct seq {};
    template<int N, int ...S> struct gens : gens<N-1, N-1, S...> {};
    template<int ...S> struct gens<0, S...>{ typedef seq<S...> type; };
    template<int ...S>
    static inline auto callFunc(seq<S...>,std::tuple<Args...>& params, F f) -> 
           decltype(f(std::get<S>(params) ...))
    {
        return f(std::get<S>(params) ... );
    }

    static inline auto delayed_dispatch(F& f, std::tuple<Args... >&  args) ->  
          decltype(callFunc(typename gens<sizeof...(Args)>::type(),args , f))
    {
        return callFunc(typename gens<sizeof...(Args)>::type(),args , f);
    }
};




template <int Depth,typename F,typename ... Args>
struct CurriedImpl;

//curried base class, when all args are consumed
template <typename F,typename ... Args>
struct CurriedImpl<0,F,Args...>
{
    std::tuple<Args...> m_tuple;
    F m_func;

    CurriedImpl(const F& a_func):m_func(a_func)
    {
    }
    auto operator()() -> 
        decltype(TupleCallHelper<F,Args...>::delayed_dispatch(m_func,m_tuple))
    {
        return TupleCallHelper<F,Args...>::delayed_dispatch(m_func,m_tuple);
    }
};

template <typename F,typename ... Args>
struct CurriedImpl<-1,F,Args ... > ; //this is against weird gcc bug

//curried before all args are consumed (stores arg in tuple)
template <int Depth,typename F,typename ... Args>
struct CurriedImpl : public CurriedImpl<Depth-1,F,Args...>
{
    using parent_t = CurriedImpl<Depth-1,F,Args...>;

    CurriedImpl(const F& a_func): parent_t(a_func)
    {
    }
    template <typename First>
    auto operator()(const First& a_first) -> CurriedImpl<Depth-1,F,Args...>
    {
        std::get<sizeof...(Args)-Depth>(parent_t::m_tuple) = a_first;
        return *this;
    }

    template <typename First, typename... Rem>
    auto operator()(const First& a_first,Rem ... a_rem) -> 
         CurriedImpl<Depth-1-sizeof...(Rem),F,Args...>
    {
        CurriedImpl<Depth-1,F,Args...> r = this->operator()(a_first);
        return r(a_rem...);
    }
};


template <typename F, typename ... Args>
struct Curried: public CurriedImpl<sizeof...(Args),F,Args...>
{   
    Curried(F a_f):
        CurriedImpl<sizeof...(Args),F,Args...>(a_f)
    {
    }
};


template<typename A>
int varcout( A a_a)
{
    std::cout << a_a << "\n" ;
    return 0;
}

template<typename A,typename ... Var>
int varcout( A a_a, Var ... a_var)
{
    std::cout << a_a << "\n" ;
    return varcout(a_var ...);
}

template <typename F, typename ... Args>
auto curried(F(*a_f)(Args...)) -> Curried<F(*)(Args...),Args...>
{
   return Curried<F(*)(Args...),Args...>(a_f);
}

template <typename R, typename ... Args>
auto curried(std::function<R(Args ... )> a_f) -> Curried<std::function<R(Args ... )>,Args...>
{
   return Curried<std::function<R(Args ... )>,Args...>(a_f);
}

int main()
{

    //function pointers
    auto f = varcout<int,float>;
    auto curried_funcp = curried(f);
    curried_funcp(1)(10)(); 

    //atd for std::function
    std::function<int(int,float)> fun(f);
    auto curried_func = curried(fun);
    curried_func(2)(5)();
    curried_func(2,5)();//works too

    //example with std::lambda using  Curried  
    auto  lamb = [](int a , int b , std::string& s){
        std::cout << a + b << s ; 
    };

    Curried<decltype(lamb),int,int,std::string> co(lamb);

    auto var1 = co(2);
    auto var2 = var1(1," is sum\n");
    var2(); //prints -> "3 is sum"
}
Luka Rahne
  • 10,336
  • 3
  • 34
  • 56
  • 1
    Looking at the above, it works with a function pointer. So it won't support overloaded functions or function objects or the like? – Yakk - Adam Nevraumont Oct 30 '14 at 18:17
  • Implementation is created in templated `struct Curried` that takes generic functor. in example it is `curried` function that is ATD overload for function pointers and returns `Curried`object. In general you just need to provided new/overloaded `curried` that makes ATD. This implementation is also pretty lightweight because it stores arguments in tuple (POD) and doesn’t use any special memory container. – Luka Rahne Oct 30 '14 at 18:26
  • I made examples for std::function and lambda – Luka Rahne Oct 30 '14 at 20:00
  • 1
    Those cases all have explicit argument lists. How does it handle a function object with more than one overload of `operator()`? – Yakk - Adam Nevraumont Oct 30 '14 at 20:09
  • `Curried` takes as first template parameter functor (function object) and then types of arguments. It can deduce witch operator() to call from provided template arguments. – Luka Rahne Oct 30 '14 at 20:34