Search code examples
c++juce

Incomplete Type InNested Name Specifier JUCE


I'm having a problem with circular dependencies when trying to manage my windows with a 'WindowList' class. When I want to close a window with closeButtonPressed code below, I need to remove the object from the windowList file, however I am including WindowSetter in the WindowList file. Previous errors like this have been solvable through a forward declaration, however I am not sure how to solve this one. Anny suggestions? (full code can be viewed here: https://gist.github.com/anonymous/7d43c6d5b2cf1fef618be9f75077ad0c)

#pragma once

#include "../JuceLibraryCode/JuceHeader.h"
#include "WindowList.h"
class WindowList;

class WindowSetter  : public DialogWindow
{
public:
WindowSetter (const String& title,
             Component* content,
             bool shouldBeResizeable,
             int initWidth, int initHeight,
             int minWidth, int minHeight,
             int maxWidth, int maxHeight)
:   DialogWindow (title, Colours::white, true, true),
    owner (this)
{
    setUsingNativeTitleBar (true);
    setResizable (true, true);
    setResizeLimits (minWidth, minHeight, maxWidth, maxHeight);
    setContentOwned (content, false);

    setVisible (true);


}

~WindowSetter()
{
}

void closeButtonPressed() override
{
    WindowList::getWindowList();        // ERROR: Incomplete type 'WindowList' named in nested name specifier
    owner = nullptr;
}

bool escapeKeyPressed() override
{
    return true;
}

private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowSetter)

ScopedPointer<Component> owner;
};

enter image description here

edit: Added the full code for the file causing the error and an error log screenshot


Solution

  • OMGtechy's response addresses the question that you asked, but I'd like to recommend a different design that:

    1. Eliminates the need to worry about cyclic dependencies
    2. Is more idiomatic JUCE code.

    The design as you have it here tightly couples things together. A more JUCE-y way to solve the problem is you use the ChangeBroadcaster / ChangeListener classes to remove that tight coupling. When you add a WindowSetter to your WindowList, also subscribe to its change messages. When the user clicks on the close button, the WindowSetter sets a boolean and alerts anyone listening to it that it's been updated.

    In sketches, it looks like

    class WindowSetter : public DialogWindow
                       , public ChangeBroadcaster
    
    {
    public:
       WindowSetter( /*(etc...)*/)
       : DialogWindow(...)
       , owner(this)
       , wantsToClose(false)
       {
          // etc
    
       }
    
       void closeButtonPressed() override
       {
          wantsToClose = true;
          // notify observers that we've changed. 
          sendChangeMessage();
       }
    
       bool windowWantsToClose() const 
       {
          return wantstoClose;
       }
    
    private:
       bool wantsToClose;
    };
    
    
    class WindowList : public ChangeListener 
    {
    
       void addWindowSetterToList(WindowSetter* wnd)
       {
          wnd->addChangeListener(this)
          windows.addIfNotAlreadyThere(wnd);
       }
    
       void changeListenerCallback(ChangeBroadcaster* src) override 
       {
          // cast from the ChangeBroadcaster base class to our WindowSetter class.
          WindowSetter* wnd = dynamic_cast<WindowSetter*>(src);
          if (nullptr != wnd)
          { 
             // if we contain the object, and the object wants to be closed...
             if (windows.contains(wnd) && wnd->windowWantsToClose())
             {
                // get rid of it. 
                windows.remove(wnd);
             }
          }
       }
    
    };
    

    You'll see this kind of design used almost everywhere in JUCE codebases.