[promising code to begin with, but bad news ahead]
I built another simple class without the lambdas, which in theory can address all three feature requests at once (daisy-chaining, default undoManager, and or_else).
class SpeedyVT
{
public:
SpeedyVT(const juce::ValueTree& vt_) : vt(vt_) {}
SpeedyVT setProp(const juce::Identifier& id, const juce::var& v, juce::UndoManager* um = nullptr)
{
if (vt.isValid())
vt.setProperty(id, v, um);
return vt;
}
SpeedyVT addAndReturnChild(const juce::ValueTree& child, int i = -1, juce::UndoManager* um = nullptr)
{
if (vt.isValid())
vt.addChild(child, i, um);
return child;
}
SpeedyVT or_else(const juce::ValueTree& other) const
{
if (vt.isValid())
return vt;
else return other;
}
// add other methods as desired: getOrCreateChild(), getParent(), etc.
juce::ValueTree vt;
};
void doSomeSpeedyVT()
{
auto invalidVT = juce::ValueTree();
auto svt = SpeedyVT(invalidVT)
.or_else(juce::ValueTree("replacement"))
.setProp("p", 1)
.addAndReturnChild(juce::ValueTree("child"))
.setProp("x", 2);
DBG(svt.vt.getRoot().toXmlString());
// <child x="2"/> !?
}
But then I ran into a problem that I hadn’t anticipated. I expected the above code to print:
<replacement p="1">
<child x="2"/>
</replacement>
But instead “replacement” is gone and you get:
<child x="2"/>
The problem is that the “replacement” ValueTree
goes out of scope when the daisy-chained code ends, and this wipes the parent
pointer inside child
. This behavior was new to me, but it kind of makes sense that I’d never seen it before, since the existing API doesn’t really allow for a parent ValueTree
to go out of scope before a child does. (Maybe that’s why daisy-chaining is disallowed in the first place!)
Note that while I’m working with a wrapper class here, you’d face the same issue if you added these methods to the ValueTree
class itself, as far as I can tell.
I’m afraid that this problem puts the breaks on both of @ImJimmi 's ideas (or_else and daisy-chaining). It’s definitely a solvable problem, but not in a simple way.