How to pass a reference from a component to a component in another window in constructor?


#1

The application class has 2 windows (private class members):
std::unique_ptr<MainWindow> mainWindow;
std::unique_ptr<SubWindow> subWindow;
They get created here:
void initialise (const String&) override
{
mainWindow.reset(new MainWindow("control Surface"));
subWindow.reset (new SubWindow ("MIDI Interface", new SubContentComponent(mainWindow->GetMainContentPointer()), *this));
}
}
I can pass the pointer to the SubContentComponent constuctor (as shown) and store it in correctly typed pointer variable (private class member):
MainContentComponent* MCCptr;
When I try to use it:
MCCptr->some_data = 42;
I’m getting the compiler error:
E0245 a nonstatic member reference must be relative to a specific object
Thanks for any help.


#2

Solved:
MCCptr->some_data = 42;
was being done inside a function declared “static” this needn’t be the case so it works now!


#3

I would recommend writing a simplified version on Wandbox.org. usually doing that will pinpoint the error in your actual code:

https://wandbox.org/permlink/3qVxlkMJyxkhLGQg

#4

Interesting tool! Thanks for the tip.
Testing for linguistic mistakes in a stripped down environment makes sense…


#5

you’re welcome!


#6

Yes, like you found out, static methods have no access to non-static member variables.

Since you use std::unique_ptr, it’s best to save the ptr to the other window as a std::weak_ptr. This ptr will automatically be set to nullptr, when the other window is deleted.
Now, each time before you use the MCCptr (btw. common convention is to use lower case for variables and upper case for types, classes and structs), you can check, if it is not nullptr and you are safe.


#7

Please don’t use std::weak_ptr with std::unique_ptr! std::weak_ptr goes with std::shared_ptr.


#8

@McMartin While weak_ptr can be used to extend the lifetime of shared_ptr, it is not limited to be used with shared_ptr, afaik.
Can you explain, why you think, it shouldn’t be used together with unique_ptr?


#9

Does a SharedResourcePointer make sense in this case?


#10

Not really. The different types of smart pointers are designed to manage the ownership and therefore the lifetime of the pointee object.
A SharedResourcePointer doesn’t do that, it will create an object on the fly, if it is needed, and will share it, if it exists somewhere else.

For reference let me write up the different types:

  • std::unique_ptr: the ptr owns the object, and it will be deleted, once the ptr goes out of scope. ScopedPointer is the same thing and is now internally wrapping a std::unique_ptr, so no reason to use ScopedPointer in new code
  • std::shared_ptr: the ownership is shared amongst all shared_ptr that are pointing to the same object. Once the last shared_ptr goes out of scope, the object is deleted. ReferenceCountedObject::Ptr is the same thing, but differently implemented, ReferenceCountedObject is an intrusive reference count, vs std::shared_ptr is non-intrusive
  • std::weak_ptr: not owning the object at all, but the ptr is automatically set to nullptr, once the pointee is deleted. There are Component::SafePointer and WeakReference in juce, that do the same thing.
  • SharedResourcePointer: creating the object on the fly and sharing it, if it was already created somewhere else. Since you have no control, when it is created and when it isn’t, this is not well suited for persistency. I am not aware of a version in the STL.

A speciality of weak_ptr, that McMartin mentioned is, that to use a weak_ptr, you should create a shared_ptr from the weak_ptr, which will prevent the object from being deleted elsewhere (extending the lifetime).
However, when used with Components, they are bound to be created, destroyed, positioned and painted on the message thread. So it can’t be destroyed in the meantime. Hence it is sufficient to check at the beginning of the function.

Hope that helps.


#11

It is limited to use with just shared_ptr. It needs support from the smart pointer class which shared_ptr implements. But std::unique_ptr is just a very thin wrapper around a pointer, it doesn’t do any kind of reference or weak reference counting.


#12

Thanks for clearing that up, @Xenakios.

In that case, use Component::SafePointer @duggle, but definitely not a raw pointer, since you have the unique_ptr managing the lifetime.


#13

So I’ve declared the pointer thus:

private:
		Component::SafePointer< MainContentComponent> MainContentPtr;

and assign it in the constructor:

MainContentPtr = new MainContentComponent();
setContentOwned(MainContentPtr, true);

and define access function:

MainContentComponent*  GetMainContentPointer() {
			return MainContentPtr;
		}

The pointer is passed in to the other component’s constructor:

public:
	SubContentComponent(MainContentComponent* p)

and in the application class looks like this:

mainWindow.reset(new MainWindow("control Surface"));
		subWindow.reset (new SubWindow ("MIDI Interface", new SubContentComponent(mainWindow->GetMainContentPointer()), *this));

Everything seems to work o.k. but I’m not sure how the pointer type helps and if I’ve done it right?


#14

When you add later a function, that allows the mainWindow to close, like calling mainWindow.reset();, the pointer in your subWindow will still point to that address, and when you use it, the program will crash.

Now since you have the SafePointer, you can check first, if the mainWindow still exists:

class MainWindow : public Component
{
public:
    void foo() 
    {
        // ...
    }
};

class SubWindow : public Component
{
public:
    SubWindow (MainWindow* mw) : mainWindowPtr (mw) {}

    void bar()
    {
        if (mainWindowPtr != nullptr)
            mainWindowPtr->foo();
    }
private:
    Component::SafePointer<MainWindow> mainWindowPtr;
};