Question

I'm designing a GUI and I have a problem, because I can't avoid dynamic_casting.

My classes:

  class Widget; //base class for all widgets

  class Container //contains widgets
  {
     std::map<std::string, Widget*> m_widgets;
     public:
     template <class T> T* get(const std::string &name)
     {
         return dynamic_cast<T*>(m_widgets.at(name)); //I need casting here
     }
   }

How can I avoid dynamic casting? I can't afford to have container for every widget type because my GUI must work with user-defined widgets. Also, I must have a container for every widget so the user doesn't have to store widgets on his own.

Why do I need casting?

 class TextBox : public Widget
 { 
      public:
      std::string getText(); //I can't have it in Widget class, because it's object-specific
      //also, my gui must work with user-defined widgets so I can't provide 
      //empty virtual functions for everything in Widget
 }
Was it helpful?

Solution 2

To expand on my comment…

Based on your code, I guess you intend to let me specify a string name for each child widget I add to a Container, and then later to access the child widget by name. So you'd expect me to write something like this:

class LoginController {

    Container *container;
    static const char *kUsernameKey = "username";
    static const char *kPasswordKey = "password";

public:

    LoginController() :
        container(new Container())
    {
        container->addChild(kUsernameKey, new TextBox());
        container->addChild(kPasswordKey, new TextBox());
        container->addChild("button", new Button("Log In"));
        container->get<Button>("button")->setAction([](){
            this->login();
        })
    }

    void login() {
        string username = container->get<TextBox>(kUsernameKey)->getText();
        string password = container->get<TextBox>(kPasswordKey)->getText();
        sendLoginRequest(username, password);
    }

};

Designing Container this way does lookups and type checks at run time, but those lookups and type checks could be done at compile time.

Instead, design the API so that I keep my own, specifically-typed references to the children in my own variables. Looking up the children simply becomes using the variables, and no casting is necessary. The lookups and type checks are done at compile time. The code looks like this:

class LoginController {

    Container *container;
    TextBox *usernameBox;
    TextBox *passwordBox;

public:

    LoginController() :
        container(new Container()),
        usernameBox(new TextBox()),
        passwordBox(new TextBox())
    {
        container->addChild(username);
        container->addChild(password);

        Button *button = new Button("Log In");
        container->addChild(button);
        button->setAction([](){
            this->login();
        })
    }

    void login() {
        string username = usernameBox->getText();
        string password = passwordBox->getText();
        sendLoginRequest(username, password);
    }

};

OTHER TIPS

I think you don't need a container holding all the widgets. The classes can hold a pointer to the concrete instance of the widget they need to use. You can pass the pointer as a parameter on the constructor; another option is to use dependency injection (e.g. wallaroo can be used for your problem).

I read everywhere that RTTI is bad and should be avoided

I doubt you read that everywhere as that is pure bullshit.

RTTI is a very good feature. Though it definitely should be avoided where even better tools are available. A good hierarchy is designed to be usable just having access to base class interface through virtual functions. And in those cases you need not any casts, just call the virtual function and it will just do the right thing.

Even your GetText might be a fair candidate with default implementation returning an empty string. Maybe in company of a feature query facility that reports presence of actual text. So most clients can just call away end be happy with empty string and others might do the check.

And those who are interested in some rare interface od just a concrete class can call dynamic_cast for good. The collection better just keep it simple and restrict to collecting. Maybe adding a few special forms for the really common Widget families.

The issue here is that your design is backwards. You want to render a Widget, but the Widget has no concrete renderable features. You have to ask the Widget to render itself, where it can render text/image/whatever it wants.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top