Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function Composition in C++

There are a lot of impressive Boost libraries such as Boost.Lambda or Boost.Phoenix which go a long way towards making C++ into a truly functional language. But is there a straightforward way to create a composite function from any 2 or more arbitrary functions or functors?

If I have: int f(int x) and int g(int x), I want to do something like f . g which would statically generate a new function object equivalent to f(g(x)).

This seems to be possible through various techniques, such as those discussed here. Certainly, you can chain calls to boost::lambda::bind to create a composite functor. But is there anything in Boost which easily allows you to take any 2 or more functions or function objects and combine them to create a single composite functor, similar to how you would do it in a language like Haskell?

like image 940
Channel72 Avatar asked May 15 '10 20:05

Channel72


People also ask

What is a composition in a function?

In Maths, the composition of a function is an operation where two functions say f and g generate a new function say h in such a way that h(x) = g(f(x)). It means here function g is applied to the function of x. So, basically, a function is applied to the result of another function.

What is composition function with example?

A composite function is generally a function that is written inside another function. Composition of a function is done by substituting one function into another function. For example, f [g (x)] is the composite function of f (x) and g (x). The composite function f [g (x)] is read as “f of g of x”.

What is function composition in programming?

Function composition is a way of combining pure functions to build more complicated ones. Like the usual composition of functions in mathematics, the result of one function is passed as the argument of the next, and the result of last one is the result of the whole. Composition is a fancy term which means "combining".

What is function composition operator?

The Composition Operator. Composing functions is a common and useful way to create new functions in Haskell. Haskell composition is based on the idea of function composition in mathematics. In mathematics, if you have two functions f(x) and g(x), you compute their composition as f(g(x)).


2 Answers

To anyone stumbling onto this page, there's a great blog post on this subject from bureau14:

http://blog.quasardb.net/function-composition-in-c11/

This takes advantage of the new features in C++ 11 as well as using boost.

like image 198
SpikedPunchVictim Avatar answered Oct 12 '22 20:10

SpikedPunchVictim


Stumbling upon this question, I'd like to point out to anyone who comes across this today that this is possible with a relatively elegant syntax using just the standard library and a few helper classes thanks to decltype, auto, and perfect forwarding.

Defining these two classes:

template <class Arg, class ArgCall, class OuterCall>
class pipe {
private:
    ArgCall argcall;
    OuterCall outercall;
public:
    typedef pipe<Arg, ArgCall, OuterCall>  this_type;
    pipe(ArgCall ac, OuterCall oc) : argcall(ac), outercall(oc) {}
    auto operator()(Arg arg) -> decltype(outercall(argcall(arg))) {
        return outercall(argcall(arg));
    }
    template <class NewCall>
    pipe<Arg, this_type, NewCall> operator[](NewCall&& nc) {
        return {*this, std::forward<NewCall>(nc)};
    }
};

template <class Arg>
class pipe_source {
public:
    typedef pipe_source<Arg> this_type;
    Arg operator()(Arg arg) {
        return arg;
    }
    template <class ArgCall, class OuterCall>
    static pipe<Arg, ArgCall, OuterCall> create(ArgCall&& ac, OuterCall&& oc) {
        return {std::forward<ArgCall>(ac), std::forward<OuterCall>(oc)};
    }
    template <class OuterCall>
    pipe<Arg, this_type, OuterCall> operator[](OuterCall&& oc) {
        return {*this, std::forward<OuterCall>(oc)};
    }
};

A simple program:

int f(int x) {
        return x*x;
}

int g(int x) {
        return x-2;
}

int h(int x) {
        return x/2;
}

int main() {
        auto foo = pipe_source<int>::create(f, g);
        //or:
        auto bar = pipe_source<int>()[g][h];
        std::cout << foo(10) << std::endl;
        std::cout << bar(10) << std::endl;
        return 0;
}

This has the added benefit that once it's in a pipe, as long as the return type is correct you can add another function f to the chain with pipe[f].

Then:

$ g++ test.cpp -o test -std=c++11
$ ./test
98
4
$
like image 36
Robert Mason Avatar answered Oct 12 '22 20:10

Robert Mason