πŸ“œ ⬆️ ⬇️

Curry on C ++

Hi, Habr.

I sat one evening, waited for a fresh revision of the clang to gather, and looked at the code for one of my projects , in which not very beautiful things were encountered
std::transform (someContainer.begin (), someContainer.end (), std::back_inserter (otherContainer), [this] (const SomeUglyAndLongType& item) { return HandleItem (item); }); 

Why create a whole lambda so that the function of two arguments (if, as the classics say, this considered to be an implicit zero argument) fix one of them? On some pseudochaskele one could just write something like
 map (handleItem this) someContainer 

We'll do the maps, functors, and other monads sometime next time, but we can try to learn how to write things that resemble (handleItem this) .


')
So, what do you want to do? I would like to learn to curry arbitrary functions, that is, to call them in "pieces" - they passed the first argument, got some new function of one argument, passed the second, got a new function, and so on until all the arguments for our original function are passed . When the arguments are over, go to the person and call our original function with all the specified arguments.

Or, if suddenly it will be more understandable to someone, it is necessary to make a sequence of functions T 1 β†’ (T 2 β†’ (... β†’ (T N β†’ R)) from the function N arguments T 1 Γ— T 2 ×… Γ— T N β†’ R. Although, to whom it is so clear, he probably even knows what currying is without me.

Immediately I warn you that the solution will be non-production-quality for several reasons, which we will discuss a little later.

What do we need for this? You need a fresh compiler with C ++ 14 support, for example, clang 3.4 and newer. You also need a standard library from C ++ 14, on some Linux systems there may be problems with it, so you can play with code on online services like this .

What should look like the result of our attempts to make C ++ Haskel on, actually, pluses? Well, for example, something like this:
 auto test (int t1, int t2, double t3, const std::string& str) { return t1 * t2 * t3 * str.size (); } // ... std::cout << curry (test) (1) (2) (3.0) ("four") << std::endl; 

Naturally, free curry functions should not be limited.

What do we need to do? Yes, nothing, write the curry function. So let's write!

Let curry (f) return some object (let's call it CurryImpl ), which obviously needs the function f that we passed to curry , and also operator() must be overloaded with it, and according to the condition of the problem it should take one The argument is not clear what type, and it is not clear what to return. Naturally, CurryImpl template to be able to memorize any functions:

 template<typename F> class CurryImpl { const F m_f; //  ,   curry public: CurryImpl (F f) : m_f { f } { } template<typename T> auto operator() (const T& arg) const { //   -  } }; template<typename F> auto curry (F f) { return CurryImpl<F> { f }; } 

Let's now look at operator-parentheses. So, this operator takes one argument arg and either calls the desired function m_f , if it is the last argument in the call chain, or returns another object that remembers this arg .

First, let's figure out how to memorize arguments.

Well, here everything is actually quite trivial: you just need to take them and store them in CurryImpl . We do not know anything about these arguments, as well as about their types, so we’ll have to add another argument to the template, and even with the variadic. You can directly store the values ​​of the arguments, for example, in std::tuple . You can tell our CurryImpl about these arguments directly in the constructor. Total, the class declaration is modified something like this:
 template<typename F, typename... PrevArgs> class CurryImpl { const F m_f; const std::tuple<PrevArgs...> m_prevArgs; public: CurryImpl (F f, const std::tuple<PrevArgs...>& prev) : m_f { f } , m_prevArgs { prev } { } //      }; 

So, well, with the storage figured out, now it remains to call the function. Here everything becomes a little more interesting: how, in fact, determine when it is worth stopping to accumulate arguments and, in fact, call m_f ? And just take and call, if possible - everything is fine!

And one of the remarkable rules of C ++ - SFINAE, or Substitution Failure Is Not An Error will help us in this. In short, [in this context] the point is that if the compiler, when selecting candidates for a function call among a number of overloaded functions, sees some incorrect expression in the function declaration , then it discards it and looks further instead of error in the console and compiling too early . Actually, all these std::enable_if and company are based exactly on SFINAE.

So, SFINAE. We write a function that will call our original m_f only when such a call is well-formed. This will help us a great template function std::result_of . std::result_of<F (T1, T2, ...)> defines a nested type , equal to the type returned by object F, if it is called with arguments of type T1, T2, ... It is described in more detail, for example, here . Actually, keywords by reference, for C ++ 14:
ArgTypes ... in an unevaluated context.


For C ++ 11, the wording is slightly different, but this is irrelevant.

By the way, in C ++ 14 you can use the convenient synonym std::result_of_t<...> instead of typename std::result_of<...>::type .

So, we write our function:
  template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg) const // PrevArgs β€”  CurryImpl,    { //  -   m_f     } 


How it works? If m_f , which is of type F , can be called with all previous arguments and the current one, then everything is fine, the function β€œexists”, if it is impossible, the compiler does not consider it.

If the function cannot be called, then there should be another invoke , which also takes one argument, remembers it and recursively returns a new object with the parent-brackets operator and all the rest. Somehow, for example:
  template<typename T> auto invoke (const T& arg) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } 


What did we do here? Returned a new CurryImpl , which retains all previous arguments plus one new one. At the level of types, this is reflected in the record PrevArgs..., T , if you want, adding T to the variadic-type list, and at the level of values, we simply connect two tuples, the old m_prevArgs and the new single-element tuple.

Look again at our operator-parentheses. Now it is clear that we should call one of our two invoke overloads in it, possibly the first one that calls the function, otherwise you can never stop saving arguments. How to do it? There are a lot of options here, my favorite in such cases is to use the fact that any overload is better than overloading with an ellipsis (aka ellipsis, aka C-style variadics). That is, we add another parameter that we will not even use, for example, int to the first function and ... to the second, and we get something like this:
  template<typename T> auto operator() (const T& arg) const { return invoke (arg, 0); } private: template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg, int) const { //  -   m_f     } template<typename T> auto invoke (const T& arg, ...) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } 


That is, if the first overload is available, it will always be selected: the int compiler likes a lot more than ...

It remains to deal with the function call from the first overload, and everything will be fine.

What do you need to call a function whose arguments (up to arg ) are stored in a tuple? We need to somehow unpack that tuple and pass the results of the unpacking to a function as usual arguments. The problem is that at the call point we don’t know at the stage of writing the code how many arguments we have, so we can’t just pick up std::get our hands. Well, it's nice, it's not good in 2014 to do work for the compiler. If we had some way to make a variadic of numbers from 0 to N-1, we could write something like this:
  //  Is    0  (   m_prevArgs)-1 template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg /*,   -,  Is   */) const { return m_f (std::get<Is> (m_prevArgs)..., arg); } 

Here, according to the rules for expanding variadic parameters, the expression std::get (m_prevArgs)... std::get (m_prevArgs), std::get (m_prevArgs), Is .

, , C++11, C++14 STL! , . , std::index_sequence_for , ( PrevArgs... ), invoke invokeIndexed :
return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {});
invokeIndexed std::index_sequence , :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return m_f (std::get<Is> (m_prevArgs)..., arg); }

! ! , , :
struct Foo { auto doFoo (int baz, int qux) { return (m_bar + baz) / qux; } }; // ... Foo someFoo; const auto fooRes = Curry (&Foo::doFoo) (&someFoo) (2) (4);
: m_f (arguments) well-formed, m_f β€” - .

, , , , . , m_f , --. , :
template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } };
invokeIndexed :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); }
, - SFINAE , Invoke , Args... β€” , . , , , :
template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } };
, , , , : c , , , -- . :
struct Foo { int m_state = 42; auto doFoo (int bar) { m_state += bar; return m_state; } }; Foo foo; curry (&Foo::doFoo) (foo) (1); // foo.m_state 42 curry (&Foo::doFoo) (&foo) (1); // foo.m_state 43

, -, .

#include <tuple> #include <type_traits> #include <utility> #include <iostream> #include <string> template<typename F, typename... PrevArgs> class CurryImpl { const F m_f; const std::tuple<PrevArgs...> m_prevArgs; public: CurryImpl (F f, const std::tuple<PrevArgs...>& prev) : m_f { f } , m_prevArgs { prev } { } template<typename T> auto operator() (const T& arg) const { return invoke (arg, 0); } private: template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg, int) const { return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {}); } template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } }; template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } }; template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); } template<typename T> auto invoke (const T& arg, ...) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } }; template<typename F> auto curry (F f) { return CurryImpl<F> { f, {} }; } auto test (int t1, int t2, double t3, const std::string& str) { return t1 * t2 * t3 * str.size (); } struct Foo { int m_bar; auto doFoo (int baz, int qux) { auto result = (m_bar + baz) / qux; ++m_bar; return result; } }; int main () { const auto res = curry (test) (1) (2) (3.0) ("four"); std::cout << res << std::endl; Foo someFoo { 42 }; const auto fooRes = curry (&Foo::doFoo) (&someFoo) (2) (4); std::cout << fooRes << " " << someFoo.m_bar << std::endl; someFoo.m_bar = 42; auto lambda = [someFoo] (int bar, int baz) mutable { return someFoo.doFoo (bar, baz); }; const auto lambdaRes = curry (lambda) (4) (2); std::cout << lambdaRes << std::endl; }

.

:

CurryImpl , std::function ?
std::function type erasure, . , .
std::tuple ?
, , , .

C++14, C++11 ?
, . C++14 , :
auto , decltype ; compile-time- ( std::index_sequence_for , ).
, , , β€” decltype .

, ?
.

?
: - , , . , , .

production-ready?
, C++14 - , .
std::get (m_prevArgs)... std::get (m_prevArgs), std::get (m_prevArgs), Is .

, , C++11, C++14 STL! , . , std::index_sequence_for , ( PrevArgs... ), invoke invokeIndexed :
return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {});
invokeIndexed std::index_sequence , :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return m_f (std::get<Is> (m_prevArgs)..., arg); }

! ! , , :
struct Foo { auto doFoo (int baz, int qux) { return (m_bar + baz) / qux; } }; // ... Foo someFoo; const auto fooRes = Curry (&Foo::doFoo) (&someFoo) (2) (4);
: m_f (arguments) well-formed, m_f β€” - .

, , , , . , m_f , --. , :
template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } };
invokeIndexed :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); }
, - SFINAE , Invoke , Args... β€” , . , , , :
template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } };
, , , , : c , , , -- . :
struct Foo { int m_state = 42; auto doFoo (int bar) { m_state += bar; return m_state; } }; Foo foo; curry (&Foo::doFoo) (foo) (1); // foo.m_state 42 curry (&Foo::doFoo) (&foo) (1); // foo.m_state 43

, -, .

#include <tuple> #include <type_traits> #include <utility> #include <iostream> #include <string> template<typename F, typename... PrevArgs> class CurryImpl { const F m_f; const std::tuple<PrevArgs...> m_prevArgs; public: CurryImpl (F f, const std::tuple<PrevArgs...>& prev) : m_f { f } , m_prevArgs { prev } { } template<typename T> auto operator() (const T& arg) const { return invoke (arg, 0); } private: template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg, int) const { return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {}); } template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } }; template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } }; template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); } template<typename T> auto invoke (const T& arg, ...) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } }; template<typename F> auto curry (F f) { return CurryImpl<F> { f, {} }; } auto test (int t1, int t2, double t3, const std::string& str) { return t1 * t2 * t3 * str.size (); } struct Foo { int m_bar; auto doFoo (int baz, int qux) { auto result = (m_bar + baz) / qux; ++m_bar; return result; } }; int main () { const auto res = curry (test) (1) (2) (3.0) ("four"); std::cout << res << std::endl; Foo someFoo { 42 }; const auto fooRes = curry (&Foo::doFoo) (&someFoo) (2) (4); std::cout << fooRes << " " << someFoo.m_bar << std::endl; someFoo.m_bar = 42; auto lambda = [someFoo] (int bar, int baz) mutable { return someFoo.doFoo (bar, baz); }; const auto lambdaRes = curry (lambda) (4) (2); std::cout << lambdaRes << std::endl; }

.

:

CurryImpl , std::function ?
std::function type erasure, . , .
std::tuple ?
, , , .

C++14, C++11 ?
, . C++14 , :
auto , decltype ; compile-time- ( std::index_sequence_for , ).
, , , β€” decltype .

, ?
.

?
: - , , . , , .

production-ready?
, C++14 - , .
 std::get (m_prevArgs)...    std::get (m_prevArgs), std::get (m_prevArgs),       Is . 

, , C++11, C++14 STL! , . , std::index_sequence_for , ( PrevArgs... ), invoke invokeIndexed :
return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {});
invokeIndexed std::index_sequence , :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return m_f (std::get<Is> (m_prevArgs)..., arg); }

! ! , , :
struct Foo { auto doFoo (int baz, int qux) { return (m_bar + baz) / qux; } }; // ... Foo someFoo; const auto fooRes = Curry (&Foo::doFoo) (&someFoo) (2) (4);
: m_f (arguments) well-formed, m_f β€” - .

, , , , . , m_f , --. , :
template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } };
invokeIndexed :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); }
, - SFINAE , Invoke , Args... β€” , . , , , :
template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } };
, , , , : c , , , -- . :
struct Foo { int m_state = 42; auto doFoo (int bar) { m_state += bar; return m_state; } }; Foo foo; curry (&Foo::doFoo) (foo) (1); // foo.m_state 42 curry (&Foo::doFoo) (&foo) (1); // foo.m_state 43

, -, .

#include <tuple> #include <type_traits> #include <utility> #include <iostream> #include <string> template<typename F, typename... PrevArgs> class CurryImpl { const F m_f; const std::tuple<PrevArgs...> m_prevArgs; public: CurryImpl (F f, const std::tuple<PrevArgs...>& prev) : m_f { f } , m_prevArgs { prev } { } template<typename T> auto operator() (const T& arg) const { return invoke (arg, 0); } private: template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg, int) const { return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {}); } template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } }; template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } }; template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); } template<typename T> auto invoke (const T& arg, ...) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } }; template<typename F> auto curry (F f) { return CurryImpl<F> { f, {} }; } auto test (int t1, int t2, double t3, const std::string& str) { return t1 * t2 * t3 * str.size (); } struct Foo { int m_bar; auto doFoo (int baz, int qux) { auto result = (m_bar + baz) / qux; ++m_bar; return result; } }; int main () { const auto res = curry (test) (1) (2) (3.0) ("four"); std::cout << res << std::endl; Foo someFoo { 42 }; const auto fooRes = curry (&Foo::doFoo) (&someFoo) (2) (4); std::cout << fooRes << " " << someFoo.m_bar << std::endl; someFoo.m_bar = 42; auto lambda = [someFoo] (int bar, int baz) mutable { return someFoo.doFoo (bar, baz); }; const auto lambdaRes = curry (lambda) (4) (2); std::cout << lambdaRes << std::endl; }

.

:

CurryImpl , std::function ?
std::function type erasure, . , .
std::tuple ?
, , , .

C++14, C++11 ?
, . C++14 , :
auto , decltype ; compile-time- ( std::index_sequence_for , ).
, , , β€” decltype .

, ?
.

?
: - , , . , , .

production-ready?
, C++14 - , .
std::get (m_prevArgs)... std::get (m_prevArgs), std::get (m_prevArgs), Is .

, , C++11, C++14 STL! , . , std::index_sequence_for , ( PrevArgs... ), invoke invokeIndexed :
return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {});
invokeIndexed std::index_sequence , :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return m_f (std::get<Is> (m_prevArgs)..., arg); }

! ! , , :
struct Foo { auto doFoo (int baz, int qux) { return (m_bar + baz) / qux; } }; // ... Foo someFoo; const auto fooRes = Curry (&Foo::doFoo) (&someFoo) (2) (4);
: m_f (arguments) well-formed, m_f β€” - .

, , , , . , m_f , --. , :
template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } };
invokeIndexed :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); }
, - SFINAE , Invoke , Args... β€” , . , , , :
template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } };
, , , , : c , , , -- . :
struct Foo { int m_state = 42; auto doFoo (int bar) { m_state += bar; return m_state; } }; Foo foo; curry (&Foo::doFoo) (foo) (1); // foo.m_state 42 curry (&Foo::doFoo) (&foo) (1); // foo.m_state 43

, -, .

#include <tuple> #include <type_traits> #include <utility> #include <iostream> #include <string> template<typename F, typename... PrevArgs> class CurryImpl { const F m_f; const std::tuple<PrevArgs...> m_prevArgs; public: CurryImpl (F f, const std::tuple<PrevArgs...>& prev) : m_f { f } , m_prevArgs { prev } { } template<typename T> auto operator() (const T& arg) const { return invoke (arg, 0); } private: template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg, int) const { return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {}); } template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } }; template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } }; template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); } template<typename T> auto invoke (const T& arg, ...) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } }; template<typename F> auto curry (F f) { return CurryImpl<F> { f, {} }; } auto test (int t1, int t2, double t3, const std::string& str) { return t1 * t2 * t3 * str.size (); } struct Foo { int m_bar; auto doFoo (int baz, int qux) { auto result = (m_bar + baz) / qux; ++m_bar; return result; } }; int main () { const auto res = curry (test) (1) (2) (3.0) ("four"); std::cout << res << std::endl; Foo someFoo { 42 }; const auto fooRes = curry (&Foo::doFoo) (&someFoo) (2) (4); std::cout << fooRes << " " << someFoo.m_bar << std::endl; someFoo.m_bar = 42; auto lambda = [someFoo] (int bar, int baz) mutable { return someFoo.doFoo (bar, baz); }; const auto lambdaRes = curry (lambda) (4) (2); std::cout << lambdaRes << std::endl; }

.

:

CurryImpl , std::function ?
std::function type erasure, . , .
std::tuple ?
, , , .

C++14, C++11 ?
, . C++14 , :
auto , decltype ; compile-time- ( std::index_sequence_for , ).
, , , β€” decltype .

, ?
.

?
: - , , . , , .

production-ready?
, C++14 - , .
 std::get (m_prevArgs)...    std::get (m_prevArgs), std::get (m_prevArgs),       Is . 

, , C++11, C++14 STL! , . , std::index_sequence_for , ( PrevArgs... ), invoke invokeIndexed :
return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {});
invokeIndexed std::index_sequence , :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return m_f (std::get<Is> (m_prevArgs)..., arg); }

! ! , , :
struct Foo { auto doFoo (int baz, int qux) { return (m_bar + baz) / qux; } }; // ... Foo someFoo; const auto fooRes = Curry (&Foo::doFoo) (&someFoo) (2) (4);
: m_f (arguments) well-formed, m_f β€” - .

, , , , . , m_f , --. , :
template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } };
invokeIndexed :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); }
, - SFINAE , Invoke , Args... β€” , . , , , :
template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } };
, , , , : c , , , -- . :
struct Foo { int m_state = 42; auto doFoo (int bar) { m_state += bar; return m_state; } }; Foo foo; curry (&Foo::doFoo) (foo) (1); // foo.m_state 42 curry (&Foo::doFoo) (&foo) (1); // foo.m_state 43

, -, .

#include <tuple> #include <type_traits> #include <utility> #include <iostream> #include <string> template<typename F, typename... PrevArgs> class CurryImpl { const F m_f; const std::tuple<PrevArgs...> m_prevArgs; public: CurryImpl (F f, const std::tuple<PrevArgs...>& prev) : m_f { f } , m_prevArgs { prev } { } template<typename T> auto operator() (const T& arg) const { return invoke (arg, 0); } private: template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg, int) const { return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {}); } template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } }; template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } }; template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); } template<typename T> auto invoke (const T& arg, ...) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } }; template<typename F> auto curry (F f) { return CurryImpl<F> { f, {} }; } auto test (int t1, int t2, double t3, const std::string& str) { return t1 * t2 * t3 * str.size (); } struct Foo { int m_bar; auto doFoo (int baz, int qux) { auto result = (m_bar + baz) / qux; ++m_bar; return result; } }; int main () { const auto res = curry (test) (1) (2) (3.0) ("four"); std::cout << res << std::endl; Foo someFoo { 42 }; const auto fooRes = curry (&Foo::doFoo) (&someFoo) (2) (4); std::cout << fooRes << " " << someFoo.m_bar << std::endl; someFoo.m_bar = 42; auto lambda = [someFoo] (int bar, int baz) mutable { return someFoo.doFoo (bar, baz); }; const auto lambdaRes = curry (lambda) (4) (2); std::cout << lambdaRes << std::endl; }

