Skip to content

Instantly share code, notes, and snippets.

@splinterofchaos
Created November 6, 2012 13:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save splinterofchaos/4024658 to your computer and use it in GitHub Desktop.
Save splinterofchaos/4024658 to your computer and use it in GitHub Desktop.
Rethinking std::binary_function
#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