[Interest] Using a single shared Object as a message bus throughout the application?

Jérôme Godbout godboutj at amotus.ca
Wed Jun 9 22:12:59 CEST 2021


It seems like you need an event bus (where the sender matter or not, could simply be the event sender id). You then could register to listen to even from a particular source only or any source depending on your requirement. The any sender is not really possible with the Signal Slot unless you have a Signal forwarder central unit that would do just like the event bus. But there is many event bus implementation that doesn’t require Qt that could suit your need, you could translate your signal to event with a simple lambda on the event bus.

You should have a bus provider that get or create (goc) the bus with specific identifier (enum, name, etc). So you could have multiple BUS inside your application and different privilege bus (like DBUS user and system for example). You could have an action bus where action could be requested to perform actions (could have a flag into the actions to tell if it was handled or not when completed) and an event bus that reflect the actual events of what did happened into the system.

All your conditions then become event listener that trigger actions. For example:
system needs to do X when Y happens and if conditions A B and C are met

You make an event listener that wait until A, B and C event happen on the BUS (can also request the initial state to begin with) and trigger action X and action Y directly or can even simply request and event/action DoX or DoY which could be process by something else. Decoupling your application by a lot that way.



From: Interest <interest-bounces at qt-project.org> on behalf of Tarre Tarkas via Interest <interest at qt-project.org>
Date: Wednesday, June 9, 2021 at 3:12 PM
To: interest at qt-project.org <interest at qt-project.org>
Subject: [Interest] Using a single shared Object as a message bus throughout the application?
I'm working on a daemon that needs to stay flexible/tweakable. I get
lots of random requirements in the form of "system needs to do X when Y
happens and if conditions A B and C are met".

Qt's doc and examples seems to exclusively encourage putting signals in
each component's class definitions. So that's what I did previously.
However I recently found out that you can actually emit a signal in a
different QObject. I always thought the "emit MySignal()" to be like a
private call, but no, signals are public functions, you can do
"someobj->MySignal()", or better yet "Q_EMIT someobj->MySignal()" to
make it stand out more.

I'd like to hear your thoughts on an application architecture style
where you use a single shared object as a public message bus throughout
the entire app. So all the components of the app have a reference to
this QObject, all the "public" signals used by the application are
defined in this QObject instead of being defined in the object that
emits them.

I've written a very simple example app to showcase this:
https://gitlab.com/tarre/message-bus-test

Notice how the features in the 2nd and 3rd commits were bolted on to the
existing architecture in a very straight-forward way, without having to
modify unrelated source files.

Pros:
1) Simpler application architecture. Since there's just a single source
of major events, a new developer doesn't need to understand (as much)
where their code fits with all the rest. They can add certain features
by just writing signal handlers for signals coming from that single
event bus. Also, they can discover how components interact with each
other by searching for the usage of each signal from that single file.
When writing new components, they don't need to think as much where to
place it, since there's already this structure in place.
2) Signals emitted by objects created at a low "depth" (for example
Hardware -> SensorManager -> TemperatureSensor) can be used by
components anywhere in the app in a simple clean way, without writing
repetitive boilerplate code just to be able to connect a signal to a
slot
3) If there's a refactoring somewhere, if a different component is now
responsible for emitting SignalX, consumers don't have to change
anything. Previously the only way to do this would be to use interfaces,
which is boilerplatey.

Cons:
1) Not Qt-ic (whatever the Qt equivalent of "pythonic" is)
2) You lose the ability to use sender() to get a reference to the
QObject* that did the real emit. With this, the sender is always the
message bus object. (Correct me if I'm wrong. If there's a way to get
the sender without adding it as an argument in every signal, that would
be wonderful.)

What do you think?

Tarre
_______________________________________________
Interest mailing list
Interest at qt-project.org
https://lists.qt-project.org/listinfo/interest
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.qt-project.org/pipermail/interest/attachments/20210609/04748b7b/attachment.html>


More information about the Interest mailing list