Yeah, this is why I haven’t pursued it too much yet. It feels like we’d just be pushing if
checks around from callbacks to inside ValueTree.
The main performance benefit would when you change a property in a deeply nested tree as you wouldn’t have to do this every time a property changed and hence call listeners for every parent:
template <typename Function>
void callListenersForAllParents (ValueTree::Listener* listenerToExclude, Function fn) const
{
for (auto* t = this; t != nullptr; t = t->parent)
t->callListeners (listenerToExclude, fn);
}
I think we can agree that there has to be at least one Identifier comparison whether that happens inside the ValueTree or in a callback. Space wise, a listener subclass (such as CachedValue) has to keep the Identifier it’s listening to anyway as it needs to compare to it so moving this inside the ValueTree wouldn’t add any more space if it means it could be left out of the listener class.
The most efficient way I can think of doing this right now is to have a separate set of listeners with a corresponding array of Identifiers they refer to e.g.
ListenerList<Listener> listeners;
Array<Identifier> identifiers;
(Keeping these in sync could be complex and verbose).
Then inside sendPropertyChangeMessage
you would have something like (using a C++20 init-for):
void sendPropertyChangeMessage (const Identifier& property, ValueTree::Listener* listenerToExclude = nullptr)
{
ValueTree tree (*this);
callListenersForAllParents (listenerToExclude, [&] (Listener& l) { l.valueTreePropertyChanged (tree, property); });
for (int i = 0; auto l : valueTreesWithListeners.propertyListeners.listeners.getListeners())
if (valueTreesWithListeners.propertyListeners.identifiers[i] == property)
l->propertyChangedCallback (tree, property);
}
You could then simply ignore the tree
and property
args if you’ve only registered a single listener.
I think this should be faster overall if you have lots of single property listeners on high-node objects but I’m not a huge fan of obfuscating the listener registration, callback semantics and internals of ValueTree.
I think I’d need some compelling data to show there is a genuine performance improvement for it to be worth it. But that does mean writing a lot of code in a certain style to push it to that demonstrable point in the first place…