Robin Hsu
Robin Hsu

Reputation: 4484

Composite function with std::bind2nd in C++03

I would like to get some function/functor acts as a composite function/functor:

Specifically, I have functions f(x, y), and g(u, v), and I want to create a function pointer or functor h(t) = f(g(t,C1), C2), so that I can pass functor h into a for_each() loop, where C1, C2 are some constants.

Or, let's do it in my real example: I have a function double g(T1 u, T2 v) which calculate something. I want to loop through all u from a vector vector<T1> vecInput, with fixed 2nd argument t2VConst. After calculating all these numbers, I want to save them into a vector vector<double> results.

I would like to do it with something similar to this:

foreach(vecInput.begin(), vecInput.end(), std::bind2nd(&results::push_back, std::bind2nd(g, t2VConst)))

Note that in this case, the f(x,y) function is really a member function vector::push_back. (It seems that push_back only have one argument. However, as a member function, it has an extra argument, which is the object itself.)

To make the case worse, I really have to do it in C++03, not C++11. Is there any solution? Moreover, I wish the solution can be cleaner (if it can be done in one line like above).

UPDATE

Related issue for bind2nd(): I got compile error, and now I reduce it to a simpler code to look at it:

#include <functional>
#include <algorithm>

#define CASE2   // choose define of CASE1 to CASE3

#ifdef CASE1
double g(double x, double y)   // 1. OK
#endif
#ifdef CASE2
double g(double &x, double y)  // 2. overload error
#endif
#ifdef CASE3
double g(double *x, double y)  // 3. OK
#endif
{
#ifdef CASE2
    x  = 5.0;
#endif
#ifdef CASE3
    *x = 5.0;
#endif
    // Note: no case1, since in case 1, x is a local variable, 
    // no meaning to assign value to it.

    return 3.0;
}

int main(int argc, char *argv[])
{
    double t = 2.0;
    double u;

#if defined(CASE1) || defined(CASE2)
    u = std::bind2nd(std::ptr_fun(&g), 3.0)(t);
#endif
#ifdef CASE3
    u = std::bind2nd(std::ptr_fun(&g), 3.0)(&t);
#endif
}

I don't know why CASE 2 fails...

Upvotes: 1

Views: 557

Answers (2)

Piotr Skotnicki
Piotr Skotnicki

Reputation: 48447

I have functions f(x, y), and g(u, v), and I want to create a function pointer or functor h(t) = f(g(t,C1), C2). I have to do it in C++03.

It's not possible to compose functions using only the adapters available in the standard library of C++03. Obviously, nothing stops you from implementing this functionality on your own. However, if you want to utilize any of the existing libraries, and get a true one-liner, there are few alternatives:

Option #1: SGI extensions

libstdc++ is shipped with the standard library extensions from SGI. It provides several additional adapters and functors, namely: compose1, compose2 and constant1. They can be combined in the following way:

#include <functional>
#include <ext/functional>

int f(int x, int y);
int g(int u, int v);
const int arg = 1;
const int C1 = 2;
const int C2 = 3;

__gnu_cxx::compose2(std::ptr_fun(&f)
                  , std::bind2nd(std::ptr_fun(&g), C1)
                  , __gnu_cxx::constant1<int>(C2)
                 )(arg);

Option #2: TR1 extensions

The initial implementation of C++11's std::bind can be also found in tr1 extension libraries:

#include <tr1/functional>

int f(int x, int y);
int g(int u, int v);
const int arg = 1;
const int C1 = 2;
const int C2 = 3;

std::tr1::bind(&f
             , std::tr1::bind(&g
                            , std::tr1::placeholders::_1
                            , C1)
             , C2
            )(arg);

Option #3: Boost library

Boost.Bind library is probably the most portable solution:

#include <boost/bind.hpp>

int f(int x, int y);
int g(int u, int v);
const int arg = 1;
const int C1 = 2;
const int C2 = 3;

boost::bind(&f, boost::bind(&g, _1, C1), C2)(arg);

The f(x,y) function is really a member function vector::push_back

When it comes to binding a member function in C++03 you would normally use std::mem_fun together with std::bind1st to bind the object parameter:

Class object;
std::bind1st(std::mem_fun(&Class::func), &object)(arg);

However, this will fail for member functions taking its parameter by reference. Once again, you could use Boost/TR1 instead:

boost::bind(&std::vector<double>::push_back
          , &results
          , boost::bind(&g, _1, C1)
          )(arg);

Note that when switching to C++11 you'd need to manually resolve overloaded std::vector<double>::push_back.

But fortunately, your use-case qualifies to be replaced by std::transform algorithm with std::back_inserter, that will take care of calling push_back member function of your vector for transformed arguments.

#include <algorithm>
#include <functional>
#include <iterator>

int g(int u, int v);
const int C1 = 2;
std::vector<int> input, output;

std::transform(input.begin(), input.end()
             , std::back_inserter(output)
             , std::bind2nd(std::ptr_fun(&g), C1));

DEMO

Upvotes: 1

MSalters
MSalters

Reputation: 179799

It's important to remember that these library functions aren't magic, they just deduce rather involved types.

In your case, you don't need all this flexibility, if only because you know you'll need vector::push_back. Instead of deducing a rather involved type, just create a type explicitly to capture the function. There's even a library type which does this, std::back_insert_iterator, but in general you can do it yourself.

So, you'll end up with a template class that takes typenames T1 and T2, stores &g, T2 C2, and std::vector<double>&. It simply implements void operator()(T1 t1) { v.pus_back(g(t1, C2)); }.

Upvotes: 0

Related Questions