addAndMakeVisible supporting unique_ptr

With ScopedPointer, it was possible to do this:

ScopedPointer<Knob> zoneLfo2RateKnob;

addAndMakeVisible(zoneLfo2RateKnob = new Knob());

Now, after migrating to use std::unique_ptr, this is not possible anymore:

std::unique_ptr<Knob> zoneLfo2RateKnob;

addAndMakeVisible(zoneLfo2RateKnob = std::make_unique<Knob>());

It would be really easy to add a specialised version of addAndMakeVisible (and addChildComponent) using std::to_address, juce could implement it for backwards compatibility pre c++20 (pretty trivial even without concepts).

1 Like

Nice idea, but I wonder if having a version of addAndMakeVisible() that takes a unique_ptr will cause confusion about whether or not the parent component takes ownership (which it doesn’t). You’d likely get people trying to do addAndMakeVisible(std::make_unique<Knob>());

As a workaround, you could just dereference the result of the assignment like so:

addAndMakeVisible(*(zoneLfo2RateKnob = std::make_unique<Knob>()));

Not as neat, but does the job.

2 Likes

I think you can solve these with a nice helper function depends on your use case.

For example:

namespace Utils
{
template <typename CompType, typename PointerType, typename... Args>
void createAdd(juce::Component* parent, PointerType& pointer, Args&&... args)
{
    pointer = std::make_unique<CompType>(std::forward<Args>(args)...);
    parent->addAndMakeVisible(*pointer);
}
} // namespace Utils

And use it like this:

using juce::TextButton;

struct Widget : juce::Component
{
    Widget()
    {
        Utils::createAdd<TextButton>(this, button, "Hello");
    }

    std::unique_ptr<TextButton> button;
};
2 Likes

Don’t think you need to specify the CompType if you write it like:

template <typename CompType, typename...Args>
void createAdd(juce::Component* parent, std::unique_ptr<CompType>& child, Args&&... args)
{
    // ...
}

createAdd(this, button, "Hello");
2 Likes

That’s true, but in my use cases I sometimes ‘createAdd’ in a polymorphic way, so it’s possible that button would be a std::unique_ptr<Component> and then created with the concrete type later - at least that’s a big reason of why I use std::unique_ptr on Components to begin with.

Beware, i’m not saying to add an overload that takes a unique_ptr. If you read well i’m talking about improving the raw pointer overload to take a generic pointer type that satisfies pointer_traits and for which to_address resolves to a valid pointer type (aka any pointer besides function pointers).

I already have it in my juce fork, i was just proposing to add it to main juce, it makes perfect sense there.

It’s rather unpleasant to look at. You first have a type, then this, then a unique_ptr passed by mutable reference (really dislike that way of working with smart pointers, passing them by mutable reference is against their purpose) that needs to match the declaration of the type, then it doesn’t work with other pointer types. It even hides the intent, i prefer see an explicit make_unique than a Utils::createAdd, and anybody else looking at the pull request will need to understand what that function does, by going to that implementation.

1 Like

This was just an example, you can change it so it takes anything with pointer semantics and just dereference it internally if you prefer that.

My point was that you don’t need to modify JUCE to add a utility function, since addAndMakeVisible is a public method, so passing the parent pointer is enough.

Also, just as a side note, but I’m usually hoping readers of my code are capable of reading the source code of functions that are called, otherwise there’s little chance I’ll let them get anywhere near the code. :slight_smile:

1 Like

I don’t want a utility function, i was just proposing of making addAndMakeVisible more flexible, and future proof, by taking advantage of standard constructs that are (or will be) in the language.

Is your code/fork public? I’m curious to see your approach.

Sure, this is a reduced example (i omit the implementation of to_address, when using c++ < than 20, but that’s just a juce_core/StandardSupport.h that is a future proof header enabling support for standard construct on lower standard versions, like std::make_unique on c++11, std::optional on c++14, std::to_address on c++17 and so on)

1 Like