.

:

CurryImpl , std::function ?
std::function type erasure, . , .
std::tuple ?
, , , .

C++14, C++11 ?
, . C++14 , :
auto , decltype ; compile-time- ( std::index_sequence_for , ).
, , , β€” decltype .

, ?
.

?
: - , , . , , .

production-ready?
, C++14 - , .
std::get (m_prevArgs)... std::get (m_prevArgs), std::get (m_prevArgs), Is .

, , C++11, C++14 STL! , . , std::index_sequence_for , ( PrevArgs... ), invoke invokeIndexed :
return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {});
invokeIndexed std::index_sequence , :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return m_f (std::get<Is> (m_prevArgs)..., arg); }

! ! , , :
struct Foo { auto doFoo (int baz, int qux) { return (m_bar + baz) / qux; } }; // ... Foo someFoo; const auto fooRes = Curry (&Foo::doFoo) (&someFoo) (2) (4);
: m_f (arguments) well-formed, m_f β€” - .

, , , , . , m_f , --. , :
template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } };
invokeIndexed :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); }
, - SFINAE , Invoke , Args... β€” , . , , , :
template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } };
, , , , : c , , , -- . :
struct Foo { int m_state = 42; auto doFoo (int bar) { m_state += bar; return m_state; } }; Foo foo; curry (&Foo::doFoo) (foo) (1); // foo.m_state 42 curry (&Foo::doFoo) (&foo) (1); // foo.m_state 43

, -, .

#include <tuple> #include <type_traits> #include <utility> #include <iostream> #include <string> template<typename F, typename... PrevArgs> class CurryImpl { const F m_f; const std::tuple<PrevArgs...> m_prevArgs; public: CurryImpl (F f, const std::tuple<PrevArgs...>& prev) : m_f { f } , m_prevArgs { prev } { } template<typename T> auto operator() (const T& arg) const { return invoke (arg, 0); } private: template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg, int) const { return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {}); } template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } }; template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } }; template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); } template<typename T> auto invoke (const T& arg, ...) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } }; template<typename F> auto curry (F f) { return CurryImpl<F> { f, {} }; } auto test (int t1, int t2, double t3, const std::string& str) { return t1 * t2 * t3 * str.size (); } struct Foo { int m_bar; auto doFoo (int baz, int qux) { auto result = (m_bar + baz) / qux; ++m_bar; return result; } }; int main () { const auto res = curry (test) (1) (2) (3.0) ("four"); std::cout << res << std::endl; Foo someFoo { 42 }; const auto fooRes = curry (&Foo::doFoo) (&someFoo) (2) (4); std::cout << fooRes << " " << someFoo.m_bar << std::endl; someFoo.m_bar = 42; auto lambda = [someFoo] (int bar, int baz) mutable { return someFoo.doFoo (bar, baz); }; const auto lambdaRes = curry (lambda) (4) (2); std::cout << lambdaRes << std::endl; }

.

:

CurryImpl , std::function ?
std::function type erasure, . , .
std::tuple ?
, , , .

C++14, C++11 ?
, . C++14 , :
auto , decltype ; compile-time- ( std::index_sequence_for , ).
, , , β€” decltype .

, ?
.

?
: - , , . , , .

production-ready?
, C++14 - , .
 std::get (m_prevArgs)...    std::get (m_prevArgs), std::get (m_prevArgs),       Is . 

, , C++11, C++14 STL! , . , std::index_sequence_for , ( PrevArgs... ), invoke invokeIndexed :
return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {});
invokeIndexed std::index_sequence , :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return m_f (std::get<Is> (m_prevArgs)..., arg); }

! ! , , :
struct Foo { auto doFoo (int baz, int qux) { return (m_bar + baz) / qux; } }; // ... Foo someFoo; const auto fooRes = Curry (&Foo::doFoo) (&someFoo) (2) (4);
: m_f (arguments) well-formed, m_f β€” - .

, , , , . , m_f , --. , :
template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } };
invokeIndexed :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); }
, - SFINAE , Invoke , Args... β€” , . , , , :
template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } };
, , , , : c , , , -- . :
struct Foo { int m_state = 42; auto doFoo (int bar) { m_state += bar; return m_state; } }; Foo foo; curry (&Foo::doFoo) (foo) (1); // foo.m_state 42 curry (&Foo::doFoo) (&foo) (1); // foo.m_state 43

, -, .

#include <tuple> #include <type_traits> #include <utility> #include <iostream> #include <string> template<typename F, typename... PrevArgs> class CurryImpl { const F m_f; const std::tuple<PrevArgs...> m_prevArgs; public: CurryImpl (F f, const std::tuple<PrevArgs...>& prev) : m_f { f } , m_prevArgs { prev } { } template<typename T> auto operator() (const T& arg) const { return invoke (arg, 0); } private: template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg, int) const { return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {}); } template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } }; template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } }; template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); } template<typename T> auto invoke (const T& arg, ...) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } }; template<typename F> auto curry (F f) { return CurryImpl<F> { f, {} }; } auto test (int t1, int t2, double t3, const std::string& str) { return t1 * t2 * t3 * str.size (); } struct Foo { int m_bar; auto doFoo (int baz, int qux) { auto result = (m_bar + baz) / qux; ++m_bar; return result; } }; int main () { const auto res = curry (test) (1) (2) (3.0) ("four"); std::cout << res << std::endl; Foo someFoo { 42 }; const auto fooRes = curry (&Foo::doFoo) (&someFoo) (2) (4); std::cout << fooRes << " " << someFoo.m_bar << std::endl; someFoo.m_bar = 42; auto lambda = [someFoo] (int bar, int baz) mutable { return someFoo.doFoo (bar, baz); }; const auto lambdaRes = curry (lambda) (4) (2); std::cout << lambdaRes << std::endl; }

.

:

CurryImpl , std::function ?
std::function type erasure, . , .
std::tuple ?
, , , .

C++14, C++11 ?
, . C++14 , :
auto , decltype ; compile-time- ( std::index_sequence_for , ).
, , , β€” decltype .

, ?
.

?
: - , , . , , .

production-ready?
, C++14 - , .
std::get (m_prevArgs)... std::get (m_prevArgs), std::get (m_prevArgs), Is .

, , C++11, C++14 STL! , . , std::index_sequence_for , ( PrevArgs... ), invoke invokeIndexed :
return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {});
invokeIndexed std::index_sequence , :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return m_f (std::get<Is> (m_prevArgs)..., arg); }

! ! , , :
struct Foo { auto doFoo (int baz, int qux) { return (m_bar + baz) / qux; } }; // ... Foo someFoo; const auto fooRes = Curry (&Foo::doFoo) (&someFoo) (2) (4);
: m_f (arguments) well-formed, m_f β€” - .

, , , , . , m_f , --. , :
template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } };
invokeIndexed :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); }
, - SFINAE , Invoke , Args... β€” , . , , , :
template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } };
, , , , : c , , , -- . :
struct Foo { int m_state = 42; auto doFoo (int bar) { m_state += bar; return m_state; } }; Foo foo; curry (&Foo::doFoo) (foo) (1); // foo.m_state 42 curry (&Foo::doFoo) (&foo) (1); // foo.m_state 43

, -, .

#include <tuple> #include <type_traits> #include <utility> #include <iostream> #include <string> template<typename F, typename... PrevArgs> class CurryImpl { const F m_f; const std::tuple<PrevArgs...> m_prevArgs; public: CurryImpl (F f, const std::tuple<PrevArgs...>& prev) : m_f { f } , m_prevArgs { prev } { } template<typename T> auto operator() (const T& arg) const { return invoke (arg, 0); } private: template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg, int) const { return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {}); } template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } }; template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } }; template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); } template<typename T> auto invoke (const T& arg, ...) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } }; template<typename F> auto curry (F f) { return CurryImpl<F> { f, {} }; } auto test (int t1, int t2, double t3, const std::string& str) { return t1 * t2 * t3 * str.size (); } struct Foo { int m_bar; auto doFoo (int baz, int qux) { auto result = (m_bar + baz) / qux; ++m_bar; return result; } }; int main () { const auto res = curry (test) (1) (2) (3.0) ("four"); std::cout << res << std::endl; Foo someFoo { 42 }; const auto fooRes = curry (&Foo::doFoo) (&someFoo) (2) (4); std::cout << fooRes << " " << someFoo.m_bar << std::endl; someFoo.m_bar = 42; auto lambda = [someFoo] (int bar, int baz) mutable { return someFoo.doFoo (bar, baz); }; const auto lambdaRes = curry (lambda) (4) (2); std::cout << lambdaRes << std::endl; }

.

:

CurryImpl , std::function ?
std::function type erasure, . , .
std::tuple ?
, , , .

C++14, C++11 ?
, . C++14 , :
auto , decltype ; compile-time- ( std::index_sequence_for , ).
, , , β€” decltype .

, ?
.

?
: - , , . , , .

production-ready?
, C++14 - , .
 std::get (m_prevArgs)...    std::get (m_prevArgs), std::get (m_prevArgs),       Is . 

, , C++11, C++14 STL! , . , std::index_sequence_for , ( PrevArgs... ), invoke invokeIndexed :
return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {});
invokeIndexed std::index_sequence , :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return m_f (std::get<Is> (m_prevArgs)..., arg); }

! ! , , :
struct Foo { auto doFoo (int baz, int qux) { return (m_bar + baz) / qux; } }; // ... Foo someFoo; const auto fooRes = Curry (&Foo::doFoo) (&someFoo) (2) (4);
: m_f (arguments) well-formed, m_f β€” - .

, , , , . , m_f , --. , :
template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } };
invokeIndexed :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); }
, - SFINAE , Invoke , Args... β€” , . , , , :
template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } };
, , , , : c , , , -- . :
struct Foo { int m_state = 42; auto doFoo (int bar) { m_state += bar; return m_state; } }; Foo foo; curry (&Foo::doFoo) (foo) (1); // foo.m_state 42 curry (&Foo::doFoo) (&foo) (1); // foo.m_state 43

, -, .

#include <tuple> #include <type_traits> #include <utility> #include <iostream> #include <string> template<typename F, typename... PrevArgs> class CurryImpl { const F m_f; const std::tuple<PrevArgs...> m_prevArgs; public: CurryImpl (F f, const std::tuple<PrevArgs...>& prev) : m_f { f } , m_prevArgs { prev } { } template<typename T> auto operator() (const T& arg) const { return invoke (arg, 0); } private: template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg, int) const { return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {}); } template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } }; template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } }; template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); } template<typename T> auto invoke (const T& arg, ...) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } }; template<typename F> auto curry (F f) { return CurryImpl<F> { f, {} }; } auto test (int t1, int t2, double t3, const std::string& str) { return t1 * t2 * t3 * str.size (); } struct Foo { int m_bar; auto doFoo (int baz, int qux) { auto result = (m_bar + baz) / qux; ++m_bar; return result; } }; int main () { const auto res = curry (test) (1) (2) (3.0) ("four"); std::cout << res << std::endl; Foo someFoo { 42 }; const auto fooRes = curry (&Foo::doFoo) (&someFoo) (2) (4); std::cout << fooRes << " " << someFoo.m_bar << std::endl; someFoo.m_bar = 42; auto lambda = [someFoo] (int bar, int baz) mutable { return someFoo.doFoo (bar, baz); }; const auto lambdaRes = curry (lambda) (4) (2); std::cout << lambdaRes << std::endl; }

.

:

CurryImpl , std::function ?
std::function type erasure, . , .
std::tuple ?
, , , .

C++14, C++11 ?
, . C++14 , :
auto , decltype ; compile-time- ( std::index_sequence_for , ).
, , , β€” decltype .

, ?
.

?
: - , , . , , .

production-ready?
, C++14 - , .
std::get (m_prevArgs)... std::get (m_prevArgs), std::get (m_prevArgs), Is .

, , C++11, C++14 STL! , . , std::index_sequence_for , ( PrevArgs... ), invoke invokeIndexed :
return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {});
invokeIndexed std::index_sequence , :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return m_f (std::get<Is> (m_prevArgs)..., arg); }

! ! , , :
struct Foo { auto doFoo (int baz, int qux) { return (m_bar + baz) / qux; } }; // ... Foo someFoo; const auto fooRes = Curry (&Foo::doFoo) (&someFoo) (2) (4);
: m_f (arguments) well-formed, m_f β€” - .

, , , , . , m_f , --. , :
template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } };
invokeIndexed :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); }
, - SFINAE , Invoke , Args... β€” , . , , , :
template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } };
, , , , : c , , , -- . :
struct Foo { int m_state = 42; auto doFoo (int bar) { m_state += bar; return m_state; } }; Foo foo; curry (&Foo::doFoo) (foo) (1); // foo.m_state 42 curry (&Foo::doFoo) (&foo) (1); // foo.m_state 43

, -, .

#include <tuple> #include <type_traits> #include <utility> #include <iostream> #include <string> template<typename F, typename... PrevArgs> class CurryImpl { const F m_f; const std::tuple<PrevArgs...> m_prevArgs; public: CurryImpl (F f, const std::tuple<PrevArgs...>& prev) : m_f { f } , m_prevArgs { prev } { } template<typename T> auto operator() (const T& arg) const { return invoke (arg, 0); } private: template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg, int) const { return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {}); } template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } }; template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } }; template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); } template<typename T> auto invoke (const T& arg, ...) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } }; template<typename F> auto curry (F f) { return CurryImpl<F> { f, {} }; } auto test (int t1, int t2, double t3, const std::string& str) { return t1 * t2 * t3 * str.size (); } struct Foo { int m_bar; auto doFoo (int baz, int qux) { auto result = (m_bar + baz) / qux; ++m_bar; return result; } }; int main () { const auto res = curry (test) (1) (2) (3.0) ("four"); std::cout << res << std::endl; Foo someFoo { 42 }; const auto fooRes = curry (&Foo::doFoo) (&someFoo) (2) (4); std::cout << fooRes << " " << someFoo.m_bar << std::endl; someFoo.m_bar = 42; auto lambda = [someFoo] (int bar, int baz) mutable { return someFoo.doFoo (bar, baz); }; const auto lambdaRes = curry (lambda) (4) (2); std::cout << lambdaRes << std::endl; }

.

:

CurryImpl , std::function ?
std::function type erasure, . , .
std::tuple ?
, , , .

C++14, C++11 ?
, . C++14 , :
auto , decltype ; compile-time- ( std::index_sequence_for , ).
, , , β€” decltype .

, ?
.

?
: - , , . , , .

production-ready?
, C++14 - , .
 std::get (m_prevArgs)...    std::get (m_prevArgs), std::get (m_prevArgs),       Is . 

, , C++11, C++14 STL! , . , std::index_sequence_for , ( PrevArgs... ), invoke invokeIndexed :
return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {});
invokeIndexed std::index_sequence , :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return m_f (std::get<Is> (m_prevArgs)..., arg); }

! ! , , :
struct Foo { auto doFoo (int baz, int qux) { return (m_bar + baz) / qux; } }; // ... Foo someFoo; const auto fooRes = Curry (&Foo::doFoo) (&someFoo) (2) (4);
: m_f (arguments) well-formed, m_f β€” - .

, , , , . , m_f , --. , :
template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } };
invokeIndexed :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); }
, - SFINAE , Invoke , Args... β€” , . , , , :
template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } };
, , , , : c , , , -- . :
struct Foo { int m_state = 42; auto doFoo (int bar) { m_state += bar; return m_state; } }; Foo foo; curry (&Foo::doFoo) (foo) (1); // foo.m_state 42 curry (&Foo::doFoo) (&foo) (1); // foo.m_state 43

, -, .

#include <tuple> #include <type_traits> #include <utility> #include <iostream> #include <string> template<typename F, typename... PrevArgs> class CurryImpl { const F m_f; const std::tuple<PrevArgs...> m_prevArgs; public: CurryImpl (F f, const std::tuple<PrevArgs...>& prev) : m_f { f } , m_prevArgs { prev } { } template<typename T> auto operator() (const T& arg) const { return invoke (arg, 0); } private: template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg, int) const { return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {}); } template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } }; template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } }; template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); } template<typename T> auto invoke (const T& arg, ...) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } }; template<typename F> auto curry (F f) { return CurryImpl<F> { f, {} }; } auto test (int t1, int t2, double t3, const std::string& str) { return t1 * t2 * t3 * str.size (); } struct Foo { int m_bar; auto doFoo (int baz, int qux) { auto result = (m_bar + baz) / qux; ++m_bar; return result; } }; int main () { const auto res = curry (test) (1) (2) (3.0) ("four"); std::cout << res << std::endl; Foo someFoo { 42 }; const auto fooRes = curry (&Foo::doFoo) (&someFoo) (2) (4); std::cout << fooRes << " " << someFoo.m_bar << std::endl; someFoo.m_bar = 42; auto lambda = [someFoo] (int bar, int baz) mutable { return someFoo.doFoo (bar, baz); }; const auto lambdaRes = curry (lambda) (4) (2); std::cout << lambdaRes << std::endl; }

.

:

CurryImpl , std::function ?
std::function type erasure, . , .
std::tuple ?
, , , .

C++14, C++11 ?
, . C++14 , :
auto , decltype ; compile-time- ( std::index_sequence_for , ).
, , , β€” decltype .

, ?
.

?
: - , , . , , .

production-ready?
, C++14 - , .
std::get (m_prevArgs)... std::get (m_prevArgs), std::get (m_prevArgs), Is .

, , C++11, C++14 STL! , . , std::index_sequence_for , ( PrevArgs... ), invoke invokeIndexed :
return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {});
invokeIndexed std::index_sequence , :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return m_f (std::get<Is> (m_prevArgs)..., arg); }

! ! , , :
struct Foo { auto doFoo (int baz, int qux) { return (m_bar + baz) / qux; } }; // ... Foo someFoo; const auto fooRes = Curry (&Foo::doFoo) (&someFoo) (2) (4);
: m_f (arguments) well-formed, m_f β€” - .

, , , , . , m_f , --. , :
template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } };
invokeIndexed :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); }
, - SFINAE , Invoke , Args... β€” , . , , , :
template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } };
, , , , : c , , , -- . :
struct Foo { int m_state = 42; auto doFoo (int bar) { m_state += bar; return m_state; } }; Foo foo; curry (&Foo::doFoo) (foo) (1); // foo.m_state 42 curry (&Foo::doFoo) (&foo) (1); // foo.m_state 43

, -, .

#include <tuple> #include <type_traits> #include <utility> #include <iostream> #include <string> template<typename F, typename... PrevArgs> class CurryImpl { const F m_f; const std::tuple<PrevArgs...> m_prevArgs; public: CurryImpl (F f, const std::tuple<PrevArgs...>& prev) : m_f { f } , m_prevArgs { prev } { } template<typename T> auto operator() (const T& arg) const { return invoke (arg, 0); } private: template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg, int) const { return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {}); } template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } }; template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } }; template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); } template<typename T> auto invoke (const T& arg, ...) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } }; template<typename F> auto curry (F f) { return CurryImpl<F> { f, {} }; } auto test (int t1, int t2, double t3, const std::string& str) { return t1 * t2 * t3 * str.size (); } struct Foo { int m_bar; auto doFoo (int baz, int qux) { auto result = (m_bar + baz) / qux; ++m_bar; return result; } }; int main () { const auto res = curry (test) (1) (2) (3.0) ("four"); std::cout << res << std::endl; Foo someFoo { 42 }; const auto fooRes = curry (&Foo::doFoo) (&someFoo) (2) (4); std::cout << fooRes << " " << someFoo.m_bar << std::endl; someFoo.m_bar = 42; auto lambda = [someFoo] (int bar, int baz) mutable { return someFoo.doFoo (bar, baz); }; const auto lambdaRes = curry (lambda) (4) (2); std::cout << lambdaRes << std::endl; }

.

:

CurryImpl , std::function ?
std::function type erasure, . , .
std::tuple ?
, , , .

C++14, C++11 ?
, . C++14 , :
auto , decltype ; compile-time- ( std::index_sequence_for , ).
, , , β€” decltype .

, ?
.

?
: - , , . , , .

production-ready?
, C++14 - , .
 std::get (m_prevArgs)...    std::get (m_prevArgs), std::get (m_prevArgs),       Is . 

, , C++11, C++14 STL! , . , std::index_sequence_for , ( PrevArgs... ), invoke invokeIndexed :
return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {});
invokeIndexed std::index_sequence , :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return m_f (std::get<Is> (m_prevArgs)..., arg); }

! ! , , :
struct Foo { auto doFoo (int baz, int qux) { return (m_bar + baz) / qux; } }; // ... Foo someFoo; const auto fooRes = Curry (&Foo::doFoo) (&someFoo) (2) (4);
: m_f (arguments) well-formed, m_f β€” - .

, , , , . , m_f , --. , :
template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } };
invokeIndexed :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); }
, - SFINAE , Invoke , Args... β€” , . , , , :
template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } };
, , , , : c , , , -- . :
struct Foo { int m_state = 42; auto doFoo (int bar) { m_state += bar; return m_state; } }; Foo foo; curry (&Foo::doFoo) (foo) (1); // foo.m_state 42 curry (&Foo::doFoo) (&foo) (1); // foo.m_state 43

, -, .

#include <tuple> #include <type_traits> #include <utility> #include <iostream> #include <string> template<typename F, typename... PrevArgs> class CurryImpl { const F m_f; const std::tuple<PrevArgs...> m_prevArgs; public: CurryImpl (F f, const std::tuple<PrevArgs...>& prev) : m_f { f } , m_prevArgs { prev } { } template<typename T> auto operator() (const T& arg) const { return invoke (arg, 0); } private: template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg, int) const { return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {}); } template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } }; template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } }; template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); } template<typename T> auto invoke (const T& arg, ...) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } }; template<typename F> auto curry (F f) { return CurryImpl<F> { f, {} }; } auto test (int t1, int t2, double t3, const std::string& str) { return t1 * t2 * t3 * str.size (); } struct Foo { int m_bar; auto doFoo (int baz, int qux) { auto result = (m_bar + baz) / qux; ++m_bar; return result; } }; int main () { const auto res = curry (test) (1) (2) (3.0) ("four"); std::cout << res << std::endl; Foo someFoo { 42 }; const auto fooRes = curry (&Foo::doFoo) (&someFoo) (2) (4); std::cout << fooRes << " " << someFoo.m_bar << std::endl; someFoo.m_bar = 42; auto lambda = [someFoo] (int bar, int baz) mutable { return someFoo.doFoo (bar, baz); }; const auto lambdaRes = curry (lambda) (4) (2); std::cout << lambdaRes << std::endl; }

.

:

CurryImpl , std::function ?
std::function type erasure, . , .
std::tuple ?
, , , .

C++14, C++11 ?
, . C++14 , :
auto , decltype ; compile-time- ( std::index_sequence_for , ).
, , , β€” decltype .

, ?
.

?
: - , , . , , .

production-ready?
, C++14 - , .
std::get (m_prevArgs)... std::get (m_prevArgs), std::get (m_prevArgs), Is .

, , C++11, C++14 STL! , . , std::index_sequence_for , ( PrevArgs... ), invoke invokeIndexed :
return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {});
invokeIndexed std::index_sequence , :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return m_f (std::get<Is> (m_prevArgs)..., arg); }

! ! , , :
struct Foo { auto doFoo (int baz, int qux) { return (m_bar + baz) / qux; } }; // ... Foo someFoo; const auto fooRes = Curry (&Foo::doFoo) (&someFoo) (2) (4);
: m_f (arguments) well-formed, m_f β€” - .

, , , , . , m_f , --. , :
template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } };
invokeIndexed :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); }
, - SFINAE , Invoke , Args... β€” , . , , , :
template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } };
, , , , : c , , , -- . :
struct Foo { int m_state = 42; auto doFoo (int bar) { m_state += bar; return m_state; } }; Foo foo; curry (&Foo::doFoo) (foo) (1); // foo.m_state 42 curry (&Foo::doFoo) (&foo) (1); // foo.m_state 43

, -, .

#include <tuple> #include <type_traits> #include <utility> #include <iostream> #include <string> template<typename F, typename... PrevArgs> class CurryImpl { const F m_f; const std::tuple<PrevArgs...> m_prevArgs; public: CurryImpl (F f, const std::tuple<PrevArgs...>& prev) : m_f { f } , m_prevArgs { prev } { } template<typename T> auto operator() (const T& arg) const { return invoke (arg, 0); } private: template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg, int) const { return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {}); } template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } }; template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } }; template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); } template<typename T> auto invoke (const T& arg, ...) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } }; template<typename F> auto curry (F f) { return CurryImpl<F> { f, {} }; } auto test (int t1, int t2, double t3, const std::string& str) { return t1 * t2 * t3 * str.size (); } struct Foo { int m_bar; auto doFoo (int baz, int qux) { auto result = (m_bar + baz) / qux; ++m_bar; return result; } }; int main () { const auto res = curry (test) (1) (2) (3.0) ("four"); std::cout << res << std::endl; Foo someFoo { 42 }; const auto fooRes = curry (&Foo::doFoo) (&someFoo) (2) (4); std::cout << fooRes << " " << someFoo.m_bar << std::endl; someFoo.m_bar = 42; auto lambda = [someFoo] (int bar, int baz) mutable { return someFoo.doFoo (bar, baz); }; const auto lambdaRes = curry (lambda) (4) (2); std::cout << lambdaRes << std::endl; }

.

:

CurryImpl , std::function ?
std::function type erasure, . , .
std::tuple ?
, , , .

C++14, C++11 ?
, . C++14 , :
auto , decltype ; compile-time- ( std::index_sequence_for , ).
, , , β€” decltype .

, ?
.

?
: - , , . , , .

production-ready?
, C++14 - , .
 std::get (m_prevArgs)...    std::get (m_prevArgs), std::get (m_prevArgs),       Is . 

, , C++11, C++14 STL! , . , std::index_sequence_for , ( PrevArgs... ), invoke invokeIndexed :
return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {});
invokeIndexed std::index_sequence , :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return m_f (std::get<Is> (m_prevArgs)..., arg); }

! ! , , :
struct Foo { auto doFoo (int baz, int qux) { return (m_bar + baz) / qux; } }; // ... Foo someFoo; const auto fooRes = Curry (&Foo::doFoo) (&someFoo) (2) (4);
: m_f (arguments) well-formed, m_f β€” - .

, , , , . , m_f , --. , :
template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } };
invokeIndexed :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); }
, - SFINAE , Invoke , Args... β€” , . , , , :
template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } };
, , , , : c , , , -- . :
struct Foo { int m_state = 42; auto doFoo (int bar) { m_state += bar; return m_state; } }; Foo foo; curry (&Foo::doFoo) (foo) (1); // foo.m_state 42 curry (&Foo::doFoo) (&foo) (1); // foo.m_state 43

, -, .

#include <tuple> #include <type_traits> #include <utility> #include <iostream> #include <string> template<typename F, typename... PrevArgs> class CurryImpl { const F m_f; const std::tuple<PrevArgs...> m_prevArgs; public: CurryImpl (F f, const std::tuple<PrevArgs...>& prev) : m_f { f } , m_prevArgs { prev } { } template<typename T> auto operator() (const T& arg) const { return invoke (arg, 0); } private: template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg, int) const { return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {}); } template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } }; template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } }; template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); } template<typename T> auto invoke (const T& arg, ...) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } }; template<typename F> auto curry (F f) { return CurryImpl<F> { f, {} }; } auto test (int t1, int t2, double t3, const std::string& str) { return t1 * t2 * t3 * str.size (); } struct Foo { int m_bar; auto doFoo (int baz, int qux) { auto result = (m_bar + baz) / qux; ++m_bar; return result; } }; int main () { const auto res = curry (test) (1) (2) (3.0) ("four"); std::cout << res << std::endl; Foo someFoo { 42 }; const auto fooRes = curry (&Foo::doFoo) (&someFoo) (2) (4); std::cout << fooRes << " " << someFoo.m_bar << std::endl; someFoo.m_bar = 42; auto lambda = [someFoo] (int bar, int baz) mutable { return someFoo.doFoo (bar, baz); }; const auto lambdaRes = curry (lambda) (4) (2); std::cout << lambdaRes << std::endl; }

.

:

CurryImpl , std::function ?
std::function type erasure, . , .
std::tuple ?
, , , .

C++14, C++11 ?
, . C++14 , :
auto , decltype ; compile-time- ( std::index_sequence_for , ).
, , , β€” decltype .

, ?
.

?
: - , , . , , .

production-ready?
, C++14 - , .
std::get (m_prevArgs)... std::get (m_prevArgs), std::get (m_prevArgs), Is .

, , C++11, C++14 STL! , . , std::index_sequence_for , ( PrevArgs... ), invoke invokeIndexed :
return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {});
invokeIndexed std::index_sequence , :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return m_f (std::get<Is> (m_prevArgs)..., arg); }

! ! , , :
struct Foo { auto doFoo (int baz, int qux) { return (m_bar + baz) / qux; } }; // ... Foo someFoo; const auto fooRes = Curry (&Foo::doFoo) (&someFoo) (2) (4);
: m_f (arguments) well-formed, m_f β€” - .

, , , , . , m_f , --. , :
template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } };
invokeIndexed :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); }
, - SFINAE , Invoke , Args... β€” , . , , , :
template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } };
, , , , : c , , , -- . :
struct Foo { int m_state = 42; auto doFoo (int bar) { m_state += bar; return m_state; } }; Foo foo; curry (&Foo::doFoo) (foo) (1); // foo.m_state 42 curry (&Foo::doFoo) (&foo) (1); // foo.m_state 43

, -, .

#include <tuple> #include <type_traits> #include <utility> #include <iostream> #include <string> template<typename F, typename... PrevArgs> class CurryImpl { const F m_f; const std::tuple<PrevArgs...> m_prevArgs; public: CurryImpl (F f, const std::tuple<PrevArgs...>& prev) : m_f { f } , m_prevArgs { prev } { } template<typename T> auto operator() (const T& arg) const { return invoke (arg, 0); } private: template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg, int) const { return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {}); } template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } }; template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } }; template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); } template<typename T> auto invoke (const T& arg, ...) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } }; template<typename F> auto curry (F f) { return CurryImpl<F> { f, {} }; } auto test (int t1, int t2, double t3, const std::string& str) { return t1 * t2 * t3 * str.size (); } struct Foo { int m_bar; auto doFoo (int baz, int qux) { auto result = (m_bar + baz) / qux; ++m_bar; return result; } }; int main () { const auto res = curry (test) (1) (2) (3.0) ("four"); std::cout << res << std::endl; Foo someFoo { 42 }; const auto fooRes = curry (&Foo::doFoo) (&someFoo) (2) (4); std::cout << fooRes << " " << someFoo.m_bar << std::endl; someFoo.m_bar = 42; auto lambda = [someFoo] (int bar, int baz) mutable { return someFoo.doFoo (bar, baz); }; const auto lambdaRes = curry (lambda) (4) (2); std::cout << lambdaRes << std::endl; }

.

:

CurryImpl , std::function ?
std::function type erasure, . , .
std::tuple ?
, , , .

C++14, C++11 ?
, . C++14 , :
auto , decltype ; compile-time- ( std::index_sequence_for , ).
, , , β€” decltype .

, ?
.

?
: - , , . , , .

production-ready?
, C++14 - , .
std::get (m_prevArgs)... std::get (m_prevArgs), std::get (m_prevArgs), Is .

, , C++11, C++14 STL! , . , std::index_sequence_for , ( PrevArgs... ), invoke invokeIndexed :
return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {});
invokeIndexed std::index_sequence , :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return m_f (std::get<Is> (m_prevArgs)..., arg); }

! ! , , :
struct Foo { auto doFoo (int baz, int qux) { return (m_bar + baz) / qux; } }; // ... Foo someFoo; const auto fooRes = Curry (&Foo::doFoo) (&someFoo) (2) (4);
: m_f (arguments) well-formed, m_f β€” - .

, , , , . , m_f , --. , :
template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } };
invokeIndexed :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); }
, - SFINAE , Invoke , Args... β€” , . , , , :
template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } };
, , , , : c , , , -- . :
struct Foo { int m_state = 42; auto doFoo (int bar) { m_state += bar; return m_state; } }; Foo foo; curry (&Foo::doFoo) (foo) (1); // foo.m_state 42 curry (&Foo::doFoo) (&foo) (1); // foo.m_state 43

, -, .

#include <tuple> #include <type_traits> #include <utility> #include <iostream> #include <string> template<typename F, typename... PrevArgs> class CurryImpl { const F m_f; const std::tuple<PrevArgs...> m_prevArgs; public: CurryImpl (F f, const std::tuple<PrevArgs...>& prev) : m_f { f } , m_prevArgs { prev } { } template<typename T> auto operator() (const T& arg) const { return invoke (arg, 0); } private: template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg, int) const { return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {}); } template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } }; template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } }; template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); } template<typename T> auto invoke (const T& arg, ...) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } }; template<typename F> auto curry (F f) { return CurryImpl<F> { f, {} }; } auto test (int t1, int t2, double t3, const std::string& str) { return t1 * t2 * t3 * str.size (); } struct Foo { int m_bar; auto doFoo (int baz, int qux) { auto result = (m_bar + baz) / qux; ++m_bar; return result; } }; int main () { const auto res = curry (test) (1) (2) (3.0) ("four"); std::cout << res << std::endl; Foo someFoo { 42 }; const auto fooRes = curry (&Foo::doFoo) (&someFoo) (2) (4); std::cout << fooRes << " " << someFoo.m_bar << std::endl; someFoo.m_bar = 42; auto lambda = [someFoo] (int bar, int baz) mutable { return someFoo.doFoo (bar, baz); }; const auto lambdaRes = curry (lambda) (4) (2); std::cout << lambdaRes << std::endl; }

.

:

CurryImpl , std::function ?
std::function type erasure, . , .
std::tuple ?
, , , .

C++14, C++11 ?
, . C++14 , :
auto , decltype ; compile-time- ( std::index_sequence_for , ).
, , , β€” decltype .

, ?
.

?
: - , , . , , .

production-ready?
, C++14 - , .
 std::get (m_prevArgs)...    std::get (m_prevArgs), std::get (m_prevArgs),       Is . 

, , C++11, C++14 STL! , . , std::index_sequence_for , ( PrevArgs... ), invoke invokeIndexed :
return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {});
invokeIndexed std::index_sequence , :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return m_f (std::get<Is> (m_prevArgs)..., arg); }

! ! , , :
struct Foo { auto doFoo (int baz, int qux) { return (m_bar + baz) / qux; } }; // ... Foo someFoo; const auto fooRes = Curry (&Foo::doFoo) (&someFoo) (2) (4);
: m_f (arguments) well-formed, m_f β€” - .

, , , , . , m_f , --. , :
template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } };
invokeIndexed :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); }
, - SFINAE , Invoke , Args... β€” , . , , , :
template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } };
, , , , : c , , , -- . :
struct Foo { int m_state = 42; auto doFoo (int bar) { m_state += bar; return m_state; } }; Foo foo; curry (&Foo::doFoo) (foo) (1); // foo.m_state 42 curry (&Foo::doFoo) (&foo) (1); // foo.m_state 43

, -, .

#include <tuple> #include <type_traits> #include <utility> #include <iostream> #include <string> template<typename F, typename... PrevArgs> class CurryImpl { const F m_f; const std::tuple<PrevArgs...> m_prevArgs; public: CurryImpl (F f, const std::tuple<PrevArgs...>& prev) : m_f { f } , m_prevArgs { prev } { } template<typename T> auto operator() (const T& arg) const { return invoke (arg, 0); } private: template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg, int) const { return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {}); } template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } }; template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } }; template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); } template<typename T> auto invoke (const T& arg, ...) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } }; template<typename F> auto curry (F f) { return CurryImpl<F> { f, {} }; } auto test (int t1, int t2, double t3, const std::string& str) { return t1 * t2 * t3 * str.size (); } struct Foo { int m_bar; auto doFoo (int baz, int qux) { auto result = (m_bar + baz) / qux; ++m_bar; return result; } }; int main () { const auto res = curry (test) (1) (2) (3.0) ("four"); std::cout << res << std::endl; Foo someFoo { 42 }; const auto fooRes = curry (&Foo::doFoo) (&someFoo) (2) (4); std::cout << fooRes << " " << someFoo.m_bar << std::endl; someFoo.m_bar = 42; auto lambda = [someFoo] (int bar, int baz) mutable { return someFoo.doFoo (bar, baz); }; const auto lambdaRes = curry (lambda) (4) (2); std::cout << lambdaRes << std::endl; }

.

:

CurryImpl , std::function ?
std::function type erasure, . , .
std::tuple ?
, , , .

C++14, C++11 ?
, . C++14 , :
auto , decltype ; compile-time- ( std::index_sequence_for , ).
, , , β€” decltype .

, ?
.

?
: - , , . , , .

production-ready?
, C++14 - , .
std::get (m_prevArgs)... std::get (m_prevArgs), std::get (m_prevArgs), Is .

, , C++11, C++14 STL! , . , std::index_sequence_for , ( PrevArgs... ), invoke invokeIndexed :
return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {});
invokeIndexed std::index_sequence , :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return m_f (std::get<Is> (m_prevArgs)..., arg); }

! ! , , :
struct Foo { auto doFoo (int baz, int qux) { return (m_bar + baz) / qux; } }; // ... Foo someFoo; const auto fooRes = Curry (&Foo::doFoo) (&someFoo) (2) (4);
: m_f (arguments) well-formed, m_f β€” - .

, , , , . , m_f , --. , :
template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } };
invokeIndexed :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); }
, - SFINAE , Invoke , Args... β€” , . , , , :
template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } };
, , , , : c , , , -- . :
struct Foo { int m_state = 42; auto doFoo (int bar) { m_state += bar; return m_state; } }; Foo foo; curry (&Foo::doFoo) (foo) (1); // foo.m_state 42 curry (&Foo::doFoo) (&foo) (1); // foo.m_state 43

, -, .

#include <tuple> #include <type_traits> #include <utility> #include <iostream> #include <string> template<typename F, typename... PrevArgs> class CurryImpl { const F m_f; const std::tuple<PrevArgs...> m_prevArgs; public: CurryImpl (F f, const std::tuple<PrevArgs...>& prev) : m_f { f } , m_prevArgs { prev } { } template<typename T> auto operator() (const T& arg) const { return invoke (arg, 0); } private: template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg, int) const { return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {}); } template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } }; template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } }; template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); } template<typename T> auto invoke (const T& arg, ...) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } }; template<typename F> auto curry (F f) { return CurryImpl<F> { f, {} }; } auto test (int t1, int t2, double t3, const std::string& str) { return t1 * t2 * t3 * str.size (); } struct Foo { int m_bar; auto doFoo (int baz, int qux) { auto result = (m_bar + baz) / qux; ++m_bar; return result; } }; int main () { const auto res = curry (test) (1) (2) (3.0) ("four"); std::cout << res << std::endl; Foo someFoo { 42 }; const auto fooRes = curry (&Foo::doFoo) (&someFoo) (2) (4); std::cout << fooRes << " " << someFoo.m_bar << std::endl; someFoo.m_bar = 42; auto lambda = [someFoo] (int bar, int baz) mutable { return someFoo.doFoo (bar, baz); }; const auto lambdaRes = curry (lambda) (4) (2); std::cout << lambdaRes << std::endl; }

.

:

CurryImpl , std::function ?
std::function type erasure, . , .
std::tuple ?
, , , .

C++14, C++11 ?
, . C++14 , :
auto , decltype ; compile-time- ( std::index_sequence_for , ).
, , , β€” decltype .

, ?
.

?
: - , , . , , .

production-ready?
, C++14 - , .
std::get (m_prevArgs)... std::get (m_prevArgs), std::get (m_prevArgs), Is .

, , C++11, C++14 STL! , . , std::index_sequence_for , ( PrevArgs... ), invoke invokeIndexed :
return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {});
invokeIndexed std::index_sequence , :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return m_f (std::get<Is> (m_prevArgs)..., arg); }

! ! , , :
struct Foo { auto doFoo (int baz, int qux) { return (m_bar + baz) / qux; } }; // ... Foo someFoo; const auto fooRes = Curry (&Foo::doFoo) (&someFoo) (2) (4);
: m_f (arguments) well-formed, m_f β€” - .

