2017-09-29 7 views
1

J'ai un problème avec les dépendances circulaires lorsque j'essaie de gérer mes fenêtres avec une classe 'WindowList'. Quand je veux fermer une fenêtre avec le code de closeButtonPressed ci-dessous, j'ai besoin de retirer l'objet du fichier windowList, cependant j'inclus WindowSetter dans le fichier WindowList. Les erreurs précédentes comme celle-ci ont pu être résolues grâce à une déclaration directe, mais je ne suis pas sûr de savoir comment résoudre celui-ci. Anny suggestions? (code complet peut être consulté ici: https://gist.github.com/anonymous/7d43c6d5b2cf1fef618be9f75077ad0c)Incomplete Type InNested Nom Spécificateur JUCE

#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

modifier: Ajout le code complet du fichier provoque l'erreur et une capture d'écran du journal d'erreur

Répondre

2

la réponse OMGtechy répond à la question que vous avez posée, mais je voudrais recommander un design différent que:

  1. élimine le besoin de se soucier des dépendances cycliques
  2. est plus Code idiomatiques de JUCE.

Le design tel que vous l'avez ici combine étroitement les choses ensemble. Une façon plus efficace de résoudre le problème est d'utiliser les classes ChangeBroadcaster/ChangeListener pour supprimer ce couplage étroit. Lorsque vous ajoutez un WindowSetter à votre WindowList, vous pouvez également vous abonner à ses messages de modification. Lorsque l'utilisateur clique sur le bouton de fermeture, le WindowSetter définit un booléen et avertit quiconque l'écoute qu'il a été mis à jour.

En croquis, il ressemble à

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); 
     } 
     } 
    } 

}; 

Vous verrez ce genre de conception utilisé presque partout dans codebases de JUCE.

+0

C'est exactement le genre de solution que je cherche! Encore tout à fait nouveau pour JUCE/rien de plus que bonjour monde en C++ – Jefferson

+1

Le temps passé à lire la source des exemples JUCE sera payant encore et encore. – bgporter

1

Jetons un coup d'oeil à votre code de la perspective d'un compilateur imaginaire pour savoir ce qui se passe ...

// NOTE: we're in WindowSetter.h 

#include "WindowList.h" 

Sure chose, laissez « S vont jeter un oeil à ce fichier et l'inclure ici ...

// NOTE: we're in WindowList.h 

#include "WindowSetter.h" 

Chose certaine, nous allons aller jeter un oeil ...

// NOTE: we're back in WindowSetter.h 

#include "WindowList.h" 

Mais ... Je faisais juste cette. Oh mon dieu, je suis dans une boucle infinie. AIDEZ-MOI!

KABOOM

Le compilateur ne peut pas inclure les en-têtes que vous demandez, car ils ont tous deux besoin les uns des autres dans une boucle infinie. Vous devez rompre ce cycle en n'incluant pas les en-têtes de cette manière récursive.

Les déclarations anticipées, que vous connaissez déjà, peuvent être utiles pour cela. La raison en est qu'ils peuvent informer le compilateur d'un nom de type sans avoir à inclure l'en-tête. C'est génial si le compilateur a juste besoin de connaître le nom du type, mais rien d'autre (comme la taille, par exemple).

Le problème que vous avez ici cependant, les questions d'architecture à part, est que vous utilisez les entrailles de l'intérieur WindowListWindowSetter ici:

WindowList::getWindowList();  // ERROR: Incomplete type 'WindowList' named in nested name specifier 

Ceci est probablement la raison pour laquelle vous avez fini, y compris l'en-tête dans la première place; Si vous ne l'avez pas, cela vous donnerait une erreur similaire. Pour contourner ce problème, vous pouvez déplacer la définition de la fonction membre closeButtonPressed() en dehors du fichier d'en-tête et dans un fichier .cpp. Vous voudrez probablement déplacer les autres fonctions aussi pour des raisons de cohérence (une question d'opinion personnelle).

Une fois que vous avez fait cela, vous n'utiliserez plus les détails de WindowList à l'intérieur de WindowSetter.h, et serez donc en mesure d'arrêter de l'inclure.

Cela résoudra votre problème, sauf s'il y a d'autres dépendances cycliques cachées ailleurs (je n'ai pas lu tout cela).

+1

Merci pour cette explication détaillée OMGtech! Je pensais que le problème était lié à la façon dont j'utilisais l'inclus, mais je n'étais pas sûr de la manière «standard» d'aborder un tel problème. Des suggestions pour l'avenir pour éviter d'avoir à faire face à ces problèmes en premier lieu? – Jefferson

+1

Je pense que @bgporter a couvert cela pour la plupart :) Une chose supplémentaire que vous pouvez penser cependant est des interfaces; Définissez une interface et demandez à votre classe de prendre un objet de type inconnu conforme à cette interface. De cette façon, vous ne vous souciez pas de la classe que vous obtenez, vous vous souciez de ce qu'elle peut faire. Autre que cela, fait juste des erreurs comme ceci et apprend d'eux;) les erreurs sont des occasions, pas des monstres odieux. – OMGtechy