views:

353

answers:

1

This first piece has been solved by Eric's comments below but has led onto a secondary issue that I describe after the horizontal rule. Thanks Eric!

I'm trying to pass a functor that is a templated class to the create_thread method of boost thread_group class along with two parameters to the functor. However I can't seem to get beyond my current compile error. With the below code:

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/thread.hpp>
#include <vector>

using namespace boost::lambda;
using namespace std;

namespace bl = boost::lambda;

template<typename ftor, typename data>
class Foo
{
public:
    explicit Foo()
    {
    }
    void doFtor ()
    {
        _threads.create_thread(bind(&Foo<ftor, data>::_ftor, _list.begin(), _list.end()));
        //_threads.create_thread(bind(_ftor, _list.begin(), _list.end()));
        _threads.join_all();
    }

private:
    boost::thread_group _threads;
    ftor _ftor;
    vector<data> _list;
};

template<typename data>
class Ftor
{
public:
    //template <class Args> struct sig { typedef void type; }

    explicit Ftor () {}

    void operator() (typename vector<data>::iterator &startItr, typename vector<data>::iterator &endItr)
    {
        for_each(startItr, endItr, cout << bl::_1 << constant("."));
    }
}

I also tried typedef-ing 'type' as I thought my problem might have something to do with the Sig Template as the functor itself is templated.

The error I am getting is:

error: no matching function for call to ‘boost::lambda::function_adaptor<Ftor<int> Foo<Ftor<int>, int>::*>::apply(Ftor<int> Foo<Ftor<int>, int>::* const&, const __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int>> >&, const __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >&)’

with a bunch of preamble beforehand.

Thanks in advance for any help!


Okay I've modified the code taking in Eric's suggestions below resulting in the following code:

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/thread.hpp>
#include <vector>

using namespace boost::lambda;
using namespace std;

namespace bl = boost::lambda;

template<typename ftor, typename data>
class Foo
{
public:
    explicit Foo()
    {
    }
    void doFtor ()
    {
        _threads.create_thread(bl::bind(boost::ref(_ftor), _list.begin(), _list.end()));
        _threads.join_all();
    }

private:
    boost::thread_group _threads;
    ftor _ftor;
    vector<data> _list;
};

template<typename data>
class Ftor
{
public:
    typedef void result_type;

    explicit Ftor () {}

    result_type operator() (typename vector<data>::iterator &startItr, typename vector<data>::iterator &endItr)
    {
        for_each(startItr, endItr, cout << bl::_1 << constant("."));
        return ;
    }
};

However this results in another compile error:

/usr/local/include/boost/lambda/detail/function_adaptors.hpp:45: error: no match for call to ‘(Ftor<int>) (const __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >&, const __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >&)’
ftor.h:41: note: candidates are: void Ftor<data>::operator()(typename std::vector<data, std::allocator<_CharT> >::iterator&, typename std::vector<data, std::allocator<_CharT> >::iterator&) [with data = int]
/usr/local/include/boost/lambda/detail/function_adaptors.hpp:45: error: return-statement with a value, in function returning 'void'

It seems having defined void as a result_type it is now expecting the operator() to return something. I tried returning result_type from within the function but this also generated errors. Any ideas?

A: 

Sig (or in your case, simply typedef void result_type; is necessary.

IIRC, lambda::bind makes const copies of its arguments.

There is thus a problem with functors with non-const operator(). This is solved by making Ftor::operator()const or by wrapping (in doFtor()), _ftor with boost::ref

There is a similar problem with the iterators. Wrapping in boost::ref here won't work directly because it would end up using a reference to a temporary. The simpler solution is to modify Ftor::operator() to take its arguments by copy.

The simplest is thus to modify Ftor so that its operator() is const and it takes its arguments by copy:

void operator() (typename vector<data>::iterator startItr, typename vector<data>::iterator endItr)const

If you really can't make Ftor::operator() const, you could modify doFtor() as follows (but it is still necessary to make Ftor::operator() take its arguments by copy):

_threads.create_thread(bind(boost::ref(_ftor), _list.begin(), _list.end()));
Éric Malenfant
Demps
Indeed, using boost::ref on _list.begin() is not a good idea, as it ends up passing a reference to a temporary. I'll update the answer
Éric Malenfant
@Eric: I modified the code as you suggested but this is also generating a compile error. This time with the return type which I believe is down to the typedef. I've modified the question above to reflect the changes.
Demps