Signals/slots, user defined classes, and synchronicity

I have a few general questions about passing user defined classes through the signal/slot mechanism, as I’m having trouble reconciling my understanding of the documentation with my actual experiences.

I have a pair of classes (let’s call them class MainFrame and class CutsDialog, since that’s pretty descriptive of what they do :slight_smile: ). CutsDialog is a dialog window that is opened from MainFrame, through which the user (me :slight_smile: ) can set a number of options and cuts for drawing events in MainFrame. These cuts are stored in a third class, class Selector. When changes are made and committed by CutsDialog, I would like to pass back the result to MainFrame (and, in future, potentially to other sinks, like a logger and a text console) through the Signal/Slot mechanism.

I can’t figure out how to do that, despite claims in various documentation that it should be possible. For instance, the User’s Guide says:

[quote]All signals and slots are normal class methods and can take any number of arguments of any type.
[/quote]
but the compiler complains vociferously if I try to Emit a signal matching an arbitrary signature:

followed by a long list of possible matches, none of which come close to being a match. In fact, the User’s Guide goes on to say that you can really only Emit for single parameters or “arrays of Long_t”. Further, the implementation in class TQObject seems to only support the basic Root types in a single argument Emit() (ie, Int_t, Bool_t, etc), along with pointers Long_t* and const char* (ie, not even really a Long_t[], because it gets transported as a Long_t*, not as a contiguous block of memory of arbitrary length, so this doesn’t really answer my concerns below). I’ve tried a number of variations to get my object passed directly, and have failed. It really does not appear possible to do something like this:

or even

without some serious gymnastics to pack values into a Long_t[] (which you can’t do easily for objects that aren’t the same size as a Long_t … and which, in any case, is a far cry from passing “any number of arbitrary type”). Am I interpreting my experience correctly, or am I missing something? The documentation seems to be, at the very least, misleading in this case.

So, instead, I’m signaling with a pointer to the Selector owned by the CutsDialog (although, you really can’t do that, either … you must cast to const char *, and then cast back to the proper type in the slot) … and therein begins my second question. I’d certainly prefer signaling by value, not pointer, because the latter brings up all sorts of potential problems with respect to object lifetime and ownership. In particular, I find no guarantees in the documentation that the processing of Emit() is synchronous, although the current implementation appears to be … in fact, this Root Talk discussion from last September briefly mentioned the possibility of queued, asynchronous processing becoming likely or available in the future. Then, signaling by pointer would become quite dangerous, as the lifetime of the pointee may be over before the slot gets called. In my dialog, for instance, I Emit() the signal just before the dialog box closes itself and commits suicide … in this case, the Selector could be long gone well before it’s clients get told to look at it.

Practically, I guess, since there are a number of places where root signals by pointer (many Emit(“Event_t*”,e) instances, for example), this is probably not a large concern. Am I right in this interpretation? Or could I be getting myself into trouble in future Root versions by passing pointers to objects around?

In any case, an implementation that really allowed arbitrary arguments to Emit() would be quite welcome.

Thanks in advance for your help, and greatest apologies for my long-winded post.

Hi,

is slightly different than [quote]I try to Emit a signal matching an arbitrary signature:[/quote]I hope you see the difference…
And yes, you can have any number of arguments of any type, as shown in several place in the code.
And yes, unfortunately, you have to pass an array as shown there: Long_t args[2]; args[0] = (Long_t)obj; args[1] = kTRUE; Emit("SignalName(TObject*,Bool_t)", args); And this allows to pass any kind of argument (only pointers and not references)…
For the second point, I think there is actually no plan to implement signal/slots with any number of arguments AND an arbitrary signature…

Cheers,
Bertrand.

Sure, I do see the difference. My interpretation of the documentation suggests that it widely mixes what I guess should be two separate issues in Root: the signal process (initiated by Emit()), and the signalling method. Since the signature of the signaling method must match the first argument of the contained Emit(), I read statements in the documentation to imply Emit() should take a similarly arbitrary number of arguments. I was hoping that this identification of different issues really meant what I wanted it to mean … but I guess it doesn’t. Does that make any sense?

If arguments must be passed through a Long_t[], this statement can only be true for a very restricted definition of “any type”: one can only pass types through the signal that can be coerced to Long_t. Precious few types pass the coercion requirement directly (not even all the built-in types pass this requirement … Double_t -> Long_T -> Double_t certainly fails on ia32, even with a reinterpret_cast). The alternative is to pass objects by stuffing pointers (potentially to local variables!) into the array … but then I’m not signaling by “any type”, but “any type*”, which I hope you’ll agree is a completely different semantics and mechanics than “any type”. But in neither case do I actually get to pass-by-value through the signal for arbitrary types (beyond the explicit, one argument built-ins for which there are Emit() overloads), exposing the lifetime issues I was worried about previously. I hope I’ve made myself clear.

I can certainly live with this, but it isn’t quite what the documentation seemed to be implying to me on a first reading … the comparisons in various Root Talk posts to Qt signals, and my passing familiarity with Qt and Boost.signals, may have pushed me over the edge, completely into fantasy land :slight_smile: I guess I was seeing more of what I hoped was there than what is actually there.

Thanks for your comments!

Hi,

I agree with you that the statement “any type” is not fully true and quite misleading… And I also agree that it would be nice to have signal/slots with an arbitrary signature, but I guess we all have to live with this for the time being… :wink: [quote]Thanks for your comments![/quote]You are most welcome :slight_smile:

Cheers, Bertrand.

1 Like