Event Callbacks

The derived subclasses of Event<T> support two different type signatures for callbacks. They differ by whether a System<T>& argument is present. For this discussion, call them legacy (no system) callbacks and system callbacks.

One goal of offering both signatures is to support the possibility of simulation without heap transactions after initialization. The motivation is to enable certain kinds of soft real-time applications of Drake; see #14543 for more detail. Even with careful support within Drake, implementers of such systems need to analyze and test carefully to see the heap proscription is maintained.

The most common and convenient use of events and callbacks will be indirect, via the LeafSystem Declare*Event() methods. The system callbacks permit those methods to avoid heap usage induced in std::function by lambdas with captures of a certain size. Empirically, the capture of an ordinary pointer and a (wider) pointer-to-member-function are enough to induce heap allocations.

Direct clients of the event callbacks here may prefer the legacy callback, together with a lambda capture of a pointer of the exact type needed. This technique avoids a down-cast within the callback body. However, if other captures are also necessary, the system callback (with a down-cast) may help avoid heap use.