I stumbled upon this nice blog post – pipable functions in C++14. This is realy fun idea as its usage plain for anybody who is familiar with unix pipelines. So i tried to use it in C++11 (without boost) from slightly different angle to make it more real-life concept.

First sample (fun, but not so interesting):

auto add = piped([](int x, int y){ return x + y; }); auto mul = piped([](int x, int y){ return x * y; }); int y = 5 | add(2) | mul(5) | add(1); // Output: 36 1 2 3 auto add = piped ( [ ] ( int x , int y ) { return x + y ; } ) ; auto mul = piped ( [ ] ( int x , int y ) { return x * y ; } ) ; int y = 5 | add ( 2 ) | mul ( 5 ) | add ( 1 ) ; // Output: 36

Second sample (functional style array processing):

vector<int> numbers{4,8,15,16,23,42}; auto result = numbers | where([](int x){ return (x > 10); }) | map([](int x){ return x + 5; }) | log(); // List: 20 21 28 47 1 2 3 vector < int > numbers { 4 , 8 , 15 , 16 , 23 , 42 } ; auto result = numbers | where ( [ ] ( int x ) { return ( x > 10 ) ; } ) | map ( [ ] ( int x ) { return x + 5 ; } ) | log ( ) ; // List: 20 21 28 47

Simple and short implementation is under the cut…

To implement the first sample you need only the following short number of lines (C++11 without boost):

template <class F> struct pipeable { private: F f; public: pipeable(F&& f) : f(std::forward<F>(f)) {} template<class... Xs> auto operator()(Xs&&... xs) -> decltype(std::bind(f, std::placeholders::_1, std::forward<Xs>(xs)...)) const { return std::bind(f, std::placeholders::_1, std::forward<Xs>(xs)...); } }; template <class F> pipeable<F> piped(F&& f) { return pipeable<F>{std::forward<F>(f)}; } template<class T, class F> auto operator|(T&& x, const F& f) -> decltype(f(std::forward<T>(x))) { return f(std::forward<T>(x)); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 template < class F > struct pipeable { private : F f ; public : pipeable ( F && f) : f(std::forward<F>(f)) {} template<class... Xs> auto operator()(Xs&&... xs) -> decltype(std::bind(f, std::placeholders::_1, std::forward<Xs>(xs)...)) const { return std::bind(f, std::placeholders::_1, std::forward<Xs>(xs)...); } } ; template < class F > pipeable < F > piped ( F && f) { return pipeable<F>{std::forward<F>(f)}; } template < class T , class F > auto operator | ( T && x, const F& f) -> decltype(f(std::forward<T>(x))) { return f(std::forward<T>(x)); }

You can see a lot of C++11 features here coupled together – perfect forwarding, &&, std::bind, auto and decltype. Using this you can even define functions inside pipeline:

int y2 = 5 | add(2) | piped([](int x, int y){ return x * y; })(5) | piped([](int x){ return x + 1; })(); // Output: 36 1 int y2 = 5 | add ( 2 ) | piped ( [ ] ( int x , int y ) { return x * y ; } ) ( 5 ) | piped ( [ ] ( int x ) { return x + 1 ; } ) ( ) ; // Output: 36

For second sample we need to define some extension functions. We don’t need linq library to make things work though. Let’s do it generic way:

template <typename T> T whereInList(const T& list, std::function<bool(decltype(list.at(0)))> f) { T result; for (auto& item : list) if (f(item)) result.push_back(item); return result; } template <typename T> T mapToList(const T& list, std::function<int(decltype(list.at(0)))> f) { T result; for (auto& item : list) result.push_back(f(item)); return result; } template <typename T> T logList(const T& list) { std::cout << "List: "; for (auto& item : list) std::cout << item << " "; std::cout << std::endl; return list; } template <typename F> auto piped1Arg(F f) -> decltype(piped(std::bind(f, placeholders::_1))) { return piped(std::bind(f, placeholders::_1)); } template <typename F> auto piped2Args(F f) -> decltype(piped(std::bind(f, placeholders::_1, placeholders::_2))) { return piped(std::bind(f, placeholders::_1, placeholders::_2)); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 template < typename T > T whereInList ( const T & list, std::function<bool(decltype(list.at(0)))> f) { T result; for ( auto & item : list) if (f(item)) result.push_back(item); return result ; } template < typename T > T mapToList ( const T & list, std::function<int(decltype(list.at(0)))> f) { T result; for ( auto & item : list) result.push_back(f(item)); return result ; } template < typename T > T logList ( const T & list) { std::cout << "List: "; for ( auto & item : list) std::cout << item << " "; std : : cout < < std : : endl ; return list ; } template < typename F > auto piped1Arg ( F f ) - > decltype ( piped ( std : : bind ( f , placeholders : : _1 ) ) ) { return piped ( std : : bind ( f , placeholders : : _1 ) ) ; } template < typename F > auto piped2Args ( F f ) - > decltype ( piped ( std : : bind ( f , placeholders : : _1 , placeholders : : _2 ) ) ) { return piped ( std : : bind ( f , placeholders : : _1 , placeholders : : _2 ) ) ; }

Processing functions here are just example and not so suitable for large amount of data – but whole concept is still viable.

Last two functions are used to create pipable functors from static methods:

vector<int> numbers{4,8,15,16,23,42}; using setType = decltype(numbers); auto where = piped2Args(whereInList<setType>); auto map = piped2Args(mapToList<setType>); auto log = piped1Arg(logList<setType>); auto result = numbers | where([](int x){ return (x > 10); }) | map([](int x){ return x + 5; }) | log(); 1 2 3 4 5 6 7 vector < int > numbers { 4 , 8 , 15 , 16 , 23 , 42 } ; using setType = decltype ( numbers ) ; auto where = piped2Args ( whereInList < setType > ) ; auto map = piped2Args ( mapToList < setType > ) ; auto log = piped1Arg ( logList < setType > ) ; auto result = numbers | where ( [ ] ( int x ) { return ( x > 10 ) ; } ) | map ( [ ] ( int x ) { return x + 5 ; } ) | log ( ) ;

As you can see the concept requires very compact implementation. If you somehow applied this in real project plz write in comments.