I'm considering different approaches to implementing events in a C++ application. There's a suggestion to implement a centralized event dispatch, via a notification center. An alternative would be for sources and targets of the events to communicate directly. I'm having reservations about the notification center approach, however. I'll outline both approaches as I see them (I could well be misunderstanding something about them, I've never implemented event handling before).
a) Direct communication. Events are a part of their source's interface. Objects interested in the event must somehow get a hold of an instance of the source class and subscribe to its event(s):
struct Source
{
Event</*some_args_here*/> InterestingEventA;
Event</*some_other_args_here*/> InterestingEventB;
};
class Target
{
public:
void Subscribe(Source& s)
{
s.InterestingEventA += CreateDelegate(&MyHandlerFunction, this);
}
private:
void MyHandlerFunction(/*args*/) { /*whatever*/ }
};
(From what I understand, boost::signals, Qt signals/slots and .NET events all work like this, but I could be wrong.)
b) Notification center. Events aren't visible in their source's interface. All events go to some notification center, probably implemented as a singleton (any advice on avoiding this would be appreciated), as they are fired. Target objects don't have to know anything about the sources; they subscribe to certain event types by accessing the notification center. Once the notification center receives a new event, it notifies all its subscribers interested in that particular event.
class NotificationCenter
{
public:
NotificationCenter& Instance();
void Subscribe(IEvent& event, IEventTarget& target);
void Unsubscribe(IEvent& event, IEventTarget& target);
void FireEvent(IEvent& event);
};
class Source
{
void SomePrivateFunc()
{
// ...
InterestingEventA event(/* some args here*/);
NotificationCenter::Instance().FireEvent(event);
// ...
}
};
class Target : public IEventTarget
{
public:
Target()
{
NotificationCenter::Instance().Subscribe(InterestingEventA(), *this);
}
void OnEvent(IEvent& event) override {/**/}
};
(I took the term "Notification center" from Poco, which, as far as I understand, implements both approaches).
I can see some pros to this approach; it will be easier for the targets to create their subscriptions because they wouldn't need access to the sources. Also, there wouldn't be any lifetime management problems: unlike sources, the notification center will always outlive the targets, so they targets always unsubscribe in their destructors without worrying whether the source still exists (that's a major con I can see in the direct communication). However, I am afraid that this approach could lead to unmaintainable code, because:
All sorts of events, probably completely unrelated to each other, would go to this one big sink.
The most obvious way of implementing the notification center is as a singleton, so it will be hard to track who and when modifies the subscribers' list.
Events aren't visible in any interface, so there is no way to see whether a particular event belongs to any source at all.
As a result of these cons, I'm afraid that as the application grows it would become very difficult to track connections between objects (I'm imagining problems trying to understand why some particular event doesn't fire, for example).
I'm looking for advice regarding the pros and cons of the "notification center" approach. Is it maintainable? Does it fit every sort of applications? Maybe there are ways to improve the implementation? Comparison between the two approaches I described, as well as any other event handling suggestions, are most welcome.
These approaches are orthogonal. Direct communication should be used when the events are exchanged between specific objects. The notification center approach should only be used for broadcast events, e.g. when you want to process all events of a given type regardless of their source, or when you want to send an event to some set of objects which you don't know in advance.
To avoid singletons, reuse the code for direct communication and subscribe the notification center object to all events you want to process in this way. To keep things manageable, you would do this in the emitting objects.
The lifetime-related problems with direct communication are usually solved by requiring that every class that subscribes to any events must be derived from a specific base class; in Boost.Signals, this class is called trackable. The equivalent of your CreateDelegate function stores the information about subscriptions for the given object in data member within trackable. On destruction of trackable, all subscriptions are cancelled by calling the matching Unsubscribe functions. Note that this is not thread-safe, because the destructor of trackable is called only after the derived class destructor finishes - there is a period where a partially destroyed object can still receive events.
I will focus on pros and cons of the "NotificationCenter" solution as required by the question (though I'd call it DataBus, Dispatcher or Publisher/Subscriber, instead).
Pros:
High decoupling of the classes
The Subscriber of an event doesn't need to know the source of the event
Concurrency management is simplified if you use an asynchronous NotificationCenter (you can avoid multithreading by using the NotificationCenter as a scheduler of cpu time).
An event driven architecture is higly testable / observable
Cons:
The interface is implicit (i.e. the interface of a class does not expose the events)
The partitioning of the application is critical, in order to avoid duplication of the state in the classes
Reusability in another application becomes harder (when you want to reuse a class in another application, it brings with itself the NotificationCenter)
It's difficult to insert a new elaboration in the flow of events from input to output (i.e.: if class A emits event E and class B registers for notifications of E, you cannot insert a class C "between" A and B to change the content of E let's say to do some kind of elaboration on it)
The NotificationCenter must manage client's errors (i.e. the NotificationCenter should provide an exception handler to catch the exeptions thrown in the event handlers).
In addition, I'd like to point out that it's not indispensable that the NotificationCenter be a singleton. In fact you can have multiple instances of NotificationCenter, each managing different category of events (e.g. in an embedded system you can have a NotificationCenter for the low level hardware events and another for the high level logic).
We should limited the NotificationCenter design, it has a lot of cons for code maintain.
It hidden the object relation ship into the implementation, you cannot
get clue from caller code, who actually will explicit bind the observer and target
object in Direct communication.
You can not see the even interface from the header, you need to look
into implementation.
You have bind your business logical with the notification center, you cannot easily unit-test the business component and reuse it in other project.
The benifit of NotificationCenter is that you doesn't need to know the event source, so you should only use NotificationCenter when there is multiple event source for same event, or even source number will change.
For example, in Windows platform you want to listen all windows size changed event, but at the same time the new window can be created.
Related
Imaging a simple double-dispatch event handling scheme:
It's cool. Moreover it works. However I see several issues here:
EventHandlerInterface must explicitly know all the events.
As a result of (1) handlers may have large space overhead because of subscribing for unused events.
What I want this structure to be, looks more like this:
As you can see, I want separate event types produced by different modules and I want for each module to implement only necessary event handling interfaces. I am not sure how to tie this all together. Ideally I would like to have a list of EventServices ready to accept any event.
One thing, I can think of here, is to have an inherited EventService for each EventHandlerInterface type and downcast event handlers to the certain type inside notify method. However it is not type safe and looks like an ugly idea.
Second approach, I can see, is to make EventService a template and statically check template argument to be a child of EventHandlerInterface. That would be type safe and will almost do what I want. However this way I can not store all EventServices into one list/vector as I would like to. Therefore this approach is not preferable.
Actually it feels like I am looking the wrong way but how to find the right one I don't know.
Thanks!
I am not sure the visitor pattern is the right approach for this problem. Sure it gives you a mechanism to achieve double dispatch but that's about it, and as you said it has drawbacks, e.g. the visitor (EventHandlerInterface) has to provide an overload for every different type of host (EventInterface). This creates a tight coupling and only makes sense if you add classes to the host hierarchy very infrequently.
Why not go for a solution according to the publish-subscribe pattern? It allows loose coupling and avoids the need of redundant interfaces. In brief, the publisher offers some events, to which subscribers can subscribe. When an event is triggerd, subscribers are notified and handle it. Subscribers only subscribe to events they find relevant. Different subscribers can provide different handling for the same type of event, which seems to be the purpose of double dispatch in your case.
In many languages (e.g. C#) there is special syntax for supporting this pattern easily. In C++ you need to use a library such as boost signals although there are others too. See also here for an example.
In the following example, the spawned actor is of type a_type, which is based on the typed_actor template. How do I convert from a_type to an instance of class A?
using a_type = typed_actor<replies_to<int>::with<void>>;
class A : public a_type::base
{
protected:
behavior_type make_behavior() override
{
return
{
[this](int value)
{
aout(this) << value << endl;
}
};
}
};
int main()
{
a_type spawned = spawn_typed<A>();
}
Actors are asynchronous entities and should be considered isolated. While it is technically possible to get a raw pointer via actor_cast and downcast it to the actual type, I strongly advise against doing so. Only an actor itself is allowed to access its state in CAF. Decoupling actors via message passing avoids race conditions by design. Instead of calling members directly, actors send messages to mailboxes of others. These mailboxes are real concurrency workhorses and implemented without locks. Ideally, you spawn more actors than you have cores available, since actor applications scale by splitting large problems into many small chunks which are then solved by lots of actors concurrently. Always keep Amdahl's law in mind: your application cannot be faster than your longest sequential processing chain.
The actor handles in CAF also enable network-transparency. Once you start to distribute your application across the network, accessing state directly will simply not work. Further, if you are using state_based actors, accessing the state from outside an actor is highly unsafe, because the state will be destroyed once the actor called quit() or got killed.
There are basically three ways to exchange information:
1:1 message passing. This is the correct solution most of the time. You can always use a scoped_actor if you need an ad-hoc way to communicate to an actor.
N:M message passing using groups. This is an easy and convenient way to push updates from any number of producers to any number of subscribers. CAF offers both named groups as well as "ad-hoc" anonymous groups to allow loosely coupled communication.
Sharing transactional (or otherwise synchronized) data structures. If you need to integrate actors into an existing application or need to combine actors with other concurrency abstractions, sharing concurrent data structures between actors and non-actors can be a valid solution. In this model, you pass the data structure to an actor via its constructor. However, before doing that, you should consider a design with plain message passing and groups (or actor pools) to organize concurrent workers first. This limits your application to a single machine and takes away design space for future developments.
Actor pools also enable 1:N communication (broadcasting), "scatter/gather"-style workflows, etc.
In any case, breaking the abstraction provided by CAF is usually a bad idea and you can end up in "undefined behavior land" real quick. One final note, if you find yourself in a dead-end with your current design, please feel free to start a discussion on the CAF mailing list.
An instance of class A can be obtained by first invoking actor_cast and then using dynamic_cast:
int main()
{
a_type spawned = spawn_typed<A>();
abstract_actor* abstractActor = actor_cast<abstract_actor*, a_type>(spawned);
A* a = dynamic_cast<A*>(abstractActor);
}
I am working on a large project in C++ that will have a graphical user interface.
The user interface will use some design pattern (MVVM/MVC) that will rely on the observer pattern.
My problem is that I currently have no way of predicting which parts of the Model should be observable. And there are many, many parts.
I find myself being pulled in several directions due to this issue:
If I develop back-end classes that do not support notification I will find myself violating the Open-Closed principle.
If I do provide support for notification to all Model classes and all of their data members it will have a massive performance cost that is unjustified since only a fraction of this support will actually be needed (even though this fraction is unknown).
The same is true if I only provide support for extension by making all non-const methods virtual and accessing these methods through base-pointers. This will also have a cost in readability.
I feel that out of these 3, (1.) is probably the lesser evil.
However, I feel like an ideal solution should actually exists in some language (definitely not C++), but I don't know if it's supported anywhere.
The unicorn solution I was thinking of is something like this:
Given a class Data, shouldn't it be possible for clients that seek to make Data observable do something like
#MakeObservable(Data)
as a compile time construct. This in turn would make it possible to call addObserver on Data objects and modify all assignments to data members with notifiers. it would also make you pay in performance only for what you get.
So my question is two-fold:
Am I right to assume that out of the 3 options I stated, (1.) is the lesser but necessary evil?
Does my unicorn solution exist anywhere? being worked on? or would be impossible to implement for some reason?
If I understand correctly, you're concerned with the cost of providing a signal/notification for potentially every observable property of every object.
Fortunately you're in luck, since storing a general thread-safe notifier with every single property of every object would generally be extremely expensive in any language or system.
Instead of getting all clever and trying to solve this problem at compile-time, which I recommend would shut out some very potentially useful options to a large-scale project (ex: plugins and scripting), I'd suggest thinking about how to make this cheaper at runtime. You want your signals to be stored at a coarser level than the individual properties of an object.
If you store just one with the object that passes along the appropriate data about which property was modified during a property change event to filter which clients to notify, then now we're getting a lot cheaper. We're exchanging some additional branching and larger aggregates for the connected slots, but you get a significantly smaller object in exchange with potentially faster read access, and I'd suggest this is a very valuable exchange in practice.
You can still design your public interface and even the event notification mechanism so that clients work with the system in a way that feels like they're connecting to properties rather than the whole object, perhaps even calling methods in a property (if it's an object/proxy) to connect slots if you need or can afford a back pointer to the object from a property.
If you're not sure, I would err on the side of attaching event slots to properties as well as modifying them as part of the object interface rather than property interface, as you'll have a lot more breathing room to optimize in exchange for a slightly different client aesthetic (one that I really don't think is less convenient so much as just 'different', or at least potentially worth the cost of eliminating a back pointer per property).
That's in the realm of convenience and wrapper-type things. But you don't need to violate the open-closed principle to achieve MVP designs in C++. Don't get crammed into a corner by data representation. You have a lot of flexibility at the public interface level.
Memory Compaction -- Paying for What We Use
On discovering that efficiency plays an important role here, I'd suggest some basic ways of thinking to help with that.
First, just because an object has some accessor like something() does not mean that the associated data has to be stored in that object. It doesn't even have to be stored anywhere until that method is called. If memory is your concern, it can be stored at some level outside.
Most software breaks down into hierarchies of aggregates owning resources. For example, in a 3D software, a vertex is owned by a mesh which is owned by the scene graph which is owned by the application root.
If you want designs where you pay almost no memory cost whatsoever for things that are not being used, then you want to associate the data to the object at a coarser level. If you store it directly in the object, then every object pays for what something() returns regardless of whether it is needed. If you store it indirectly in the object with a pointer, then you pay for the pointer to something() but not for the full cost of it unless it is used. If you associate it to the owner of the object, then retrieving it has a lookup cost, but one that is not as expensive as associating it to the owner of the owner of the object.
So there's always ways to get something very close to free for things you don't use if you associate at a coarse enough level. At granular levels you mitigate lookup and indirection overhead, at coarse levels you mitigate costs for things you don't use.
Massive Scale Events
Given massive scalability concerns with millions to billions of elements being processed, and still the desire for potentially some of them to generate events, if you can use an asynchronous design, I'd really recommend it here. You can have a lock-free per-thread event queue to which objects with a single bit flag set generate events. If the bit flag is not set, they don't.
This kind of deferred, async design is useful with such scale since it gives you periodic intervals (or possibly just other threads, though you'd need write locks -- as well as read locks, though writing is what needs to cheap -- in that case) in which to poll and devote full resources to bulk processing the queue while the more time-critical processing can continue without synchronizing with the event/notifier system.
Basic Example
// Interned strings are very useful here for fast lookups
// and reduced redundancy in memory.
// They're basically just indices or pointers to an
// associative string container (ex: hash or trie).
// Some contextual class for the thread storing things like a handle
// to its event queue, thread-local lock-free memory allocator,
// possible error codes triggered by functions called in the thread,
// etc. This is optional and can be replaced by thread-local storage
// or even just globals with an appropriate lock. However, while
// inconvenient, passing this down a thread's callstack is usually
// the most efficient and reliable, lock-free way.
// There may be times when passing around this contextual parameter
// is too impractical. There TLS helps in those exceptional cases.
class Context;
// Variant is some generic store/get/set anything type.
// A basic implementation is a void pointer combined with
// a type code to at least allow runtime checking prior to
// casting along with deep copying capabilities (functionality
// mapped to the type code). A more sophisticated one is
// abstract and overriden by subtypes like VariantInt
// or VariantT<int>
typedef void EventFunc(Context& ctx, int argc, Variant** argv);
// Your universal object interface. This is purely abstract:
// I recommend a two-tier design here:
// -- ObjectInterface->Object->YourSubType
// It'll give you room to use a different rep for
// certain subtypes without affecting ABI.
class ObjectInterface
{
public:
virtual ~Object() {}
// Leave it up to the subtype to choose the most
// efficient rep.
virtual bool has_events(Context& ctx) const = 0;
// Connect a slot to the object's signal (or its property
// if the event_id matches the property ID, e.g.).
// Returns a connection handle for th eslot. Note: RAII
// is useful here as failing to disconnect can have
// grave consequences if the slot is invalidated prior to
// the signal.
virtual int connect(Context& ctx, InternedString event_id, EventFunc func, const Variant& slot_data) = 0;
// Disconnect the slot from the signal.
virtual int disconnect(Context& ctx, int slot) = 0;
// Fetches a property with the specified ID O(n) integral cmps.
// Recommended: make properties stateless proxies pointing
// back to the object (more room for backend optimization).
// Properties can have set<T>/get<T> methods (can build this
// on top of your Variant if desired, but a bit more overhead
// if so).
// If even interned string compares are not fast enough for
// desired needs, then an alternative, less convenient interface
// to memoize property indices from an ID might be appropriate in
// addition to these.
virtual Property operator[](InternedString prop_id) = 0;
// Returns the nth property through an index.
virtual Property operator[](int n) = 0;
// Returns the number of properties for introspection/reflection.
virtual int num_properties() const = 0;
// Set the value of the specified property. This can generate
// an event with the matching property name to indicate that it
// changed.
virtual void set_value(Context& ctx, InternedString prop_id, const Variant& new_value) = 0;
// Returns the value of the specified property.
virtual const Variant& value(Context& ctx, InternedString prop_id) = 0;
// Poor man's RTTI. This can be ignored in favor of dynamic_cast
// for a COM-like design to retrieve additional interfaces the
// object supports if RTTI can be allowed for all builds/modes.
// I use this anyway for higher ABI compatibility with third
// parties.
virtual Interface* fetch_interface(Context& ctx, InternedString interface_id) = 0;
};
I'll avoid going into the nitty gritty details of the data representation -- the whole point is that it's flexible. What's important is to buy yourself room to change it as needed. Keeping the object abstract, keeping the property as a stateless proxy (with the exception of the backpointer to the object), etc. gives a lot of breathing room to profile and optimize away.
For async event handling, each thread should have a queue associated which can be passed down the call stack through this Context handle. When events occur, such as a property change, objects can push events to this queue through it if has_events() == true. Likewise, connect doesn't necessarily add any state to the object. It can create an associative structure, again through Context, which maps the object/event_id to the client. disconnect also removes it from that central thread source. Even the act of connecting/disconnecting a slot to/from a signal can be pushed to the event queue for a central, global place to process and make the appropriate associations (again preventing objects without observers from paying any memory cost).
When using this type of design, each thread should have at its entry point an exit handler for the thread which transfers the events pushed to the thread event queue from the thread-local queue to some global queue. This requires a lock but can be done not-too-frequently to avoid heavy contention and to allow each thread to not be slowed down by the event processing during performance-critical areas. Some kind of thread_yield kind of function should likewise be provided with such a design which also transfers from the thread-local queue to the global queue for long-lived threads/tasks.
The global queue is processed in a different thread, triggering the appropriate signals to the connected slots. There it can focus on bulk processing the queue when it isn't empty, sleeping/yielding when it is. The whole point of all of this is to aid performance: pushing to a queue is extremely cheap compared to the potential of sending synchronous events every time an object property is modified, and when working with massive scale inputs, this can be a very costly overhead. So simply pushing to a queue allows that thread to avoid spending its time on the event handling, deferring it to another thread.
Templatious library can help you with completely decoupling GUI and domain logic and making flexible/extendable/easy to maintain message systems and notifiers.
However, there's one downside - you need C++11 support for this.
Check out this article taming qt
And the example in github: DecoupledGuiExamples
So, you probably don't need notifiers on every class, you can just shoot messages from the inside functions and on specific classes where you can make any class send any message you want to GUI.
Using C++ & MFC I've created a class which makes it easier to add drag/drop functionality to a CWnd object. There is nothing special about it really. Currently it is used like so:
Create CDropListener object
Call a method on the CDropListener object saying which file extension you want it to react to and a function pointer of what to call when a file is dropped
Register this with the CWnd object
Delete the CDropListener object when the window is destroyed
Repeat all of the above steps if you want different file extensions for a different CWnd
It is a bit cumbersome having to create a class member variable for each listener, and I was just wondering which design pattern would be more suitable for this. I only need the member objects so that I can delete them at the end. I thought I could just use an array to store them in and it would simplify it a bit, but I also thought there might be a better way where you can just call a static function similar to DropListener::RegisterListener(CWnd* wnd, CString& extension, void(*callback) callback) and it handles all of the creating / registering / deleting for you.
I'm not familiar with MFC but from an OO point of view your design can be improved.
First identify what aspects of your requirements are most likely to change and then identify what the interfaces needed to isolate those changes are:
Changes:
The thing you want to listen for (the event)
The action you want to take (the callback)
Interfaces:
The mechanism of adding a callback related to an event to the event notifier
The mechanism of calling the callback from the notifier
So you need an Event interface, a Callback interface, and a Notifier interface.
In C++ there is a handy thing called std::function<T> where T is any callable type (a pointer to a function, a functor, a lambda). So you should probably use that for encapsulating your callbacks to give your user more freedom.
Then how may event types do you want to support? This will tell you whether you need to support different Event objects as well as how your registration will look:
// For example if you support just `Drop` events:
void addDropListener(std::function<T> callback);
// If you support many events:
void addListener(Event::Type evType, std::function<T> callback);
Once you have answered that you need to decide what a "callback" looks like (T in the above examples). This could return a value (if you want success verification) or throw a particular exception type (make sure to document the contract). Then ask if you want a copy of the event that was fired (usually you would). Assuming you are happy to only be notified of errors via exceptions then you can typedef the expected std::function like this:
typedef std::function<void (const Event&)> EventCallback;
I recommend then that your Notifier implementer use a std::vector<EventCallback> or std::map<Event::Type, std:vector<EventCallback>. The first one is useful if you want to only support one event type or to call all listeners for all events. The second is handy when you want to only notify listeners of certain types of event.
In any case if you are finding that you need to change your class code to accommodate minor changes to behaviour then you need some refactoring.
Hope that helped. :)
Need some help in sorting out design options for simulation framework in C++ (no C++11).
The user creates an "event dispatcher" and registers interest (using "watchers") in occurrence of "events". The dispatcher internally holds "event sources" which are used to detect event activation and manage notifications to watchers. There's a 1:1:1 mapping between watcher, event and event source classes.
I'd like to extend the system with
ability to register any sub-set of event watchers only in dispatcher (request unsupported notification will fail)
alternative implementations for event sources (e.g., one or multiple watchers per event)
extended event types (i.e., inheritance). Watcher and Source can handle sub-classes as if they're the base type, albeit with reduced functionality.
user defined event, event source and event watcher. For completely new events
I've considered using "event type identifiers" (either strings or Base.Derived notation), it works but doesn't feel correct (e.g., type safety relies on runtime integers, inheritance is limited, too many type casts...)
Would appreciate suggestions for code structure and mechanisms.
This is exactly the right situation to use dynamic_cast. It's only needed in one place.
All events inherit from a single base class, and all event handlers inherit from a (different) single base class. This makes all event sources and dispatchers uniform. Events are checked and filtered by the handlers in the base class.
This is a rough 10-minute sketch of what the overall structure could look like. There are no event sources in the sketch since it's not entirely clear to me what they should look like. I just fire events from main().