-1

I'm developing an UI application where I ran into an issue with a cyclic dependency. Here is the simplified code, to explain the problem.

#include <list>

class UiStyle;
UiStyle* CreateStyle();

class UiWindow {
public:
    virtual void Draw() = 0;
};

class UiMenu : public UiWindow {
public:
    void Draw() override {
        // Some UI drawing

        // How should I implement changing style from here?
    }
};

class UiApplication {
public:
    void Draw() {
        for (UiWindow* window : this->windows)
            window->Draw();
    }

protected:
    void SetStyle(UiStyle *style);

    void AddWindow(UiWindow* window) {
        this->windows.push_back(window);
    }

private:
    std::list<UiWindow*> windows;
};

class UiMainApplication : public UiApplication {
public:
    void Initialize() {
        this->SetStyle(CreateStyle());

        this->AddWindow(new UiMenu());
    }
};

Explanation:

  • UiStyle - application style class.
  • CreateStyle - method that creates some UI style.
  • UiWindow - interface of an UI window (not to be changed)
  • UiMenu - some implemented UI window from where I need to change a style.
  • UiApplication - base application class that provides managing windows, styles, etc. (not to be changed)
  • UiMainApplication - implemented application that adds real windows, sets real style.

The problem I face is about changing the application style from inside UiMenu. How can UiMenu call UiApplication::SetStyle?

Of course, I could pass UiMainApplication pointer to UiMenu, then create public method UiMainApplication::SetStyle that will allow to call UiApplication::SetStyle, and call UiMainApplication::SetStyle from UiMenu. But, as far as I know, it's bad practice, to have a bidirectional reference, like A -> B and B -> A. In my case, UiMainApplication would refer to UiMenu and UiMenu refer to UiMenuApplication.

Hence I think it's not the best way to implement it.

Hope for your help! I will be glad to any suggestions!

2
  • It is not entirely clear what your problem is. Are you asking how to draw the menu item in the style currently set for the application? Or do you want to change the style of the application from the menu item? Feb 8, 2021 at 9:53
  • @BartvanIngenSchenau: To me, it looks very clear it is the second of your two interpretations
    – Doc Brown
    Feb 8, 2021 at 10:13

1 Answer 1

2

Cyclic references sometimes have their place, and before rejecting them as a solution, I think it is important to understand why and when they may become an issue, and if this issue really applies to your case.

For example, as you mentioned by yourself, one could solve this problem by introducing an ctor parameter UiMainApplication * to UiMenu. I would implement it this way:

class UIMenu
{
   UiMainApplication *_app;
public:
     UIMenu(UiMainApplication * app){
         _app=app;
     }
     void Draw() override {
          if(_app) {
              _app->SetStyle(...);
          }
          //...
    }
};


This would result in a cyclic dependency between UiMainApplication and UiMenu However, this is only a compile time dependency. At run time, you still can instantiate an UiMainApplication object without creating an UiMenu object first. It is also possible to create an UiMenu object without UiMainApplication, by passing a nullptr as parameter, if you need one for testing purposes.

You need to decide if this compile time dependency is acceptable for your case. It will forbid to place UiMenu in a reusable library module independend from UiMainApplication, but if you don't intend to do so, this may be perfectly fine.

Of course, it is possible to get rid of the compile time dependency, if necessary. For this, one can create a abstract new class SetStyleService with a virtual method SetStyle. This class definition can be part of the same library module where UiMenu is placed, or in a separate "interface" library. Then instead of expecting a pointer to UiMainApplication in the UiMenu ctor, you make it expecting a pointer to SetStyleService. UiMainApplication will have to be derived from SetStyleService and implement SetStyle accordingly. (Note the same behaviour can be accomplished by using function pointers and passing a pointer to SetStyle into UiMenu).

I would probably stick to the first approach as long as there is no real need for the second. Just because someone has written somewhere "bidrectional dependencies are bad practice" I would not follow such a recommendation thoughtlessly, but first check if the potential drawbacks of such a dependency really applies to my specific case.

2
  • In C++ specifically, though, every pointer cycle needs to be thought about in terms of ownership: who's responsible for deleting resources, how to present use-after-free, etc. Feb 8, 2021 at 12:41
  • @SebastianRedl: that is IMHO pretty trivial in this case: UiMainApplication is owning all window pointers like UiMenu, and UiMenu gets a pointer to UiMainApplication * , which has a lifetime longer that itself. UiMainApplication should definitely not try to use any window pointer after the latter was freed.
    – Doc Brown
    Feb 8, 2021 at 12:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.