I started a simple project that uses an AudioGraph within an AudioProcessor. I was using a singleton to pass a ValueTree around between various nodes and other bits and pieces of the system. It works very well, until I move from standalone to hosted plugin, where multiple instances do not behave well. The reasons for this are clear.
I am now trying to rework the code, while still providing all children with simple access to the main data structure. But it so messy to pass a pointer to my data structure, which is a member of the AudioProcessor, to every other class in my project - they all need access to this data. Surely there is a simpler way? For example, I’m passing a pointer to the data from my AudioProcessor to my AudioGraph to my GraphPluginNode to my GraphPluginNodeEditor to my GraphPluginEditorSlider, etc. It just feels wrong.
Does anyone have any ideas on how to share this data around as some kind of semi-global, but not really, high level object. Is there a tried and tested pattern I should use here? Any advice is much appreciated.
So I bit the bullet and passed a reference to my top level data structure to all classes that need it. In comparison to the singleton approach is seems quite clunky. On the other hand it’s probably safer to do it this way. Pardon my ignorance, but I guess this is technically referred to as dependency injection?
I am afraid there is not really an alternative. However what you should try to achieve is that the data you pass around gets thinner each time it gets passed on, because your hierarchy should go from general to the specialized. E.g. the main panel might need to know about everything, but if it has a panel e.g. for a filter, you should aim to only pass on the references to the parameters that panel is concerned with.
Thanks @daniel, it’s always good to get the backing of an expert One of the reasons I need to pass this stuff around is because I decided to go nuclear on the undo/redo options. So my main data structure has an UndoManager that I need to give all the other classes access to. Thanks for the feedback, as always.
Could it be possible to generate an unique key for each instance of the plugin, and then fetch related data with that key in a global shared instance? It might be clunky to achieve thread safety and such, but after all it is just a map of few pointers.
Note that I’m not familiar with plugin dev thus it can be a totally dumb approach.
Note that if you can avoid that it is better. You could just pass the adress of the stuff into the constructor of your objects to store them as a (reference) (usually named owner) member. In that case life-time must be extra double checked. But IMHO most of time the overall main class remains from start to end, thus is easy to manage.
That’s what I’ve done. I didn’t think about assigning some kind of unique key to each instance, and using that with the singleton, but a quick read through previous posts about singletons in plugins and it seems it was destined to fail anyway. Although I did read about some interesting examples of using singletons to share data between plugin instances.
One approach I may consider, depending on the hierarchy involved, is to pass the pointer/reference only just down to the GraphPluginNode, and make it obtainable with a getter, let’s call it GraphPluginNode::getUndoManager().
Then, each GraphPluginNodeEditor can return the same UndoManager with a getter like this:
and then obtain the desired reference using editor->getUndoManager().
Bonus points: if each GraphPluginNode has a way to get to the Graph that contains it, that can be the ultimate holder of the reference to the UndoManager and expose it with a getter. I think you got the idea by now…
This sort of thing is pretty much the motivation for GUI architecture patterns like MVC and MVP (see GUI Architectures).
Your app’s data is stored in the model and passed to whatever controllers/presenters need it. As @daniel suggests, it’s a good idea to only pass the data each presenter needs, rather than having one big model.
I’m not a fan of passing around raw value trees, I prefer to wrap the tree in a Model interface with getters/setters for specific properties of the tree.
Singletons are just completely off the table for plugins, passing around data to classes that need it is totally fine - so long as you encapsulate that data in meaningful interfaces.