[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
Hi,
maybe I managed to narrow it down a little.
This works for both functors and member functions:
class Queue {
...
template
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
&n_signal, Slot n_method) { return n_signal.connect(boost::bind(&CommandQueue::slotWrapper<Slot>, this, n_method)); }; template boost::signals2::connection connect(boost::signals2::signal & n_signal, Slot n_method) { return n_signal.connect(boost::bind(&CommandQueue::slotWrapper , 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
void slotWrapper(Slot n_method, T1 t1) { boost::function 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
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
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
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
Stephan, hi! please see my answer below.
On Wed, Jun 23, 2010 at 5:42 PM, Stephan Menzel
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
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
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
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
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