In this commit, most of the methods of juce::DynamicObject that were previously virtual have been made non-virtual, adding unnecessary breaking changes that, in some cases, have no viable workaround.
In particular, there’s no viable workaround for a custom implementation of setProperty() without any clients of the DynamicObject derivative needing to know to dynamic_cast to the derivative in order to set the property.
JIVE uses a derivative of juce::DynamicObject called jive::Object, which overrides the setProperty() method in order to register the parent hierarchy of objects (needed when retreiving fallback properties), and to call listeners (something that’s missing from the base class). JIVE/jive_core/values/jive_Object.cpp at d93bace7e1677e2964aa1e3ef4b1577181f35c4e · ImJimmi/JIVE · GitHub. The intension here is that a client should be able to use the juce::DynamicObject* retrived from a juce::var without needing to know that it’s actually a jive::Object under the hood.
Although I understand the intension to ensure derivatives of juce::DynamicObject do not “break” anything - I feel this breaks the original contract of the juce::DynamicObject class which states the class “represents a dynamically implemented object” and explicitly says it “could be used for other purposes” besides wrapping scripting language objects - implying that adding custom behaviour to that object is expected.
Could I request this change be reverted, and for the sake of the new QuickJS-based JS engine, that a new derivative of juce::DynamicObject be used that enforces those more strict behaviours, leaving the original more open for general-purpose uses?
Alternatively, could you implement the necessery listener mechanism and getParent(), getRoot(), etc. features that would allow me to remove jive::Object altogether?
FWIW, adding the listener mechanism and parent hierarchy to juce::DynamicObject would make it pretty much just as viable as juce::ValueTree for use as a general-purpose app-state container, with the advantage of being serialisable to JSON rather than XML. I.e. everything that Dave says about value-trees in his ADC talk could also be said about dynamic objects if those few small features were added…
Can’t you still do what you need by adding/removing properties?
When we updated to J8 and found these issues, we just added switched to using setMethod and passed it a lambda that forwarded on to our old dynamic implementation.
Now that I look at it though, it does seem much cleaner to do this through composition rather than inheritance.
So the problem is that I don’t want users of the API to have to know the underlying type - they should be able to do everything through juce::ValueTree, juce::var, juce::DynamicObject, etc. without needing to know to dynamic_cast to my additional type.
The only way to do this is to override setProperty() to add the additional listener mechanism that’s needed to pick up changes in the object’s properties.
// MyAPI.h
void setProperty (const juce::Identifier& id,
const juce::var& value) override
{
if (getProperties().set (id, value))
listeners.call (&Listener::valueChanged, this, id);
}
// Client code:
// Previously, this worked fine, calling the listeners to inform about the change:
#include <juce_data_structures/juce_data_structures.h>
someValueTree["some-object"].getDynamicObject()->setProperty("foo", 123);
// Now, the client is forced to cast the pointer first:
#include <MyAPI/MyAPI.h>
dynamic_cast<MyAPI::Object*>(someValueTree["some-object"].getDynamicObject())->setProperty("foo", 123);
The listeners in this case could be internal to the API (to pick up the clients changes), or could be in the clients code too.
For a real world example, see here where I had to make these changes in the JIVE test runner in order for the listeners to be properly invoked:
I also need to be able to find an object’s parent (if any), so in setProperty() I also check if the incoming value is also an object and set its parent property to this.
That would get my vote, maybe with a virtual methodChanged const Identifier& methodName) as well but tbh, I’m not a massive fan of the separation of property/method in DynamicObject. They’re two different ways of saying the same thing.
Thanks for your patience. We’ve added a virtual function to DynamicObject that is called when a property is modified. Please try it out and let us know if you run into any problems.