Tuesday, July 22, 2008

Exterminating signals and slots

I recently posted at the clam devel wiki some advices, conventions, traps and tips on programing with Qt within CLAM addressed to the GSoC students. The most painful trap is the one of gratuitous signals and slots usage. Signals and slots is a very powerful way of designing independent components that comunicate each other with low coupling. Is that powerful that it is very tempting for novices to use them everywhere and this can turn very harmful for you.

If a connected slot doesn't exist, you just get an error console on run-time, checks on slots are that soft, and this is bad. Also the signal slot resolution is more expensive than just a method call. But the main reason to avoid them is that they make the code very hard to follow. When you see a signal 'emit' you have to find where such signal is connected in order to find which are the objects and slots that will be called. Multiply this process by nearly 200 signal emisions that were done at the vmqt library Annotator is using and you'll understand why such components although very smartly designed in structure, they are very hard to maintain and use.

You should emit signals just when you don't know which is the receiver of an event. If you can guess the receiver, there is no use for signals. On the other side, if you cannot access the emitter code, or you don't want to couple it, then there is room for a 'connect'.

A bad smell for noticing that you are over using sigslots is having in a class you are writting a connection like this:

connect(this, signal, knownWidget, slot)

and later, maybe in a different method:
emit signal()

When that known, and later forgotten, object is the only one you are connecting to the slot you might want to just keep a reference to it and just call the slot instead of emitting the signal.
knownWidget->slot()

which is faster, compile time checked and much more traceable.

Given that bad smell locator at hand i decided to do a fast review of vmqt module. vmqt is the successor of the many Visualization Modules we had in CLAM, just annother definitive VM rewrite. I tried to use it several times, but it is very hard to have a global view of what it does because all the program flow is driven by signal connections. No joke: 180 signal emissions and a similar number of 'connect's in 30 classes.

After a first review, 110 signals emisions have been avoided just by having a pointer to the Plot2D at the Renderers (the objects that represent drawing layers of the plot such as the lines, the playhead, the grid...). Having a pointer to it, Renderers can do a direct call to the Plot2D slots (now regular functions) instead of emitting a signal whose only receiver is the plot.

70 'emit's are still wandering around. Some of them communicate the plot with the wplot (a widget containing the plot and other elements such as the sliders, the rulers...) so they are used to syncronize. Those are likely to be removed in a similar way than for renderers.

The rest are specific renderer signals that are connected and propagated by the specific wplot. Those are harder to remove and indeed they are very convenient to keep.

As the extermination goes on, one can better see how to really take profit of the nice vmqt module structure and which aspects can be enhanced.