SliderAttachment std::unique_ptr reset does not delete

In our plugin we need to dynamically change SliderAttachments.

Per the documentation, the recommended strategy for SliderAttachments is:

std::unique_ptr<SliderAttachment> sa;
sa.reset (new SliderAttachment (args));
// etc.

This was our initial strategy, with further calls to sa.reset (new SliderAttachment (args)) whenever we needed to change the attachment.

This behaves as expected on the first reassignment, but from that point on it’s as if the second attachment was never replaced at all. The slider displays all the signs of being still linked to the second attachment.

Here are some strategies that *do work for our purposes:

(1)

std::unique_ptr<SliderAttachment> sa;
sa.reset (new SliderAttachment (args));
sa.reset();
sa.reset (new SliderAttachment (args));
// etc.

(2)

SliderAttachment* sa;
sa = new SliderAttachment (args);
delete sa;
sa = new SliderAttachment (args);
// etc.

Have we totally misunderstood the delete behaviour of std::unique_ptrs? Thank you!

Calling reset on a std::unique_ptr will let it point to the new object you are supplying as argument (or nullptr, if none is given), which will result in deleting the object, it was pointing to before.
If you have the std::unique_ptr in a function scope, it will delete whatever it points to, when the function goes out of scope.

Can you post the code, where the std::unique_ptr<SliderAttachment> sa; is located?
Is it a class member or declared inside a method?

1 Like

Thanks Daniel, sure thing - this is the basic structure

MySlider.h:

class MySlider : public Slider
{
private:
    std::unique_ptr<SliderAttachment> sa;
};

MySlider.cpp:

void MySlider::MySlider()
{
    configureAttachment();
}

void MySlider::handleEvent()
{
    configureAttachment();
}

void MySlider::configureAttachment()
{
    // Set up some temporary variables
    sa.reset (new SliderAttachment (vtsToAttachTo, parameterID, *this));
}

One further bit of clarification:

This behaves as expected on the first reassignment, but from that point on it’s as if the second attachment was never replaced at all. The slider displays all the signs of being still linked to the second attachment.

What I mean in general terms is: The slider behaves as if it is linked to the most novel attachment, ie the one that was most recently created provided that an identical attachment had not previously been created.

Maybe I don’t understand it correctly, but that’s expected, isn’t it? Any new SliderAttachment replaces the previous one.

But on a separate note, I would avoid inheriting Slider for that purpose, but rather having it alongside the slider in the containing Component (or Editor).
Or put it in a struct:

struct AttachedSlider
{
    Slider slider;
    std::unique_ptr<AudioProcessorValueTreeState::SliderAttachment> attachment;
};

Or if you don’t need to change the attachment later a nice generic version:

struct AttachedSlider
{
    AttachedSlider (AudioProcessorValueTreeState& state, const String& paramID, Args&&... args)
    : slider (std::forward<Args>(args)...), attachment (state, paramID, slider)
    {
    }
    Slider slider;
    AudioProcessorValueTreeState::SliderAttachment attachment;
};
2 Likes

Sorry, it’s not easy to explain.

So as a timeline:

(1) Declare SliderAttachment.
(2) Assign SliderAttachment to new attachment. -> Behaves as if attached. Great.
(3) Reassign SliderAttachment via reset() to a different param. -> Behaves as if changed. Great.
(4) Reassign SliderAttachment via reset() to the same param as (2). -> Behaves as if still on (3).

That is indeed strange. I am sorry, I don’t have an idea, how that would happen.

No worries thanks anyway!

Being as we can solve the problem just by calling reset() before reset(new etc.) I think I’ll take that option for now, but I take your point about it possibly being better in the containing component.

I’ve been looking at using something like these AttachedSlider examples to clean up a mess of Editor constructor calls.

In the latter example, where you’re using the Args, that just lets you pass arguments to one of the Slider constructors, right? So you could thereby access any of the 3 Slider constructors from a single AttachedSlider constructor? (I’m not really up to speed on all the benefits of variadic templates, so if there’s more to it, I’m curious to know.)

I was unhappy about having a pair of juce::Slider and std::unique_ptr<juce::SliderAttachment> for all of my components as well, so I came up with this class which works perfectly fine for me:

/**
 * Adds support to link a component's value to
 * an AudioProcessorValueTreeState parameter.
 * @tparam ComponentClass The component to extend.
 * @tparam AttachmentClass The attachment class to instantiate.
 */
template<class ComponentClass, class AttachmentClass>
class alignas(ComponentClass) Attachable : public virtual ComponentClass {
public:
	/**
	 * Attaches the component's value to the given parameter.
	 * If already attached to a parameter, the existing attachment is lost.
	 * @param stateToControl The APVTS hosting the parameter to attach to.
	 * @param parameterID The ID of the parameter to attach to.
	 */
	void attachTo(juce::AudioProcessorValueTreeState &stateToControl, const juce::String &parameterID) {
		attachment = std::make_unique<AttachmentClass>(stateToControl, parameterID, *this);
	}

	AttachmentClass *getAttachment() {
		return attachment.get();
	}

private:
	std::unique_ptr<AttachmentClass> attachment;
};

You can use it like this:

typedef Attachable<juce::Slider, juce::AudioProcessorValueTreeState::SliderAttachment> AttachableSlider;
typedef Attachable<juce::Button, juce::AudioProcessorValueTreeState::ButtonAttachment> AttachableButton;
typedef Attachable<juce::ComboBox, juce::AudioProcessorValueTreeState::ComboBoxAttachment> AttachableComboBox;

AttachableSlider slider;
slider.attachTo(apvts, "paramId");

That is pretty slick! Nice work.

If you don’t need the component to be a virtual base class, you can remove the „virtual“ and „alignas“ parts - I need the former for some of my own components, and the latter works around a bug in clang with virtual inheritance.