, , , , . , m_f , --. , :
template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } };
invokeIndexed :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); }
, - SFINAE , Invoke , Args... β€” , . , , , :
template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } };
, , , , : c , , , -- . :
struct Foo { int m_state = 42; auto doFoo (int bar) { m_state += bar; return m_state; } }; Foo foo; curry (&Foo::doFoo) (foo) (1); // foo.m_state 42 curry (&Foo::doFoo) (&foo) (1); // foo.m_state 43

, -, .

#include <tuple> #include <type_traits> #include <utility> #include <iostream> #include <string> template<typename F, typename... PrevArgs> class CurryImpl { const F m_f; const std::tuple<PrevArgs...> m_prevArgs; public: CurryImpl (F f, const std::tuple<PrevArgs...>& prev) : m_f { f } , m_prevArgs { prev } { } template<typename T> auto operator() (const T& arg) const { return invoke (arg, 0); } private: template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg, int) const { return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {}); } template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } }; template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } }; template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); } template<typename T> auto invoke (const T& arg, ...) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } }; template<typename F> auto curry (F f) { return CurryImpl<F> { f, {} }; } auto test (int t1, int t2, double t3, const std::string& str) { return t1 * t2 * t3 * str.size (); } struct Foo { int m_bar; auto doFoo (int baz, int qux) { auto result = (m_bar + baz) / qux; ++m_bar; return result; } }; int main () { const auto res = curry (test) (1) (2) (3.0) ("four"); std::cout << res << std::endl; Foo someFoo { 42 }; const auto fooRes = curry (&Foo::doFoo) (&someFoo) (2) (4); std::cout << fooRes << " " << someFoo.m_bar << std::endl; someFoo.m_bar = 42; auto lambda = [someFoo] (int bar, int baz) mutable { return someFoo.doFoo (bar, baz); }; const auto lambdaRes = curry (lambda) (4) (2); std::cout << lambdaRes << std::endl; }

.

:

CurryImpl , std::function ?
std::function type erasure, . , .
std::tuple ?
, , , .

C++14, C++11 ?
, . C++14 , :
auto , decltype ; compile-time- ( std::index_sequence_for , ).
, , , β€” decltype .

, ?
.

?
: - , , . , , .

production-ready?
, C++14 - , .
  1. std::get (m_prevArgs)... std::get (m_prevArgs), std::get (m_prevArgs), Is .

    , , C++11, C++14 STL! , . , std::index_sequence_for , ( PrevArgs... ), invoke invokeIndexed :
    return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {});
    invokeIndexed std::index_sequence , :
    template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return m_f (std::get<Is> (m_prevArgs)..., arg); }

    ! ! , , :
    struct Foo { auto doFoo (int baz, int qux) { return (m_bar + baz) / qux; } }; // ... Foo someFoo; const auto fooRes = Curry (&Foo::doFoo) (&someFoo) (2) (4);
    : m_f (arguments) well-formed, m_f β€” - .

    , , , , . , m_f , --. , :
    template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } };
    invokeIndexed :
    template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); }
    , - SFINAE , Invoke , Args... β€” , . , , , :
    template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } };
    , , , , : c , , , -- . :
    struct Foo { int m_state = 42; auto doFoo (int bar) { m_state += bar; return m_state; } }; Foo foo; curry (&Foo::doFoo) (foo) (1); // foo.m_state 42 curry (&Foo::doFoo) (&foo) (1); // foo.m_state 43

    , -, .

    #include <tuple> #include <type_traits> #include <utility> #include <iostream> #include <string> template<typename F, typename... PrevArgs> class CurryImpl { const F m_f; const std::tuple<PrevArgs...> m_prevArgs; public: CurryImpl (F f, const std::tuple<PrevArgs...>& prev) : m_f { f } , m_prevArgs { prev } { } template<typename T> auto operator() (const T& arg) const { return invoke (arg, 0); } private: template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg, int) const { return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {}); } template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } }; template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } }; template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); } template<typename T> auto invoke (const T& arg, ...) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } }; template<typename F> auto curry (F f) { return CurryImpl<F> { f, {} }; } auto test (int t1, int t2, double t3, const std::string& str) { return t1 * t2 * t3 * str.size (); } struct Foo { int m_bar; auto doFoo (int baz, int qux) { auto result = (m_bar + baz) / qux; ++m_bar; return result; } }; int main () { const auto res = curry (test) (1) (2) (3.0) ("four"); std::cout << res << std::endl; Foo someFoo { 42 }; const auto fooRes = curry (&Foo::doFoo) (&someFoo) (2) (4); std::cout << fooRes << " " << someFoo.m_bar << std::endl; someFoo.m_bar = 42; auto lambda = [someFoo] (int bar, int baz) mutable { return someFoo.doFoo (bar, baz); }; const auto lambdaRes = curry (lambda) (4) (2); std::cout << lambdaRes << std::endl; }

    .

    :

    CurryImpl , std::function ?
    std::function type erasure, . , .
    std::tuple ?
    , , , .

    C++14, C++11 ?
    , . C++14 , :
    auto , decltype ; compile-time- ( std::index_sequence_for , ).
    , , , β€” decltype .

    , ?
    .

    ?
    : - , , . , , .

    production-ready?
    , C++14 - , .
  2. std::get (m_prevArgs)... std::get (m_prevArgs), std::get (m_prevArgs), Is .

    , , C++11, C++14 STL! , . , std::index_sequence_for , ( PrevArgs... ), invoke invokeIndexed :
    return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {});
    invokeIndexed std::index_sequence , :
    template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return m_f (std::get<Is> (m_prevArgs)..., arg); }

    ! ! , , :
    struct Foo { auto doFoo (int baz, int qux) { return (m_bar + baz) / qux; } }; // ... Foo someFoo; const auto fooRes = Curry (&Foo::doFoo) (&someFoo) (2) (4);
    : m_f (arguments) well-formed, m_f β€” - .

    , , , , . , m_f , --. , :
    template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } };
    invokeIndexed :
    template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); }
    , - SFINAE , Invoke , Args... β€” , . , , , :
    template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } };
    , , , , : c , , , -- . :
    struct Foo { int m_state = 42; auto doFoo (int bar) { m_state += bar; return m_state; } }; Foo foo; curry (&Foo::doFoo) (foo) (1); // foo.m_state 42 curry (&Foo::doFoo) (&foo) (1); // foo.m_state 43

    , -, .

    #include <tuple> #include <type_traits> #include <utility> #include <iostream> #include <string> template<typename F, typename... PrevArgs> class CurryImpl { const F m_f; const std::tuple<PrevArgs...> m_prevArgs; public: CurryImpl (F f, const std::tuple<PrevArgs...>& prev) : m_f { f } , m_prevArgs { prev } { } template<typename T> auto operator() (const T& arg) const { return invoke (arg, 0); } private: template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg, int) const { return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {}); } template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } }; template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } }; template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); } template<typename T> auto invoke (const T& arg, ...) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } }; template<typename F> auto curry (F f) { return CurryImpl<F> { f, {} }; } auto test (int t1, int t2, double t3, const std::string& str) { return t1 * t2 * t3 * str.size (); } struct Foo { int m_bar; auto doFoo (int baz, int qux) { auto result = (m_bar + baz) / qux; ++m_bar; return result; } }; int main () { const auto res = curry (test) (1) (2) (3.0) ("four"); std::cout << res << std::endl; Foo someFoo { 42 }; const auto fooRes = curry (&Foo::doFoo) (&someFoo) (2) (4); std::cout << fooRes << " " << someFoo.m_bar << std::endl; someFoo.m_bar = 42; auto lambda = [someFoo] (int bar, int baz) mutable { return someFoo.doFoo (bar, baz); }; const auto lambdaRes = curry (lambda) (4) (2); std::cout << lambdaRes << std::endl; }

    .

    :

    CurryImpl , std::function ?
    std::function type erasure, . , .
    std::tuple ?
    , , , .

    C++14, C++11 ?
    , . C++14 , :
    auto , decltype ; compile-time- ( std::index_sequence_for , ).
    , , , β€” decltype .

    , ?
    .

    ?
    : - , , . , , .

    production-ready?
    , C++14 - , .
std::get (m_prevArgs)... std::get (m_prevArgs), std::get (m_prevArgs), Is .

, , C++11, C++14 STL! , . , std::index_sequence_for , ( PrevArgs... ), invoke invokeIndexed :
return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {});
invokeIndexed std::index_sequence , :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return m_f (std::get<Is> (m_prevArgs)..., arg); }

! ! , , :
struct Foo { auto doFoo (int baz, int qux) { return (m_bar + baz) / qux; } }; // ... Foo someFoo; const auto fooRes = Curry (&Foo::doFoo) (&someFoo) (2) (4);
: m_f (arguments) well-formed, m_f β€” - .

, , , , . , m_f , --. , :
template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } };
invokeIndexed :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); }
, - SFINAE , Invoke , Args... β€” , . , , , :
template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } };
, , , , : c , , , -- . :
struct Foo { int m_state = 42; auto doFoo (int bar) { m_state += bar; return m_state; } }; Foo foo; curry (&Foo::doFoo) (foo) (1); // foo.m_state 42 curry (&Foo::doFoo) (&foo) (1); // foo.m_state 43

, -, .

#include <tuple> #include <type_traits> #include <utility> #include <iostream> #include <string> template<typename F, typename... PrevArgs> class CurryImpl { const F m_f; const std::tuple<PrevArgs...> m_prevArgs; public: CurryImpl (F f, const std::tuple<PrevArgs...>& prev) : m_f { f } , m_prevArgs { prev } { } template<typename T> auto operator() (const T& arg) const { return invoke (arg, 0); } private: template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg, int) const { return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {}); } template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } }; template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } }; template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); } template<typename T> auto invoke (const T& arg, ...) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } }; template<typename F> auto curry (F f) { return CurryImpl<F> { f, {} }; } auto test (int t1, int t2, double t3, const std::string& str) { return t1 * t2 * t3 * str.size (); } struct Foo { int m_bar; auto doFoo (int baz, int qux) { auto result = (m_bar + baz) / qux; ++m_bar; return result; } }; int main () { const auto res = curry (test) (1) (2) (3.0) ("four"); std::cout << res << std::endl; Foo someFoo { 42 }; const auto fooRes = curry (&Foo::doFoo) (&someFoo) (2) (4); std::cout << fooRes << " " << someFoo.m_bar << std::endl; someFoo.m_bar = 42; auto lambda = [someFoo] (int bar, int baz) mutable { return someFoo.doFoo (bar, baz); }; const auto lambdaRes = curry (lambda) (4) (2); std::cout << lambdaRes << std::endl; }

