A general and extendable solution using boost::proto :
#include <iostream>
#include <boost/proto/proto.hpp>
namespace bp = boost::proto;
// -----------------------------------------------------------------------------
// perform is a callable transform that take a function_ terminal and execute it
// -----------------------------------------------------------------------------
struct perform : bp::callable
{
template<class Sig> struct result;
template<class This, class Func, class In>
struct result<This(Func,In)>
: boost::result_of<typename boost::remove_reference<Func>::type(In)> {};
template<class Func, class In>
typename result<perform(Func &,In)>::type
operator()( Func& f, In& in ) const
{
return f(in);
}
};
// -----------------------------------------------------------------------------
// Grammar for chaining pipe of functions
// -----------------------------------------------------------------------------
struct pipeline_grammar
: bp::or_<
bp::when<
bp::bitwise_or<pipeline_grammar,pipeline_grammar>
, pipeline_grammar(
bp::_right
, pipeline_grammar(bp::_left,bp::_state)
)
>
, bp::when<
bp::terminal<bp::_>
, perform(bp::_value, bp::_state)
>
> {};
// -----------------------------------------------------------------------------
// Forward declaration of the pipeline domain
// -----------------------------------------------------------------------------
struct pipeline_domain;
// -----------------------------------------------------------------------------
// A pipeline is the top level DS entity
// -----------------------------------------------------------------------------
template<class Expr>
struct pipeline : bp::extends<Expr,pipeline<Expr>, pipeline_domain>
{
typedef bp::extends<Expr, pipeline<Expr>, pipeline_domain> base_type;
pipeline(Expr const &expr = Expr()) : base_type(expr) {}
// ---------------------------------------------------------------------------
// A pipeline is an unary callable object
// ---------------------------------------------------------------------------
template<class Input>
typename boost::result_of<pipeline_grammar(pipeline,Input)>::type
operator()(Input const& in) const
{
pipeline_grammar evaluator;
return evaluator(*this,in);
}
};
// -----------------------------------------------------------------------------
// the pipeline_domain make pipeline expression macthes pipeline_grammar
// -----------------------------------------------------------------------------
struct pipeline_domain
: bp::domain<bp::generator<pipeline>,pipeline_grammar>
{};
// -----------------------------------------------------------------------------
// Takes a PFO instance and make it a pipeline terminal
// -----------------------------------------------------------------------------
template<class Func>
typename bp::result_of::
make_expr<bp::tag::terminal, pipeline_domain,Func>::type
task( Func const& f )
{
return bp::make_expr<bp::tag::terminal,pipeline_domain>( f );
}
//--------------------------- Examples --------------------
struct return_value
{
template<class Sig> struct result;
template<class This, class T>
struct result<This(T)> : bp::detail::uncvref<T>
{};
return_value(int i = 1) : factor(i) {}
template<class T>
T operator()(T const& in) const
{
return in*factor;
}
int factor;
};
struct say_hi
{
typedef void result_type;
template<class T>
void operator()(T const& in) const
{
std::cout << "Hi from value = " << in << "\n";
}
};
int main()
{
return_value r1,r2(5);
(task(r1) | task(r2) | task(say_hi())) (7); // SHould print 35
float k = 10,r;
r = (task(r2) | task(r2) | task(r2) | task(r2))(k);
std::cout << r << "\n"; // Should print 6250
}
The basic idea is to wrap function objects as proto terminals, build a small | based grammar and let the proto system deals with the composition.