[signals] is assertion necessary when no slots connected

When signalling, with no slots connected, an assertion is raised (if the return type of the slot is not void). For example: boost::signal<void ()> sig; sig(); does "nothing" while boost::signal<int ()> sig; sig(); asserts (if the default combiner is used). I guess that makes sense, because there can be no "combined" return value if there are no slots. However, an assertion seems a bit harsh. I am really not sure what should happen, but maybe an exception is a better alternative, or maybe an alternative interface can define a default value to be returned if there are no slots connected), or maybe something else... Anyway, I guess I was wanting to know why this is the case, and what is the usual method of handling this circumstance. I assume something like: if (!sig.empty()) { result = sig(); } but that just does not look pretty...

On Tuesday 17 February 2004 05:01 pm, Jody Hagins wrote:
asserts (if the default combiner is used). I guess that makes sense, because there can be no "combined" return value if there are no slots. However, an assertion seems a bit harsh. I am really not sure what should happen, but maybe an exception is a better alternative,
Well, we could throw bad_function_call, perhaps, or pick/create some other exception (I'm at a loss for exception names for this).
or maybe an alternative interface can define a default value to be returned if there are no slots connected), or maybe something else...
Well, you can always write a different combiner that does this.
Anyway, I guess I was wanting to know why this is the case, and what is the usual method of handling this circumstance. I assume something like:
if (!sig.empty()) { result = sig(); }
but that just does not look pretty...
No, it isn't pretty; perhaps the exception is the right way to go, depending on your application. Doug

On Tue, 17 Feb 2004 18:45:19 -0500 Douglas Gregor <gregod@cs.rpi.edu> wrote:
Well, we could throw bad_function_call, perhaps, or pick/create some other exception (I'm at a loss for exception names for this).
Either way... if you create a new one, maybe something like no_slots_connected.
Well, you can always write a different combiner that does this.
Right, but then that is a fair amount of work to duplicate the code for the current one, to avoid the assertion. Not much way to actually use the existing code the way it is.
No, it isn't pretty; perhaps the exception is the right way to go, depending on your application.
Currently, I am doing the check for empty. Actually, I wanted to discuss my application with you, because it is actually an extension of the signal/slot mechanism. Consider the case where you want to signal events with multiple types. Since a slot and signal have to match signatures explicitly, you are limited in how you can direct the signals. Specifically, you must know exactly which signal object can "signal" certain events. Consider a slot that has the following interface: struct some_slot { void operator()(some_class const & sc) { } }; This slot must be connected with a signal of the type boost::signal<void (some_class const&)> which means that that the connection logic must know which signal object meets that criteria. Suppose you have many different signatures (or think of it is events) that you want to dispatch (i.e., signal). What I want is the ability to have a single signal that can dispatch multiple signatures (but I do not want to give up any type safety). For example, what I have now is very much like: boost:multisignal sig; sig(); sig(some_class()); sig(yet_one_more_class()); The underpinning is based upon typesafe access (demux based on the type) to the right bost::signal instance, and then forwarding the signal dispatching to that signal, and I have a protoyype that allows this as well: sig(another_class(), and_another_class()); This provides power and flexibility to many of our applications, and allows us to create specific event objects, and then dispatch them via a single multisignal object, not really caring about the type of the object that we are dispatching, since the multisignal will correctly (and safely) take care of that redirection. A typical "slot" might have multiple handler functions... struct some_slot { void operator()(); void operator()(some_class const &); void operator()(another_class const &, and_another_class const &); }; Currently, the connection is a bit tricky, and has to be done explicitly (or wrapped with clever type lists)... sig.connect<some_class>(a_slot); so that the multisignal knows that a_slot is interested in signals that have some_class as the signature. I have not yet found a way to automatically deduce all the "operator()" function signatures. If I could, then connection to all the proper underlying signals would be much nicer as well. If you are interested, I'd be willing to carry on a discussion offline (unless there are enough sig/slot users interested in this). I will admit that nothing is new under the sun, so this may have already come up and been discounted. I have been using a very similar technique for years, and ported the ideas on top of sig/slot when I first started playing with boost, since it decouples the code from any other internal libraries. Some of the more advanced features are not yet ported to boost::signals, because they are implemented using the loki typelist stuff, and I have as of yet played with whatever boost component would replace that stuff (I imagine that would be mpl). -- Jody Hagins The early bird who catches the worm works for someone who comes in late and owns the worm farm. -- Travis McGee

Jody Hagins wrote: [...]
What I want is the ability to have a single signal that can dispatch multiple signatures (but I do not want to give up any type safety). For example, what I have now is very much like:
boost:multisignal sig; sig(); sig(some_class()); sig(yet_one_more_class());
The underpinning is based upon typesafe access (demux based on the type) to the right bost::signal instance, and then forwarding the signal dispatching to that signal, and I have a protoyype that allows this as well:
sig(another_class(), and_another_class());
This provides power and flexibility to many of our applications, and allows us to create specific event objects, and then dispatch them via a single multisignal object, not really caring about the type of the object that we are dispatching, since the multisignal will correctly (and safely) take care of that redirection.
A typical "slot" might have multiple handler functions...
struct some_slot { void operator()(); void operator()(some_class const &); void operator()(another_class const &, and_another_class const &); };
Currently, the connection is a bit tricky, and has to be done explicitly (or wrapped with clever type lists)...
sig.connect<some_class>(a_slot);
so that the multisignal knows that a_slot is interested in signals that have some_class as the signature. I have not yet found a way to automatically deduce all the "operator()" function signatures. If I could, then connection to all the proper underlying signals would be much nicer as well.
That's nice. One alternative approach would be to pass a signature to connect: sig.connect< void (some_class const&) >(a_slot); or, going a bit further in that direction, require a nested signature list: struct some_slot { void operator()(); void operator()(some_class const &); void operator()(another_class const &, and_another_class const &); typedef list< void(), void(some_class const &), void(another_class const &, and_another_class const &) > signature_list; };
participants (3)
-
Douglas Gregor
-
Jody Hagins
-
Peter Dimov