Some sort of GC problem?

Hey everyone,

Sorry for asking for help for something that’s probably an id10t error, but I’m stuck and I have been for hours :frowning:

Short version, I want to get a string from a class I’ve instantiated in PluginEditor but it’s causing a crash in my daw and when I check the crash log it says that I tried to access an inaccessible object.

I wrote a small test class to reduce the code down to one thing, here it is:

TestObject.h

// Conductor : Copyright ioFlow Studios 2021 //

#pragma once

// System includes
#include <vector>

// Juce includes
#include <JuceHeader.h>

class TestObject
{
public:
	String testString;
	int testInt;

	TestObject();
	~TestObject();

	String getTestString();
	int getTestInt();
};

TestObject.cpp

// Conductor : Copyright ioFlow Studios 2021 //

#include "TestObject.h" 

TestObject::TestObject()
{
	testString = "fml";
	testInt = 1;
};

TestObject::~TestObject() {};

String TestObject::getTestString()
{
	return testString;
}

int TestObject::getTestInt()
{
	return testInt;
}

I’ve included TestObject.h at the top of PluginEditor.h and have declared it as a member variable down in the class, i.e.:

	TestObject test;

At the bottom of the constructor in PluginEditor.cpp I have:

	test = TestObject();

Then I have a test method called songTest:

String ConductorAudioProcessorEditor::songTest()
{
	return test.getTestString();
}

But when I call songTest I get this:
image

And when I check the memory dump it says this:

And now, I just don’t know what to do :frowning:

Any advice/tips?

You’re explicitly calling the constructor again of an automatically-constructed member variable.

When you just write

TestObject test;

that object’s constructor will be called.

Try getting rid of the explicit reassignment and see if that fixes it.

You’re right I only added that in desperation :slight_smile:

I removed it, unfortunately it hasn’t made any difference…

In Unreal Engine we have to put macros over our class properties to mark as not to be GC’d, is it something like that?

Ok. So I’m at the point where what I really want to do is have a big angry rant about why the hell is something this simple not working… of course, as we all know, as soon as you do that, someone instantly pops up and says “uh, why haven’t done such and such super basic thing” and then you look like an idiot xP

Nevertheless, I’m on a short deadline and I really need to get this code going. At the moment my best plan is to go back to Juce 5.4.4 where this code is originally from because at least there it worked.

Would be super awesome if someone could suggest something that would let it work in 6.0.8 (is that even the latest version anymore?)

No, there is no garbage collection in C++.

This doesn’t really seem like it has anything to do with Juce, this is a more basic and low-level C++ problem…

Can you attach a debugger to the plugin and step in to the getTestString() function? You’re right that this is atypical behavior for a C++ program. Are you doing anything else with this TestObject object before calling getTestString on it?

I wasn’t aware you could attach a debugger to a plugin.

I normally build the VST, get an error that VS couldn’t run it (cos, it’s a VST), then manually copy it from the folder where VS put it to program files/common files/VST3, then run studio one and try and use the plugin from there…

Sounds terrible but it’s only a 20 second turn around for me so I’ve just been putting up with it.

Right, a plugin isn’t executable on its own, but you can attach a debugger to the host that’s running your plugin.

Alternatively, you can just build the Standalone format of your plugin, and then attach a debugger directly to it from within your IDE.

Also, FYI: Juce has an option to automatically copy the finished binaries to the default system directories once the build is complete. In the Projucer I think it’s called “Enable plugin copy step”.

Aight, I think I’ve figured out what was causing the problem.

I have a hierarchy of components, the building of which gets triggered from inside the PluginEditors constructor:

ConductorAudioProcessorEditor::ConductorAudioProcessorEditor (ConductorAudioProcessor& p)
    : AudioProcessorEditor (&p), audioProcessor (p)
{
	//// ---- setup ---- ////

	getImages();

	addAndMakeVisible(body);
	addAndMakeVisible(header);
	addAndMakeVisible(footer);

	body.linkEditor(this);
	header.linkEditor(this);
	footer.linkEditor(this);
...

After I make them I pass a reference to the PluginEditor to them… but I’m doing this in the constructor, so it hasn’t been “made properly” yet so the reference is a dud [facepalm]

Actually, just to rob me of feeling like I’m completely back in control, it’s a bit messier than that…

the ed ref passed through to body, header and footer above actually does work (how I don’t even know). But when they try to pass that reference in the constructors of body, header and footer to their child components, thats the one that doesn’t work (pulls hair).

Anyways, I just tell the page components (who are children of the body component) to update their ed ref with the ed ref stored in their parent (body) and as long as I do that outside of the constructor, it works.

How are body, header and footer defined? How are they created/initialized? Usually, you use a pointer for those, not the instance itself, like this:

myKnob.reset (new juce::Slider ("myKnob"));
addAndMakeVisible (myKnob.get());

where the knob is defined in the class as:

std::unique_ptr<juce::Slider> MyKnob;

That’s what Projucer writes out when you drop a Slider onto a Component.

Perhaps your body/header/footer are not initialized correctly?

Why do components need to be on the heap? Why can’t they just be regular stack members?

They don’t have to be, but how are those ones defined and initialized? Do they have a size? Are they derived classes that require some type of initialization? An unnamed Component without anything set would be pretty useless and unusual. Also, when is songTest() being called? Is everything initialized by then?

1 Like

If you’re having a component as a non-pointer member instead of as a unique_ptr its still going to be on the heap if the holding component is on the heap. Which it most likely is. But instead of being at a random new location on the heap it’s going to be located within the memory area of the holding component. The stack is where variables local to the function go unless the compiler decides to put them into registers.

Sorry, could help myself to point this out :slight_smile:

I also don’t like when class members that are object instances are referred to as “stack allocated”. Like you pointed out, whether the subobjects (which I believe is the word used by the language standard for such member objects) really are on the stack or heap depends on where the “host” or “parent” or “owner” object itself lives.

class MyComponent : public Component
{
public:
  // The following by itself doesn't tell anything about where slider1 instance lives in memory
  // at runtime. It depends on whether a MyComponent instance has been created
  // on the stack or on the heap.
  Slider slider1;
  // Following strongly implies the slider object will be on the heap once the pointer is 
  // initialized from an allocation, but it could for example also be made 
  // to point to the slider1 object declared above.
  Slider* slider2 = nullptr;
};

There is nothing wrong with passing the “this” pointer from a constructor. What you do not want to do is call any virtual functions of the class (or its class hierarchy) from its constructor. Otherwise, using “this” is fine.

By the way, you keep referring to the editor “reference”, but it’s a pointer, not a reference. They are different. You’re passing a “pointer to the editor” (a ConductorAudioProcessorEditor*), not a reference (a ConductorAudioProcessorEditor&). It’s important to keep those two terms distinct.

Hey Everyone,

Wow, thread really took off over night eh?

Thanks for all the advice, yah I had my terminology a bit mangled =P

So what I’m hearing above is that in principle I should be able to pass a pointer to the editor down through my component hierarchy from its constructor. Unfortunately, in my case, this just doesn’t work reliably.

To be more specific, in the editors constructor, I can pass a pointer to it to child components and they can use it fine in their constructor. But if the child components tried to send the same pointer to their child components, when those grand children components tried to use the pointer the plugin crashed.

Anyways I’ve managed to stabilize the app using an architecture I actually fell back on last time, it’s just been so long that I forgot the finer details and had to figure it out again the hard way. Can’t guarantee it won’t push your buttons either, but for now, it’s what’s letting me just get on with it.

I use an abstract class called LayoutComponent to handle passing the pointer down (and passing input back up):

LayoutComponent.h

// Conductor : Copyright ioFlow Studios 2021 //

#pragma once

// Forward declarations
class ConductorAudioProcessorEditor;

// Juce includes
#include <JuceHeader.h>

enum class eLayoutParentType
{
    Editor,
    Layout
};

class LayoutComponent  : public juce::Component
{
public:
    ////////// ---------- Properties ---------- //////////


    // anti juce torture mechanism
    bool initEditor;
    bool initParent;

    ConductorAudioProcessorEditor* editor;
    LayoutComponent* parent;


    ////////// ---------- Methods ---------- //////////


    LayoutComponent();
    ~LayoutComponent() override;

    void init(ConductorAudioProcessorEditor* argEditor, LayoutComponent* argParent);
    virtual void initChildren() = 0;

    virtual void input(String argComponentID, String argEventType, String argArgument) = 0;

    virtual void paint (juce::Graphics&) = 0;
    virtual void resized() = 0;

private:
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LayoutComponent)
};

