Friday, February 02, 2007

Binders

I came across a bug with the binders. Had heard from people that the standard ones are not very effective and buggy and boost binders should be used. But I realized it recently why that's said.

Here is a piece of code that should work but does not, why? Because STL bind2nd works with const member functions and NOT the non-const member functions. The code first:

[CODE]
        size_t N = 1000;
        vector<vector<int> > v(N);
        for_each(v.begin(), v.end(), bind2nd(mem_fun_ref(&vector<int>::reserve)),N));


Another sample to test:

[CODE]
#include<vector>
#include<algorithm>
#include<functional>

using namespace std;

struct Test
{
             void func1(int){}
             void func2(int)const{}
};

int main()
{
             size_t N=100;
             vector<Test> TestVectorObject(N);
             for_each(TestVectorObject.begin(), TestVectorObject.end(), bind2nd(mem_fun_ref(&Test::func1),N)); //ERROR
             for_each(TestVectorObject.begin(), TestVectorObject.end(), bind2nd(mem_fun_ref(&Test::func2),N)); //OK
}


Try compiling with online Comeau here - Online Comeau Compiler and see for yourself. The above code will compile fine but just comment the const version and uncomment the non-const version and see. The code will fail to compile.

So, what's the solution? Two possible solutions come to my mind. The first would need you to write a functor whose operator() takes each object of the vector and calls the required member function. The other is to use tr1::bind.

Here are the relevant codes for the two solutions that I talked about.

Solution (using explicit functor)
[CODE]
#include<vector>
#include<algorithm>
#include<functional>

using namespace std;

template<typename T>
struct CallNonConstFunc1
{
               void operator() (T& obj) const
               {
                               obj.func1(int());
               }
};


struct Test
{
               void func1(int){}
               void func2(int)const{}
};

int main()
{
               size_t N=100;
               vector<Test> TestVectorObject(N);
               //for_each(TestVectorObject.begin(), TestVectorObject.end(), bind2nd(mem_fun_ref(&Test::func1),N));
               for_each(TestVectorObject.begin(), TestVectorObject.end(), CallNonConstFunc1<Test>());
               for_each(TestVectorObject.begin(), TestVectorObject.end(), bind2nd(mem_fun_ref(&Test::func2),N));
}

Not very elegant, isn't it? So, lets see how tr1::bind does it...

Solution (using tr1::bind)
[CODE]
#include<vector>
#include<algorithm>
#include<functional>

using namespace std;
using namespace std::tr1;
using namespace std::tr1::placeholders;

template<typename T>
struct CallNonConstFunc1
{
               void operator() (T& obj) const
               {
                               obj.func1(int());
               }
};

struct Test
{
               void func1(int){}
               void func2(int)const{}
};

int main()
{
               size_t N=100;
               vector<Test> TestVectorObject(N);
               for_each(TestVectorObject.begin(), TestVectorObject.end(), bind(mem_fun_ref(&Test::func1),_1,N));
               for_each(TestVectorObject.begin(), TestVectorObject.end(), CallNonConstFunc1<Test>());
               for_each(TestVectorObject.begin(), TestVectorObject.end(), bind2nd(mem_fun_ref(&Test::func2),N));
}

Try compiling that correction with Dinkumware online compiler here - Dinkumware Exam (online compiler)

References:

-- Scott Meyers : tr1 Information - TR1 Information - Scott Meyers
-- Dinkumware : tr1 - Dinkumware TR1 Documentation
-- Pete Becker : C++ Function Objects in TR1 - C++ Function Objects in TR1 - Pete Becker

Have fun!

No comments: