Singleton Class and "Leaked objects detected"

Hi I created this class to manage the structure of my application (obviously this is an extract of my class, for example I converted in string a lot of identifiers and I remove a lot of methods to simplify the code to post here):

enum ChType {audio, midi, input, send, cue};

class RootEngine {
public:
    
    UndoManager undoManager{};
    
    ValueTree rootVt {"root"};
    
    ValueTree channelsVt {"channels"};
    ValueTree inputsVt {"inputs"};
    ValueTree sendsVt {"sends"};
    ValueTree cuesVt {"cues"};
    ValueTree mainOutVt {"mainOut"};
    ValueTree clockVt {"clock"};
    
    static RootEngine* sharedInstance()
    {
        static RootEngine rootEngine;
        return &rootEngine;
    }
    
    
    void addChannel(String chName, int chIndex, ChType type)
    {
        undoManager.beginNewTransaction();
        
        ValueTree channelVt;
        
        switch (type) {
            case audio:
                channelVt = ValueTree{"aucioCh"};
                channelsVt.addChild(channelVt, -1, &undoManager);
                break;
                
            case midi:
                channelVt = ValueTree{"instrCh"};
                channelsVt.addChild(channelVt, -1, &undoManager);
                break;
                
            case input:
                channelVt = ValueTree{"inputCh"};
                inputsVt.addChild(channelVt, -1, &undoManager);
                break;
                
            case send:
                channelVt = ValueTree{"sendCh"};
                sendsVt.addChild(channelVt, -1, &undoManager);
                break;
                
            case cue:
                channelVt = ValueTree{"cueCh"};
                cuesVt.addChild(channelVt, -1, &undoManager);
                break;
                
        }
    }
    
    ScopedPointer<XmlElement> createRootXml()
    {
        ScopedPointer<XmlElement> xml = rootVt.createXml();
        return xml;
    }
    
    ~RootEngine() {}
    
private:
    
    RootEngine()
    {
        
        rootVt.addChild(channelsVt, -1, nullptr);
        rootVt.addChild(inputsVt, -1, nullptr);
        rootVt.addChild(sendsVt, -1, nullptr);
        rootVt.addChild(cuesVt, -1, nullptr);
        rootVt.addChild(mainOutVt, -1, nullptr);
        rootVt.addChild(clockVt, -1, nullptr);
        
        rootVt.getChildWithName("mainOut").setProperty("volume", 0, nullptr);
        rootVt.getChildWithName("mainOut").setProperty("panpot", 0, nullptr);
        rootVt.getChildWithName("mainOut").setProperty("metroOn", false, nullptr);
        rootVt.getChildWithName("mainOut").setProperty("metroVolume", 0, nullptr);
        rootVt.getChildWithName("mainOut").setProperty("metroPanpot", 0, nullptr);
        rootVt.getChildWithName("clock").setProperty("bpm", 120, nullptr);
        rootVt.getChildWithName("clock").setProperty("movements", 4, nullptr);
    }
    
};

And in my MainComponent.cpp I created some buttons with this onClick method implementations:

addMidi.onClick = [this]()
{
    DBG("Inside addMidi:");
    RootEngine::sharedInstance()->addChannel("midiTrack" + String(++i), i, ChType::midi);
    
};
addAudio.onClick = [this]()
{
    DBG("Inside addAudio:");
    RootEngine::sharedInstance()->addChannel("audioTrack" + String(++i), i, ChType::audio);
};
addInput.onClick = [this]()
{
    DBG("Inside addInput:");
    RootEngine::sharedInstance()->addChannel("inputTrack" + String(++i), i, ChType::input);

};
addSend.onClick = [this]()
{
    DBG("Inside addInput:");
    RootEngine::sharedInstance()->addChannel("sendTrack" + String(++i), i, ChType::send);
};
addCue.onClick = [this]()
{
    DBG("Inside addInput:");
    RootEngine::sharedInstance()->addChannel("cueTrack" + String(++i), i, ChType::cue);
};
tb2.onClick = [this]()
{
    DBG("Inside undo:");
    RootEngine::sharedInstance()->undoManager.undo();
};
tb3.onClick = [this]()
{
    DBG("Inside redo:");
    RootEngine::sharedInstance()->undoManager.redo();
};
tb4.onClick = [this]()
{
    DBG("Inside print:");
    DBG(RootEngine::sharedInstance()->createRootXml()->createDocument(""));
};

All works but when I quit from my application on console come out this error:
***** Leaked objects detected: “X” instance(s) of class OwnedArray**
(“X” here is the number of channels added).

Someone can help me to understand whats going on here and how to solve this? Thank you in advice!

JUCE’s leak detector fires before static destructors are called. To get around this you can use JUCE’s singleton wrapper like so:

1 Like

@ t0m really thank you for your reply! I change my class as you show me with your post, so I:

  1. Delete this method:

     static RootEngine* sharedInstance()
     {
         static RootEngine rootEngine;
         return &rootEngine;
     }
    
  2. Inherited my class from DeletedAtShutdown adding " : private DeletedAtShutdown"

  3. Add this line inside the public: of my class: “JUCE_DECLARE_SINGLETON(RootEngine, false )”

  4. Add this line outside my class: “JUCE_IMPLEMENT_SINGLETON (RootEngine)”

  5. Add this line inside the destructor (I have both constructor and destructor of my class under private: section): “clearSingletonInstance();”

However when I try to compile my project xCode fails and gives me this error:
“linker command failed with exit code 1 (use -v to see invocation)”.

I’m sorry for the trouble and I really thank you again! :slight_smile:

Did you put this line into a cpp file? afaik it won’t work, if it’s in a header file.

1 Like

WOW thank you @daniel, now all works perfectly, but can I ask you why it doesn’t work if it is in the same .h file? (it’s just my curiosity) !

Really thank you again!

The static variable of your singleton needs to be initialised exactly one time. A header occurs in several translation units, so you will end up with multiple definitions.
By putting it into a cpp, it will be initialised in one translation unit, and the linker can find it when putting all together.

I know that it works that way, but the explanation might be not completely accurate. Anybody please correct me if I am wrong.

1 Like

Really Thank you again! :slight_smile:

The singleton macro stuff is probably one of the oldest surviving bits of JUCE… I think with C++14 it’s probably possible now to write something much cleaner and easier to use.

However, you might want to think about using juce::SharedResourcePointer instead, which cleans itself up automatically using reference counting.

2 Likes

Thank you! I’ll try it! :slight_smile: