Problem Getting Processor Reference from Struct methods

gui

#1

Hi all,

I trying to make a simple GUI using tabbed panes. I’ve made a struct containing some Sliders and added to a specific tab. This works fine but i’m confused as to reference the processor from the action listener when a slider is modified.

Header File:

class GuiWorkAudioProcessorEditor  : public AudioProcessorEditor, 
								     private Slider::Listener

{

public:
  GuiWorkAudioProcessorEditor (GuiWorkAudioProcessor&);
~GuiWorkAudioProcessorEditor();

//==============================================================================
void paint (Graphics&) override;
void resized() override;

private:

void sliderValueChanged(Slider * slider) override;

// This reference is provided as a quick way for your editor to
// access the processor object that created it.
GuiWorkAudioProcessor& processor;
TabbedComponent tab;
Slider midiVolume;
Slider altSlider;

struct SliderHolder : public Component , private Slider::Listener
{
	
	SliderHolder()
	{		
		addAndMakeVisible(slider1);
	    addAndMakeVisible(slider2);
		slider1.setName("slider1");
		slider2.setName("slider2");
		slider1.addListener(this);
		slider2.addListener(this);
	}

	void sliderValueChanged(Slider * slider) override;

	void resized() override
	{
		
		slider1.setBounds(30, 50,200, 40);		
		slider2.centreWithSize(100, 100);
	}
	
	Slider slider1;
	Slider slider2;

};

In the .cpp file when i try get some info from the processing thread i get this error msg:
“a nonstatic member reference must be relative to a specific object”

CPP File:

void GuiWorkAudioProcessorEditor::SliderHolder::sliderValueChanged(Slider * slider)
{

    if (slider->getName() == "slider1")
    {
	     processor.getSampleRate(); //gives error
    }
    else if (slider->getName() == "slider2")
    {

    }


}

Any ideas of how to solve this or why it’s throwing this error?


#2

It doesn’t matter that your SliderHolder is a class declared within the GuiWorkAudioProcessorEditor class. You still need to explicitly pass a pointer or reference of the processor into the SliderHolder instances. (Like is done with the GuiWorkAudioProcessorEditor where it takes and stores a reference of the GuiWorkAudioProcessor.)


#3

Hi thank you for the swift reply. So is it as simple as declaring:

 GuiWorkAudioProcessor processingRef;

in the struct ? I have tested this and it seems to work but i’m kind of unsure as to why…The way you phrased it, it seemed i would have to pass a reference of the original Processing reference (that is “processor”) to the struct? This stuff is quite confusing for me so any help is always appreciated! :slight_smile:


#4

No, that’s not going to work. The processor must be a pointer or reference in the SliderHolder, not a whole new instance of the processor. (If it actually does work at the moment during runtime, I suspect there’s some additional error in your code that makes the processor instances not be real distinct instances.)

GuiWorkAudioProcessor processingRef; // New instance that’s not going to do you any good in the SliderHolder.
GuiWorkAudioProcessor& processingRef; // Reference
GuiWorkAudioProcessor* processingRef; // Pointer


#5

Hmm i did originally try just declaring a refernce to the processor but it gave me an access error … i will continue working on it tomorrow morning , maybe I’m doing something silly. The code is using a simple Juce GUI tutorial as its base so i think the rest of the code is ok.

Thank you for the help !


#6

Okay so here’s what i came up with…
Struct Header:

	struct SliderHolder : public Component , private Slider::Listener
{
	
	SliderHolder(GuiWorkAudioProcessor &p)
	{		
		processingRef = &p;
		addAndMakeVisible(slider1);
	    addAndMakeVisible(slider2);
		slider1.setName("slider1");
		slider2.setName("slider2");
		slider1.addListener(this);
		slider2.addListener(this);
	}

	void sliderValueChanged(Slider * slider) override;

	
	void resized() override
	{
		
		slider1.setBounds(30, 50,200, 40);		
		slider2.centreWithSize(100, 100);
	}
	
	Slider slider1;
	Slider slider2;
    GuiWorkAudioProcessor *processingRef;
	

};

In the .cpp file the processor address is sent via struct constructor:

tab.addTab("Tab 0", Colours::antiquewhite, new SliderHolder(processor)  , true);

I can then access the processors methods via:

void GuiWorkAudioProcessorEditor::SliderHolder::sliderValueChanged(Slider * slider)
{

if (slider->getName() == "slider1")
{
	float i = processingRef->getStuff();
	 i++;
}

This seems to work ok… but i’m concerned that i may be over complicating things, and don’t want to continue if the code at this stage is not proper. I could not for the life of me figure out how to do this with just Declaring a new reference to the processor such as “GuiWorkAudioProcessor &processingRef;” … So many errors popped up when i try to initialize this variable in the struct constructor…

Any suggestions on this would be most helpful :slight_smile:


#7

A reference class member needs to be initialized in the constructor initializer list. You can’t do = etc in the constructor body itself.

SliderHolder(GuiWorkAudioProcessor &p) : processingRef(p)
{ // constructor body

Having to do the reference initing in the constructor initializer list sometimes makes things tricky, so depending on the situation just using a pointer can be easier to deal with.


#8

Ah thanks for reply ! just tried that there and it works perfectly.
So is the way i have gone about it using a pointer also just as valid ?


#9

Just as valid, but using pointers when something will always need a valid object reference in order to work is considered suspicious coding style. (A pointer can easily end up being a null pointer, it’s more difficult to end up with a null reference by mistake.)


#10

Ah ok i see… Thank you so much for your help! Very much appreciated!


#11

BUT a reference is just as fragile as a pointer, if the object it references is freed, you are still doomed! And with references you cannot check for nullptr.

My personal rule of thumb: I use references as back links, like the ProcessorEditor cannot exist without the Processor, hence it is safe to use a reference.

But if the ownership is independent, I use smart pointers like WeakReference, they auto null, if the pointee is deleted and you can just use them with checking for not nullptr.


#12

Let’s not scare people possibly new to all this with all the details… :wink:


#13

Sorry, didn’t mean to. :wink:
It just makes sense to know which one to choose…

I’ve seen people lately assuming, that they are safe if they use a reference… But the problem of dangling pointers is not out of the world…

But I also want to advocate for smart pointers, WeakReference is so useful, and so is ReferenceCountedObject.

And if you want to go STL there is
std::unique_ptr for ScopedPointer
std::shared_ptr for ReferenceCountedObject and
std::weak_ptr for WeakReference (same as Component::SafePointer I guess)

People complain that proper memory management is hard on C++ and that java garbage collection is so handy, and ObjectiveC with ARC is so advanced. But they forget, it is all possible in C++ too nowadays. It is just not mandatory…

So everybody use them and enjoy! :slight_smile: