[RFC] Signals bound to phoenix functions

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

Hi, maybe I managed to narrow it down a little. This works for both functors and member functions: class Queue { ... template<typename SlotSignature, typename SlotFunction> boost::signals2::connection connect(boost::signals2::signal<SlotSignature> &n_signal, SlotFunction n_method) { return n_signal.connect(m_iosrv.wrap( n_method)); }; ... } io_service::wrap basically does what I needed all the time. No more wrapper. Unless I want to add something. Unfortunately I can't always take wrap()s output but have to modify a little. In fact, my Queue needs to be templatized as well, modifying the behaviour of the functor. I have a template parameter "Calling". When it is true, another function shall be called by the functor after the method was posted. Now I wanted to do this by phoenix: template <bool Calling> class Queue { template<typename Handler> void post(Handler n_handler) { m_iosrv.post(n_handler); }; ... template<typename SlotSignature, typename SlotFunction> boost::signals2::connection connect(boost::signals2::signal<SlotSignature> &n_signal, SlotFunction n_method) { return n_signal.connect( if_( CallingBack ) [ post(n_method) , m_signal_callback() ] .else_ [ m_iosrv.wrap( n_method ) ] ); }; ... private: boost::function<void ()> m_callback; boost::asio::io_service m_iosrv; } Now as I understand phoenix the if_ statement is supposed to give me a functor that is composed of either what is in the true section or the false section, depending on the expression at compile time. Which I guess is what I want. Alas, even after some days of trial and error I can't get it to compile. It seems like the if_ operator is not accepted properly. Or I am completely misunderstanding phoenix. ... no match for »operator[]« in »boost::phoenix::if_ [with Cond = bool](((const bool&)((const bool*)(& false))))[(Queue<Calling>::post [with Handler = queue_testsuite::testslot, bool Calling = false]((n_method, queue_testsuite::testslot())), ((Queue<false>*)this)->Queue<false>::m_signal_callback.boost::function<void()>::<anonymous>.boost::function0<R>::operator() [with R = void]())]« ... Any ideas anyone?? Cheers, Stephan On Mon, Jun 21, 2010 at 11:14 AM, Stephan Menzel <stephan.menzel@gmail.com> wrote:
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

