Skip to content

Instantly share code, notes, and snippets.

@splinterofchaos
Created November 11, 2012 14:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save splinterofchaos/4055136 to your computer and use it in GitHub Desktop.
Save splinterofchaos/4055136 to your computer and use it in GitHub Desktop.
Transitivity and Associativity.
#include <utility>
using std::forward;
using std::declval;
using std::move;
template< class F, class X >
struct Part {
F f;
X x;
template< class _F, class _X >
constexpr Part( _F&& f, _X&& x )
: f(forward<_F>(f)), x(forward<_X>(x))
{
}
template< class ... Xs >
constexpr auto operator () ( Xs&& ...xs )
-> decltype( f(x,declval<Xs>()...) )
{
return f( x, forward<Xs>(xs)... );
}
};
template< class F, class X > struct RPart {
F f;
X x;
template< class _F, class _X >
constexpr RPart( _F&& f, _X&& x )
: f( forward<_F>(f) ), x( forward<_X>(x) ) { }
template< class ...Y >
constexpr decltype( f(declval<Y>()..., x) )
operator() ( Y&& ...y ) {
return f( forward<Y>(y)..., x );
}
};
template< class F > struct Binary {
template< class X >
constexpr Part<F,X> operator () ( X x ) {
return Part<F,X>( F(), move(x) );
}
template< class X >
constexpr RPart<F,X> with( X x ) {
return RPart<F,X>( F(), move(x) );
}
};
template< class F > struct Chainable : Binary<F> {
using Binary<F>::operator();
template< class X, class Y >
using R = typename std::result_of< F(X,Y) >::type;
// Three arguments: unroll.
template< class X, class Y, class Z >
constexpr auto operator () ( X&& x, Y&& y, Z&& z )
-> R< R<X,Y>, Z >
{
return F()(
F()( 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 <
Chainable<F>( typename std::result_of<F(X,Y)>::type, 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)(
F()( std::forward<X>(x), std::forward<Y>(y) ),
std::forward<Z>(z), std::forward<H>(h), std::forward<J>(j)...
);
}
};
template< class F, class Fold > struct Transitive : Binary<F> {
using Binary<F>::operator();
template< class X, class Y, class Z >
constexpr auto operator () ( X&& x, Y&& y, Z&& z )
-> typename std::result_of<F(X,Y)>::type
{
return Fold() (
F()( forward<X>(x), forward<Y>(y) ),
F()( forward<Y>(y), forward<Z>(z) )
);
}
template< class X, class Y, class Z, class A, class ...B >
constexpr auto operator () ( X&& x, Y&& y, Z&& z, A&& a, B&& ...b )
-> typename std::result_of<F(X,Y)>::type
{
return Fold() ( F()( forward<X>(x), forward<Y>(y) ),
F()( forward<Y>(y), forward<Z>(z),
forward<A>(a), forward<B>(b)... ) );
}
};
struct And : Chainable<And> {
using Chainable<And>::operator();
template< class X, class Y >
constexpr auto operator () ( X&& x, Y&& y )
-> decltype( declval<X>() && declval<Y>() )
{
return forward<X>(x) && forward<Y>(y);
}
};
constexpr struct Less : Transitive<Less,And> {
using Transitive<Less,And>::operator();
template< class X, class Y >
constexpr bool operator() ( X&& x, Y&& y ) {
return forward<X>(x) < forward<Y>(y);
}
} less{};
template< template<class...> class T > struct ConstructT {
template< class ...X, class R = T< typename std::decay<X>::type... > >
constexpr R operator () ( X&& ...x ) {
return R( forward<X>(x)... );
}
};
template< template<class...> class T >
struct ConstructChainable : Chainable<ConstructT<T>> {
using Self = ConstructChainable<T>;
using Chainable<ConstructT<T>>::operator();
template< class X >
using D = typename std::decay<X>::type;
template< class X, class Y, class R = T< D<X>, D<Y> > >
constexpr R operator () ( X&& x, Y&& y ) {
return R( forward<X>(x), forward<Y>(y) );
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment