Boost logo

Boost Users :

Subject: [Boost-users] [RFC] Signals bound to phoenix functions
From: Stephan Menzel (stephan.menzel_at_[hidden])
Date: 2010-06-21 05:14:35


G'day,

I'd like to get your opinion on a problem that puzzles me for a while.
Basically, I'm trying to do sort of a delayed signals2 notification
mechanism. I have a class wrapped around ASIOs io_service object that
I normally use as a thread multiplexer and function storage. Now in
this case I want to use it without working threads so a current thread
can process whatever handlers are in it. Which works fine but now I
want to connect signals to it. I have several data classes that
contain signals. These are to be connected to slots, but not directly.
Instead, these slots are to be wrapped in little handlers and these
handlers shall be put in the io_service object. So the signal's slot
should not be executed straight away but by another thread that
executes everything in the io_service.

Now this strikes me as something that could be done nicely with
phoenix. I want to use phoenix to create a little function that I can
connect to the signal and that will do nothing more but post the
handler into the io_service when the signal is triggered. I just can't
figure out if that can actually work. Here's what I have so far
(simplyfied). I try to illustrate on an example with a one-parameter
slot.

class Queue : public boost::noncopyable {

public:
   template<typename Handler>
   void post(Handler n_handler) {
       m_iosrv.post(n_handler);
   }

   std::size_t process(boost::system::error_code & n_errcode) {
      return m_iosrv.poll(n_errcode);
   };

   template<typename Slot>
   boost::signals2::connection connect(boost::signals2::signal<void()>
&n_signal, Slot n_method) {
     return n_signal.connect(boost::bind(&CommandQueue::slotWrapper<Slot>,
this, n_method));
  };
   template<typename Slot, typename T1>
   boost::signals2::connection
connect(boost::signals2::signal<void(T1)>& n_signal, Slot n_method) {
      return n_signal.connect(boost::bind(&CommandQueue::slotWrapper<Slot,
T1>, this, n_method, _1));
   }
// ... more to come with more parameters T2, T3.....

private:
   template<typename Slot>
   void slotWrapper(Slot n_method) {
       post(n_method);
   }

  template<typename Slot, typename T1>
  void slotWrapper(Slot n_method, T1 t1) {
      boost::function<void(T1)> f(n_method);
      post(boost::bind(f, t1));
  }
// ... more to come with more parameters ...

   boost::asio::io_service m_iosrv;
};

Now I can use this like this:

struct testslot_one {
  void operator()(int n_arg) const {
      std::cout << "testslot 1: " << n_arg << std::endl;
  }
};

main {
 Queue q;
 boost::signals2::signal<void (int)> sig1;

 struct testslot_one t1;
 q.connect(sig1, t1);

 // signal not yet executed but only the handler posted
 sig1(42);

 boost::system::error_code errc;
 // here we execute the accumulated signals
 q.process(errc);
}

Now this works OK so far but I have two problems:

First of all, the standard use case will be to bind member functions
and not seperate functors such as "testslot_one" in this example.
Which is when the connect fails. For reasons unknown to me. But that
must work.
Second, I want to get rid of the "slotWrapper" functions and replace
them by a phoenix expression. If that makes sense, which I'm not sure
of.

Also, I am not certain about the whole thing. Can a phoenix generated
function actually serve this way? Just post the handler when the
signal is triggered?

Any opinions are appreciated!

Cheers,
Stephan


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net