Stephan, hi! please see my answer below. On Wed, Jun 23, 2010 at 5:42 PM, Stephan Menzel <stephan.menzel@gmail.com>wrote:
Unless I want to add something. Unfortunately I can't always take wrap()s output but have to modify a little. In fact, my Queue needs to be templatized as well, modifying the behaviour of the functor. I have a template parameter "Calling". When it is true, another function shall be called by the functor after the method was posted. Now I wanted to do this by phoenix:
template <bool Calling> class Queue { template<typename Handler> void post(Handler n_handler) { m_iosrv.post(n_handler); }; ... template<typename SlotSignature, typename SlotFunction> boost::signals2::connection connect(boost::signals2::signal<SlotSignature> &n_signal, SlotFunction n_method) { return n_signal.connect( if_( CallingBack ) [ post(n_method) , m_signal_callback() ] .else_ [ m_iosrv.wrap( n_method ) ] ); }; ...
It is difficult to conclude smth from the code without having a self-containing example. But my assumption is that in the 'else'-branch your code does not generate a function object. As far as I can see in the 'if'-branch your code generates a function object using the the comma-operator (if that operator is overloaded in Phoenix), in the 'else'-branch you just have a normal function call, which is not lazy and does not return a function-object. Hope that helps, Ovanes

Ovanes, you are right in as much as there's something wrong with what's inside the if. Problem is, I've tried far more than just what I posted here and I couldn't get it to compile once. So I figured I may use if_ the wrong way. Anyway, not posting a standalone example is generally not a good idea but I wanted to focus more on the general idea and how to tackle it rather than what may be wrong with a particular code example I may or may not have. Anyway, I'll attach at least a standalone test of what I meant here. It shows the error nicely but contains lots of other crap I've played around with. Cheers, Stephan On Wed, Jun 23, 2010 at 6:50 PM, Ovanes Markarian <om_boost@keywallet.com> wrote:
Stephan, hi! please see my answer below.
On Wed, Jun 23, 2010 at 5:42 PM, Stephan Menzel <stephan.menzel@gmail.com> wrote:
Unless I want to add something. Unfortunately I can't always take wrap()s output but have to modify a little. In fact, my Queue needs to be templatized as well, modifying the behaviour of the functor. I have a template parameter "Calling". When it is true, another function shall be called by the functor after the method was posted. Now I wanted to do this by phoenix:
template <bool Calling> class Queue { template<typename Handler> void post(Handler n_handler) { m_iosrv.post(n_handler); }; ... template<typename SlotSignature, typename SlotFunction> boost::signals2::connection connect(boost::signals2::signal<SlotSignature> &n_signal, SlotFunction n_method) { return n_signal.connect( if_( CallingBack ) [ post(n_method) , m_signal_callback() ] .else_ [ m_iosrv.wrap( n_method ) ] ); }; ...
It is difficult to conclude smth from the code without having a self-containing example. But my assumption is that in the 'else'-branch your code does not generate a function object. As far as I can see in the 'if'-branch your code generates a function object using the the comma-operator (if that operator is overloaded in Phoenix), in the 'else'-branch you just have a normal function call, which is not lazy and does not return a function-object. Hope that helps, Ovanes
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users

Stephan, as I stated before in the [] you need to have a function object which is called when the if_-condition is evaluated. It should not be a function call, returning a non-function object! Replacing Line 148 with: return n_signal.connect( if_( CallingBack ) [m_signal_callback]); will compile. If you need more complex szenario you need to make a composite function object, which calls both functions. Anything in [] should return a function object. I suggest you first try to create a sample composite function object without the whole complex machinery around and try to compile and run it first. Then you can take the function object over into your sample. Hope that helps, Ovanes On Thu, Jun 24, 2010 at 9:11 AM, Stephan Menzel <stephan.menzel@gmail.com>wrote:
Ovanes,
you are right in as much as there's something wrong with what's inside the if. Problem is, I've tried far more than just what I posted here and I couldn't get it to compile once. So I figured I may use if_ the wrong way.
Anyway, not posting a standalone example is generally not a good idea but I wanted to focus more on the general idea and how to tackle it rather than what may be wrong with a particular code example I may or may not have. Anyway, I'll attach at least a standalone test of what I meant here.
It shows the error nicely but contains lots of other crap I've played around with.
Cheers, Stephan
On Wed, Jun 23, 2010 at 6:50 PM, Ovanes Markarian <om_boost@keywallet.com> wrote:
Stephan, hi! please see my answer below.
On Wed, Jun 23, 2010 at 5:42 PM, Stephan Menzel < stephan.menzel@gmail.com> wrote:
Unless I want to add something. Unfortunately I can't always take wrap()s output but have to modify a little. In fact, my Queue needs to be templatized as well, modifying the behaviour of the functor. I have a template parameter "Calling". When it is true, another function shall be called by the functor after the method was posted. Now I wanted to do this by phoenix:
template <bool Calling> class Queue { template<typename Handler> void post(Handler n_handler) { m_iosrv.post(n_handler); }; ... template<typename SlotSignature, typename SlotFunction> boost::signals2::connection connect(boost::signals2::signal<SlotSignature> &n_signal, SlotFunction n_method) { return n_signal.connect( if_( CallingBack ) [ post(n_method) , m_signal_callback() ] .else_ [ m_iosrv.wrap( n_method ) ] ); }; ...
It is difficult to conclude smth from the code without having a self-containing example. But my assumption is that in the 'else'-branch your code does not generate a function object. As far as I can see in the 'if'-branch your code generates a function object using the the comma-operator (if that operator is overloaded in Phoenix), in the 'else'-branch you just have a normal function call, which is not lazy and does not return a function-object. Hope that helps, Ovanes
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
participants (2)
-
Ovanes Markarian
-
Stephan Menzel