Docs say AudioProcessorValueTreeState::getParameterAsValue can be used to control value, but it returns a copy

The documentations says:


* getParameterAsValue()

Value AudioProcessorValueTreeState::getParameterAsValue (StringRef parameterID) const

Returns a Value object that can be used to control a particular parameter.

However, when checking the source (and as is evident in the documentation segment listed above), I noticed that this method returns a copy of the Value, rather than a pointer or reference.

Is there a mistake in the documentation, or is it possibly loosely worded. I was about to use this until I noticed that it returns a copy, meaning I can’t actually control the Value using this method, but can only read it.

The method in the framework is:

Value AudioProcessorValueTreeState::getParameterAsValue (StringRef paramID) const
{
    if (auto* adapter = getParameterAdapter (paramID))
        if (adapter->tree.isValid())
            return adapter->tree.getPropertyAsValue (valuePropertyID, undoManager);

    return {};
}

I presume it’s loosely worded, so perhaps it could be clarified!

juce::Value is effetively a reference to a property in a ValueTree, so if you take a copy you’re still refering to the same property.

Represents a shared variant value.

A Value object contains a reference to a var object, and can get and set its value.

https://docs.juce.com/master/classValue.html#details

1 Like

Amazing! You’re totally right. Although I’m a little skittish about using persistent reference values, due to the hidden side of what the framework might do, it totally does mean that.

Source:

Value& operator= (const var& newValue);

(I might still go straight for the Listener method, as it seems more reliable.)

It’s not “hidden” - it’s well documented that that’s what a Value object does. Similar to how a ValueTree is just a wrapper around a reference to some shared data so a copy of a value-tree still refers to the same data as the original.

If that’s not the behaviour you want then you should use a different API. You can easily get a copy of the value from a Value object using Value::getValue() - maybe that’s more what you’re after?

1 Like

I presume Value::getValue() performs a similar function to the overloaded equality operator.

I think it’s just a personal preference regarding coding style, but I tend to avoid using references for anything that may require longevity, as while it’s not technically “hidden,” it does require searching through framework code if something is amiss. References give no assurance to longevity vs. the use of pointers, and an anomalous address change can break a program in a frustrating way. So while it’s just a personal preference, I do think it’s good practice to avoid using references for anything requiring longevity.

Thanks a lot! Those are certainly viable options, however, the

virtual void Value::Listener::valueChanged(Value& value)

method helps me sleep at night :slight_smile:

No, one returns a copy of the referenced var while the other assigns a new value to the referenced var - the var you get from Value::getValue() is in no way “linked” to the data being refered to by the Value object.

The first line of the docs for Value says that it represents a shared variant value. It goes on to explain that the shared value is reference-counted meaning for as long as you keep your Value object around, the referenced var will remain. Value is similar to a std::shared_ptr<juce::var> in that way.

The docs do a pretty good job of explaining what’s going on - there’s no need to look at the actual code…

It appears you are correct.

The Value class is a wrapper around a shared, reference-counted underlying data object - this means that multiple Value objects can all refer to the same piece of data, allowing all of them to be notified when any of them changes it.

Although, when looking through:

and

I do not see where it does that.

I am willing to trust the documentation to some degree, certainly. It does definitely say that, so perhaps I was mistaken, thinking it was returning a simple reference.

I suppose I’ll try it out and see! Although I wish I could find where it does that in the source code. I looked through it and although it does use a ReferenceCountedObject, as I found, I don’t see in the actual class where it occurs.

Basically, value which is the property contained in var is this:

    union ValueUnion
    {
        int intValue;
        int64 int64Value;
        bool boolValue;
        double doubleValue;
        char stringValue[sizeof (String)];
        ReferenceCountedObject* objectValue;
        MemoryBlock* binaryValue;
        NativeFunction* methodValue;
    };

    ...
    ValueUnion value;

Which apparently may be a ReferenceCountedObject, although not necessarily as it seems… The documentation does say otherwise is a worthy point; it just bugs me a little for it not to be obvious in the source.

You’re looking at juce::var, not juce::Value. The ref counted object is near the end of juce_Value.h:

ReferenceCountedObjectPtr<ValueSource> value;

More important is when this is intended to be used. getParameterAsValue calls getPropertyAsValue on the internal ValueTree for the parameter in APVTS. This makes a connection between the ValueTree and the Value, so whenever one changes, the other will change too. You can add listeners to the Value and get informed of those changes. The connection is asynchronous, so it’s not intended for audio thread use -there’s getRawParameterValue for that. This functionality is closer to what a parameter attachment does. All widgets have a function, like Slider::getValueObject or Button::getToggleStateValue, that allows their internal Value object to be connected to another via Value::referTo. The parameter attachment classes are implemented differently though, and they also deal with gestures. Moreover, these Value connections are broken by APVTS::replaceState. I wouldn’t recommend using them for APVTS. They’re useful for ValueTrees in general, to link them to different parts of the UI.

3 Likes