.

:

CurryImpl , std::function ?
std::function type erasure, . , .
std::tuple ?
, , , .

C++14, C++11 ?
, . C++14 , :
auto , decltype ; compile-time- ( std::index_sequence_for , ).
, , , β€” decltype .

, ?
.

?
: - , , . , , .

production-ready?
, C++14 - , .
  1. std::get (m_prevArgs)... std::get (m_prevArgs), std::get (m_prevArgs), Is .

    , , C++11, C++14 STL! , . , std::index_sequence_for , ( PrevArgs... ), invoke invokeIndexed :
    return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {});
    invokeIndexed std::index_sequence , :
    template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return m_f (std::get<Is> (m_prevArgs)..., arg); }

    ! ! , , :
    struct Foo { auto doFoo (int baz, int qux) { return (m_bar + baz) / qux; } }; // ... Foo someFoo; const auto fooRes = Curry (&Foo::doFoo) (&someFoo) (2) (4);
    : m_f (arguments) well-formed, m_f β€” - .

    , , , , . , m_f , --. , :
    template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } };
    invokeIndexed :
    template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); }
    , - SFINAE , Invoke , Args... β€” , . , , , :
    template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } };
    , , , , : c , , , -- . :
    struct Foo { int m_state = 42; auto doFoo (int bar) { m_state += bar; return m_state; } }; Foo foo; curry (&Foo::doFoo) (foo) (1); // foo.m_state 42 curry (&Foo::doFoo) (&foo) (1); // foo.m_state 43

    , -, .

    #include <tuple> #include <type_traits> #include <utility> #include <iostream> #include <string> template<typename F, typename... PrevArgs> class CurryImpl { const F m_f; const std::tuple<PrevArgs...> m_prevArgs; public: CurryImpl (F f, const std::tuple<PrevArgs...>& prev) : m_f { f } , m_prevArgs { prev } { } template<typename T> auto operator() (const T& arg) const { return invoke (arg, 0); } private: template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg, int) const { return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {}); } template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } }; template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } }; template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); } template<typename T> auto invoke (const T& arg, ...) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } }; template<typename F> auto curry (F f) { return CurryImpl<F> { f, {} }; } auto test (int t1, int t2, double t3, const std::string& str) { return t1 * t2 * t3 * str.size (); } struct Foo { int m_bar; auto doFoo (int baz, int qux) { auto result = (m_bar + baz) / qux; ++m_bar; return result; } }; int main () { const auto res = curry (test) (1) (2) (3.0) ("four"); std::cout << res << std::endl; Foo someFoo { 42 }; const auto fooRes = curry (&Foo::doFoo) (&someFoo) (2) (4); std::cout << fooRes << " " << someFoo.m_bar << std::endl; someFoo.m_bar = 42; auto lambda = [someFoo] (int bar, int baz) mutable { return someFoo.doFoo (bar, baz); }; const auto lambdaRes = curry (lambda) (4) (2); std::cout << lambdaRes << std::endl; }

    .

    :

    CurryImpl , std::function ?
    std::function type erasure, . , .
    std::tuple ?
    , , , .

    C++14, C++11 ?
    , . C++14 , :
    auto , decltype ; compile-time- ( std::index_sequence_for , ).
    , , , β€” decltype .

    , ?
    .

    ?
    : - , , . , , .

    production-ready?
    , C++14 - , .
  2. std::get (m_prevArgs)... std::get (m_prevArgs), std::get (m_prevArgs), Is .

    , , C++11, C++14 STL! , . , std::index_sequence_for , ( PrevArgs... ), invoke invokeIndexed :
    return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {});
    invokeIndexed std::index_sequence , :
    template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return m_f (std::get<Is> (m_prevArgs)..., arg); }

    ! ! , , :
    struct Foo { auto doFoo (int baz, int qux) { return (m_bar + baz) / qux; } }; // ... Foo someFoo; const auto fooRes = Curry (&Foo::doFoo) (&someFoo) (2) (4);
    : m_f (arguments) well-formed, m_f β€” - .

    , , , , . , m_f , --. , :
    template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } };
    invokeIndexed :
    template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); }
    , - SFINAE , Invoke , Args... β€” , . , , , :
    template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } };
    , , , , : c , , , -- . :
    struct Foo { int m_state = 42; auto doFoo (int bar) { m_state += bar; return m_state; } }; Foo foo; curry (&Foo::doFoo) (foo) (1); // foo.m_state 42 curry (&Foo::doFoo) (&foo) (1); // foo.m_state 43

    , -, .

    #include <tuple> #include <type_traits> #include <utility> #include <iostream> #include <string> template<typename F, typename... PrevArgs> class CurryImpl { const F m_f; const std::tuple<PrevArgs...> m_prevArgs; public: CurryImpl (F f, const std::tuple<PrevArgs...>& prev) : m_f { f } , m_prevArgs { prev } { } template<typename T> auto operator() (const T& arg) const { return invoke (arg, 0); } private: template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg, int) const { return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {}); } template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } }; template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } }; template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); } template<typename T> auto invoke (const T& arg, ...) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } }; template<typename F> auto curry (F f) { return CurryImpl<F> { f, {} }; } auto test (int t1, int t2, double t3, const std::string& str) { return t1 * t2 * t3 * str.size (); } struct Foo { int m_bar; auto doFoo (int baz, int qux) { auto result = (m_bar + baz) / qux; ++m_bar; return result; } }; int main () { const auto res = curry (test) (1) (2) (3.0) ("four"); std::cout << res << std::endl; Foo someFoo { 42 }; const auto fooRes = curry (&Foo::doFoo) (&someFoo) (2) (4); std::cout << fooRes << " " << someFoo.m_bar << std::endl; someFoo.m_bar = 42; auto lambda = [someFoo] (int bar, int baz) mutable { return someFoo.doFoo (bar, baz); }; const auto lambdaRes = curry (lambda) (4) (2); std::cout << lambdaRes << std::endl; }

    .

    :

    CurryImpl , std::function ?
    std::function type erasure, . , .
    std::tuple ?
    , , , .

    C++14, C++11 ?
    , . C++14 , :
    auto , decltype ; compile-time- ( std::index_sequence_for , ).
    , , , β€” decltype .

    , ?
    .

    ?
    : - , , . , , .

    production-ready?
    , C++14 - , .
std::get (m_prevArgs)... std::get (m_prevArgs), std::get (m_prevArgs), Is .

, , C++11, C++14 STL! , . , std::index_sequence_for , ( PrevArgs... ), invoke invokeIndexed :
return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {});
invokeIndexed std::index_sequence , :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return m_f (std::get<Is> (m_prevArgs)..., arg); }

! ! , , :
struct Foo { auto doFoo (int baz, int qux) { return (m_bar + baz) / qux; } }; // ... Foo someFoo; const auto fooRes = Curry (&Foo::doFoo) (&someFoo) (2) (4);
: m_f (arguments) well-formed, m_f β€” - .

, , , , . , m_f , --. , :
template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } };
invokeIndexed :
template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); }
, - SFINAE , Invoke , Args... β€” , . , , , :
template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } };
, , , , : c , , , -- . :
struct Foo { int m_state = 42; auto doFoo (int bar) { m_state += bar; return m_state; } }; Foo foo; curry (&Foo::doFoo) (foo) (1); // foo.m_state 42 curry (&Foo::doFoo) (&foo) (1); // foo.m_state 43

, -, .

#include <tuple> #include <type_traits> #include <utility> #include <iostream> #include <string> template<typename F, typename... PrevArgs> class CurryImpl { const F m_f; const std::tuple<PrevArgs...> m_prevArgs; public: CurryImpl (F f, const std::tuple<PrevArgs...>& prev) : m_f { f } , m_prevArgs { prev } { } template<typename T> auto operator() (const T& arg) const { return invoke (arg, 0); } private: template<typename T> std::result_of_t<F (PrevArgs..., T)> invoke (const T& arg, int) const { return invokeIndexed (arg, std::index_sequence_for<PrevArgs...> {}); } template<typename IF> struct Invoke { template<typename... IArgs> auto operator() (IF fr, IArgs... args) { return fr (args...); } }; template<typename R, typename C, typename... Args> struct Invoke<R (C::*) (Args...)> { auto operator() (R (C::*ptr) (Args...), C c, Args... rest) { return (c.*ptr) (rest...); } auto operator() (R (C::*ptr) (Args...), C *c, Args... rest) { return (c->*ptr) (rest...); } }; template<typename T, std::size_t... Is> auto invokeIndexed (const T& arg, std::index_sequence<Is...>) const { return Invoke<F> {} (m_f, std::get<Is> (m_prevArgs)..., arg); } template<typename T> auto invoke (const T& arg, ...) const { return CurryImpl<F, PrevArgs..., T> { m_f, std::tuple_cat (m_prevArgs, std::tuple<T> { arg }) }; } }; template<typename F> auto curry (F f) { return CurryImpl<F> { f, {} }; } auto test (int t1, int t2, double t3, const std::string& str) { return t1 * t2 * t3 * str.size (); } struct Foo { int m_bar; auto doFoo (int baz, int qux) { auto result = (m_bar + baz) / qux; ++m_bar; return result; } }; int main () { const auto res = curry (test) (1) (2) (3.0) ("four"); std::cout << res << std::endl; Foo someFoo { 42 }; const auto fooRes = curry (&Foo::doFoo) (&someFoo) (2) (4); std::cout << fooRes << " " << someFoo.m_bar << std::endl; someFoo.m_bar = 42; auto lambda = [someFoo] (int bar, int baz) mutable { return someFoo.doFoo (bar, baz); }; const auto lambdaRes = curry (lambda) (4) (2); std::cout << lambdaRes << std::endl; }

.

:

CurryImpl , std::function ?
std::function type erasure, . , .
std::tuple ?
, , , .

C++14, C++11 ?
, . C++14 , :
auto , decltype ; compile-time- ( std::index_sequence_for , ).
, , , β€” decltype .

, ?
.

?
: - , , . , , .

production-ready?
, C++14 - , .

Source: https://habr.com/ru/post/238879/


All Articles