layoutComponent.cpp

// Conductor : Copyright ioFlow Studios 2021 //

#include <JuceHeader.h>
#include "LayoutComponent.h"

// Forward declaration includes
#include "PluginEditor.h"

///////////////////
//               //
//  constructor  //
//               //
///////////////////

LayoutComponent::LayoutComponent()
{
	initEditor = false;
	initParent = false;
}
LayoutComponent::~LayoutComponent(){}

////////////
//        //
//  init  //
//        //
////////////

void LayoutComponent::init(ConductorAudioProcessorEditor* argEditor, LayoutComponent* argParent)
{
	editor = argEditor;
	initEditor = true;

	parent = argParent;
	initParent = true;

	initChildren();
}

In my PluginEditors constructor I add the three top level LayoutComponents as per usual:

ConductorAudioProcessorEditor::ConductorAudioProcessorEditor (ConductorAudioProcessor& p)
    : AudioProcessorEditor (&p), audioProcessor (p)
{
	// add app layout components
	addAndMakeVisible(header);
	addAndMakeVisible(body);
	addAndMakeVisible(footer);

They in turn add their child components in their constructor, but do NOTHING else.

Now my hierarchy is in place but completely inert and each component has an init method buried in it.

Back in the PluginEditor I use a timerCallback (which I needed anyway for tracking time from the daw) to only once trigger the init in the top level LayoutComponents AFTER the constructors have all done their thing. This cascades down through the hierarchy, now everyone has a pointer to PluginEditor and no one crashes :slight_smile:

/////////////////////
//                 //
//  timerCallBack  //
//                 //
/////////////////////

void ConductorAudioProcessorEditor::timerCallback()
{
	// cascade editor references through the component hierarchy AFTER construction
	if (!edInit)
	{
		debug("starting init cascade");

		header.init(this, nullptr);
		body.init(this, nullptr);
		footer.init(this, nullptr);

		edInit = true;
	}

So it works, and I’m free and clear to do what I need to now, but I figured I’d post it all here for wiser C++ peoples than me to pick apart if it’s awful. If it’s not awful, maybe it might save someone else going through the grief I did getting it going.

There are two hierarchies involved:

  • the hierarchy of Component: by calling addAndMakeVisible you make the component a child of where you called addAndMakeVisible from
    N.B. addAndMakeVisible is a shorthand for addChildComponent(child) and setVisible(true)
  • the hierarchy of ownership: where the sub components are owned, like your header, body and footer

I would advise only to deviate one from the other if it is really necessary, i.e. preferably own the components where they are added via addAndMakeVisible.

In the constructor of a component you cannot call getParentComp[onent() successfully, because only after the construction has finished the parent will call addChildComponent, i.e. during construction it doesn’t know its parents (I assume this is why your timer version works and the previous attempts failed).

But if you don’t use getParentComponent() but you pass down a reference to the owner, that is no problem. This reference is always valid.

Here an example how I would skin the cat (sorry meow :cat: ):

class HeaderComponent : public juce::Component
{
public:
    HeaderComponent::HeaderComponent (ConductorAudioProcessorEditor& e) : editor (e) {}
private:
    ConductorAudioProcessorEditor& editor;
};

class LayoutComponent : public juce::Component
{
public:
    LayoutComponent (ConductorAudioProcessorEditor& e) : editor (e) { /* the other stuff */ }
private:
    ConductorAudioProcessorEditor& editor;
    HeaderComponent header { editor };  // make sure to get the initialisation order right!
};

// cpp
LayoutComponent::LayoutComponent (ConductorAudioProcessorEditor& e) 
: editor (e) 
{ /* the other stuff */ }

// and in the editor:
private:
    LayoutComponent layout { *this };

Hope that helps

3 Likes