From: Edward Diener (eddielee_at_[hidden])
Date: 2003-09-14 19:34:33
This is a proposal for a plan for the Boost.Signals library to implement a
more complete algorithm for the order of slots which are called when
are connected to a single signal. Some of this follows the explanation of
documentation in Boost.Signals under "Ordering slot call groups", some of
offers what I fell is a better solution at the cost of changing the current
it is done, and some of this offers an extension to what already exists.
1) Group values are stilled used and they are still 'int's.
2) Group values can go from the greatest negative 'int' to the greatest
3) A group value of a given name, which let us call SIGNALS_GROUP_TOP,
specifies the greatest positive 'int' value and may be passed. This value
places that slot at the top of the list for a given signal.
4) A group value of a given name, which let us call SIGNALS_GROUP_BOTTOM,
specifies the greatest negative 'int' value and may be passed. This value
places that slot at the bottom of the list for a given signal.
5) A slot which does not pass a group value automatically gets a group value
of 0, which places it in the middle of the list and not at the end as the
current implementation does.
Slots for a given signal are called in the order of their group value from
highest 'int' to lowest 'int'. The interesting question, obviously, is in
order slots, which have the same group value, are called. What follows is a
design suggestion for that occurrence.
1) Ordering for a given signal for slots with the same group number may be
random, FIFO ( first in, first out ), LIFO ( last in, first out ), and
user defined. This can be done with an enum with values such as
SIGNALS_ORDER_RANDOM, SIGNALS_ORDER_FIFO, SIGNALS_ORDER_LIFO, and
SIGNALS_ORDER_USER. A function of the connection can let the user change
this value from the default. The default should be SIGNALS_ORDER_FIFO
and not SIGNALS_ORDER_RANDOM which is essentially the current
2) If SIGNALS_ORDER_USER is specified the user should pass into the
a Boost::Function as his callback which takes two const references to
the appropriate Boost::Function for the signal and returns a bool.
Each time the Signals library has to decide in what order slots
should be called for a specific signal, when the slots have the same group
number, it starts with the first slot in its list for that group number
and begins comparing it to every other slot after it using the above
Boost::function callback. If the callback returns true, it keeps the order.
If the callback returns false, it moves the other slot in front of it
in the order but continues to compare the original slot in the same way
with all other uncompared slots.
Then it goes to the next slot at the beginning of the list and does the
same, making sure not to compare again with any slot that has already been
previously compared. It follows this algorithm until the final list order
is determined and then calls each slot in that order.
An obvious alternative to the above is to pass the entire list as a vector
of slots and let the end-user reorder it as he wants. However I do prefer
the previous method as being easier, from the end-user's point of view,
to use, although obviously more iterative ( multiple calls versus a single
Rationale for the group values suggestion:
The using of group numbers is a good idea to allow the ordering of slots for
a given signal by groups. One should, however, be allowed to say that a slot
should go to the top group or the bottom group without worrying
about what group number this is, thus the suggestion of a macro-like
SIGNALS_GROUP_TOP and SIGNALS_GROUP_BOTTOM. Of course the end-user can do it
himself with std::numeric_limits<int>::max and
but I think the implementation should offer it instead, in case a future
which still retains the same concept but changes the internal representation
of top and bottom. Specifying no group number should place the slot in the
middle and not the bottom since "no group number" should be neutral and not
an indication of last. This also allows an end-user to use negative numbers
to specify groups which are called after the default slots of "no group
and specify positive numbers to specify groups which are called before the
default slots of "no group number". This change very likely will break no
existing code since current usage is, almost certainly, to specify
positive group numbers as ways of having groups whose slots are called
slots with no group numbers. Conceptually using positive and negative group
numbers, top and bottom values, and no group numbers as a neutral 0 value,
makes it much easier to allow and devise a system whereby multiple slots can
ordered by group number.
Rationale for ordering suggestion within a given group number:
I do not believe that typical signal/slot systems which allow a given signal
to be handled by multiple slots are normally random systems for calling the
slots. My own experience with every event-handling system which I have ever
used is that slots are called in the order in which they are connected to
their signal ( FIFO ). I strongly recommend this is the default and not a
random calling of slots for a given signal.
On the other hand, a robust system which needs to appeal to programmers for
years to come and which will hopefully become part of C++ in the future as
its event handling metaphor, will need to be as flexible as possible in the
area of the order of slots which are called for a given signal. By allowing
the end-user to choose this order, there should be very little complaint
that the system is not flexible enough. The four choices should cover nearly
any possibility to which an end-user might wish to apply to the order, and
satisfy nearly anyone's needs. While the user-defined method may come in for
the most criticism, as being unnecessary, as being too slow, as having too
many iterations, one should remember that
a) It will be the end-user's choice to specify it and incur the
overhead involved. All those who never use it pay no speed penalty.
b) It will almost surely be used when their are a small number of disparate
event handlers in different objects, functors, functions etc. and the
end-user must decide at run-time which slot should be called first just
before the signal is triggered.
Knowledge of the internals of Boost.Signals:
I have not looked at the internals of Boost.Signals and do not know how it
works except from a user's point of view. My suggestion is made from a
designer's point of view, of essentially what would be the best ideal
to the thorny problem of how to order the calling of slots for a signal. I
believe that this is a very important issue because I believe that event
handling in computer programming will be a bigger metaphor in the future
and that C++ must address this issue at least at the library level. I do
not think that C++ can afford to turn its back on this metaphor and I would
like to see a solution that offers the greatest flexibility and ease of
use in what I believe is the spirit of C++.
Mr. Gregor knows what can and can't be done along the lines I have
but I do hope that my suggestion will be seen as a viable ideal. His current
implementation is pretty close to what I have suggested, and my suggestion
is a way to further nail down a conceptually easy and yet strong system
for ordering the calling of slots. I am aware that at the present time it
be impossible for an end-user to really know what slot really corresponds to
what handling function, but I antcipate that this may change in the future.
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk