Created
November 6, 2012 13:14
-
-
Save splinterofchaos/4024658 to your computer and use it in GitHub Desktop.
Rethinking std::binary_function
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <memory> | |
#include <iostream> | |
#include <sstream> | |
#include <utility> | |
#include <algorithm> | |
#include <iterator> | |
template<class...> struct Part; | |
template< class F, class X > | |
struct Part< F, X > | |
{ | |
F f; | |
X x; | |
template< class _F, class _X > | |
constexpr Part( _F&& f, _X&& x ) | |
: f(std::forward<_F>(f)), x(std::forward<_X>(x)) | |
{ | |
} | |
/* | |
* The return type of F only gets deduced based on the number of xuments | |
* supplied. Part otherwise has no idea whether f takes 1 or 10 x's. | |
*/ | |
template< class ... Xs > | |
constexpr auto operator() ( Xs&& ...xs ) | |
-> decltype( f(x,std::declval<Xs>()...) ) | |
{ | |
return f( x, std::forward<Xs>(xs)... ); | |
} | |
}; | |
/* Recursive, variadic version. */ | |
template< class F, class X1, class ...Xs > | |
struct Part< F, X1, Xs... > | |
: public Part< Part<F,X1>, Xs... > | |
{ | |
template< class _F, class _X1, class ..._Xs > | |
constexpr Part( _F&& f, _X1&& x1, _Xs&& ...xs ) | |
: Part< Part<F,X1>, Xs... > ( | |
Part<F,X1>( std::forward<_F>(f), std::forward<_X1>(x1) ), | |
std::forward<_Xs>(xs)... | |
) | |
{ | |
} | |
}; | |
template< class F, class ...X > | |
constexpr Part<F,X...> closure( F&& f, X&& ...x ) { | |
return Part<F,X...>( std::forward<F>(f), std::forward<X>(x)... ); | |
} | |
template< class F, class ...X > | |
constexpr Part<F,X...> closet( F f, X ...x ) { | |
return Part<F,X...>( std::move(f), std::move(x)... ); | |
} | |
template< class D > struct Unary { | |
}; | |
template< class Derr > struct Binary { | |
// One argument: curry. | |
template< class X > | |
constexpr auto operator () ( X x ) -> Part<Derr,X> { | |
return closet( Derr(), std::move(x) ); | |
} | |
template< class X, class Y > | |
using Result = typename std::result_of< Derr(X,Y) >::type; | |
// Three arguments: unroll. | |
template< class X, class Y, class Z > | |
constexpr auto operator () ( X&& x, Y&& y, Z&& z ) | |
-> Result<Result<X,Y>,Z> | |
{ | |
return Derr()( | |
Derr()( std::forward<X>(x), std::forward<Y>(y) ), | |
std::forward<Z>(z) | |
); | |
} | |
template< class X, class Y, class ...Z > | |
using Unroll = typename std::result_of < | |
Binary<Derr>( Result<X,Y>, Z... ) | |
>::type; | |
// Any more? recurse. | |
template< class X, class Y, class Z, class H, class ...J > | |
constexpr auto operator () ( X&& x, Y&& y, Z&& z, H&& h, J&& ...j ) | |
-> Unroll<X,Y,Z,H,J...> | |
{ | |
// Notice how (*this) always gets applied at LEAST three arguments. | |
return (*this)( | |
Derr()( std::forward<X>(x), std::forward<Y>(y) ), | |
std::forward<Z>(z), std::forward<H>(h), std::forward<J>(j)... | |
); | |
} | |
}; | |
constexpr struct Add : public Binary<Add> { | |
using Binary::operator(); | |
template< class X, class Y > | |
constexpr auto operator () ( X&& x, Y&& y ) | |
-> decltype( std::declval<X>() + std::declval<Y>() ) | |
{ | |
return std::forward<X>(x) + std::forward<Y>(y); | |
} | |
} add{}; | |
int main() { | |
constexpr int x = add(1,2,3,4,5); | |
std::cout << x << std::endl; | |
std::cout << add(std::string("hello"),' ',"there!") << std::endl; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment