0

I have a class hierarchy like this one (this is the actual class but I cleaned it up):

class Notifiable 
{
public:
   void notify();
}

template <class Exp>
class Batch : public Notifiable
{
public:
    void run();
}

void Batch<Exp>::run()
{
   done.clear();
   generator->resetGeneration();

   while(generator->hasMoreParameters())
   {
       // Lock for accessing active
       std::unique_lock<std::mutex> lock(q_mutex, std::adopt_lock);

       // If we've less experiments than threads
       if (active.size() < threads)
       {
          Configuration conf = generator->generateParameters();
        Exp e(executable, conf);
           //std::weak_ptr<Batch<Exp>> bp;
           //bp.reset(this);

           std::thread t(&Exp::run, e, *this);
           std::thread::id id = t.get_id();
           active.insert(id);
           t.detach();
       }
       q_control.wait(lock, [this] { return active.size() < threads; } );
   }
}


class Experiment
{
public:
   void run(Notifiable& caller)
   {
      do_stuff();
      caller.notify();
   }

   virtual void do_stuff() = 0;
}

class MyExperiment : public Experiment 
{
public:
   void do_stuff() 
   {
       // do my stuff
   }
}

I then instantiate a Batch<MyExperiment> object and call run(), using this code:

Batch<ELExperiment> b(pex, options["name"].as<string>(), options["executable"].as<string>());
    b.run();

but I get this at compile-time:

In file included from /opt/local/include/gcc47/c++/bits/move.h:57:0,
                 from /opt/local/include/gcc47/c++/bits/stl_pair.h:61,
                 from /opt/local/include/gcc47/c++/bits/stl_algobase.h:65,
                 from /opt/local/include/gcc47/c++/bits/char_traits.h:41,
                 from /opt/local/include/gcc47/c++/ios:41,
                 from /opt/local/include/gcc47/c++/ostream:40,
                 from /opt/local/include/gcc47/c++/iostream:40,
                 from json2cli/main.cpp:9:
/opt/local/include/gcc47/c++/type_traits: In instantiation of 'struct std::_Result_of_impl<false, false, std::_Mem_fn<void (Experiment::*)(Notifiable&)>, MyExperiment, Batch<MyExperiment> >':
/opt/local/include/gcc47/c++/type_traits:1857:12:   required from 'class std::result_of<std::_Mem_fn<void (Experiment::*)(Notifiable&)>(MyExperiment, Batch<MyExperiment>)>'
/opt/local/include/gcc47/c++/functional:1563:61:   required from 'struct std::_Bind_simple<std::_Mem_fn<void (Experiment::*)(Notifiable&)>(MyExperiment, Batch<MyExperiment>)>'
/opt/local/include/gcc47/c++/thread:133:9:   required from 'std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (Experiment::*)(Notifiable&); _Args = {MyExperiment&, Batch<MyExperiment>&}]'
json2cli/batch.hh:86:46:   required from 'void Batch<Exp>::run() [with Exp = MyExperiment]'
json2cli/main.cpp:113:15:   required from here
/opt/local/include/gcc47/c++/type_traits:1834:9: error: no match for call to '(std::_Mem_fn<void (Experiment::*)(Notifiable&)>) (MyExperiment, Batch<MyExperiment>)'
In file included from /opt/local/include/gcc47/c++/memory:81:0,
                 from json2cli/parameterexpression.hh:19,
                 from json2cli/main.cpp:13:
/opt/local/include/gcc47/c++/functional:525:11: note: candidates are:
/opt/local/include/gcc47/c++/functional:548:7: note: _Res std::_Mem_fn<_Res (_Class::*)(_ArgTypes ...)>::operator()(_Class&, _ArgTypes ...) const [with _Res = void; _Class = Experiment; _ArgTypes = {Notifiable&}]
/opt/local/include/gcc47/c++/functional:548:7: note:   no known conversion for argument 1 from 'MyExperiment' to 'Experiment&'
/opt/local/include/gcc47/c++/functional:553:7: note: _Res std::_Mem_fn<_Res (_Class::*)(_ArgTypes ...)>::operator()(_Class*, _ArgTypes ...) const [with _Res = void; _Class = Experiment; _ArgTypes = {Notifiable&}]
/opt/local/include/gcc47/c++/functional:553:7: note:   no known conversion for argument 1 from 'MyExperiment' to 'Experiment*'
/opt/local/include/gcc47/c++/functional:559:2: note: template<class _Tp> _Res std::_Mem_fn<_Res (_Class::*)(_ArgTypes ...)>::operator()(_Tp&, _ArgTypes ...) const [with _Tp = _Tp; _Res = void; _Class = Experiment; _ArgTypes = {Notifiable&}]
/opt/local/include/gcc47/c++/functional:559:2: note:   template argument deduction/substitution failed:
In file included from /opt/local/include/gcc47/c++/bits/move.h:57:0,
                 from /opt/local/include/gcc47/c++/bits/stl_pair.h:61,
                 from /opt/local/include/gcc47/c++/bits/stl_algobase.h:65,
                 from /opt/local/include/gcc47/c++/bits/char_traits.h:41,
                 from /opt/local/include/gcc47/c++/ios:41,
                 from /opt/local/include/gcc47/c++/ostream:40,
                 from /opt/local/include/gcc47/c++/iostream:40,
                 from json2cli/main.cpp:9:
/opt/local/include/gcc47/c++/type_traits:1834:9: note:   cannot convert 'std::declval<Batch<MyExperiment> >()' (type 'Batch<MyExperiment>') to type 'Notifiable&'

It looks like I can't just expect to generalize any Batch<Exp> into a Notifiable for function calling. Can you confirm that?

Update sorry, I thought I could avoid dumping all of my code inside the question, but in fact there must be something wrong in the way I spawn the thread for Batch<Exp>::run(). There are still a couple of details missing, but I don't really think they are related (e.g. how I generate the parameters for the experiment).

Thanks

tunnuz
  • 23,338
  • 31
  • 90
  • 128

2 Answers2

2

Your error is not in the code that you show to us, Some where in the code you try to bind your run and create a thread using std::thread and that's the problem, since it can't create a correct struct for your bound function and simplest workaround is to write your own wrapper:

template< class Expr >
struct my_bind {
    my_bind( Expr& e, Notifiable& n ) : e_( e ), n_(n) {}
    void operator()() {e_.run(n_);}
    Expr& e_;
    Notifiable& n_;
};

And then use your own wrapper to start the function, I can't say for sure but I think this is a bug in compiler (all compilers have this bug: GCC, MSVC, ...) that when you expression get complicated they fail to use it in std::bind!!

BigBoss
  • 6,904
  • 2
  • 23
  • 38
  • Please see updated code in my question, however I think you might be right, trying that now. – tunnuz Oct 16 '12 at 09:35
1

Change

 std::thread t(&Exp::run, e, *this);

to

std::thread t([](Exp&& e, Batch& b) { e.run(b); }, std::move(e), std::ref(*this));

Or if you really intended for the thread to inherit a copy from *this:

std::thread t([](Exp&& e, Batch&& b) { e.run(b); }, std::move(e), *this);

The root of the error (at least the one referenced by the messages) is the semantics of that particular std::thread constructor (which I'm not going to expose here, it's sort of gory). If you're already familiar with the semantics std::bind (which do have their own quirks), you can defer to it:

std::thread t(std::bind(&Exp::run, std::move(e), std::ref(*this));

(Once again std::ref(*this) can be substituted by *this depending on what you want.)

Luc Danton
  • 34,649
  • 6
  • 70
  • 114