简体   繁体   中英

Is there a better solution than dynamic_cast in that case?

I'm trying to make platform independent code so I'm using OOP. For example, on Windows, Mac OS X, and Linux you can have windows, but on android you have views so I'm trying to abstract this.

I first made a class to represent a window or a view which I called view:

class View
{
public:
    virtual ~View()
    {}

    virtual void display() = 0;
    virtual void hide() = 0;
};

Now the problem is that on Android, there is no title for views while on Windows there are so I decided to create another class:

class NameableView : public View
{
public:
    virtual void setName(const std::string& name) 
};

And then finally implement the classes:

class WindowsView : public NameableView
{
    /* Windows implementation */
}

class AndroidView : public View
{
    /* Android implementation */
}

Then I need to make some code which sets the name for a view only if it is possible (if it inherits from the NameableView class).

So how can I solve this problem? I first thought about dynamic_cast but I often hear that having too much dynamic_cast is an indication of a design problem. I'm a beginner in C++ so maybe I didn't think the right way and I should change the whole design.

I'm trying to make platform independent code so I'm using OOP.

This is not an optimal approach - polymorphic hierarchies and virtual functions allow different concrete object types that inherit from the same interface to behave differently at run-time , but you know the platform you're going to target at compile-time .

What you should instead do is use static polymorphism and CRTP to provide a common interface that every concrete per-platform implementation must satisfy.

template <typename TDerived>
struct View
{
    void display() { static_cast<TDerived&>(*this).display(); }
    void hide() { static_cast<TDerived&>(*this).hide(); }

    constexpr bool supportsSetView() const
    {
        return static_cast<TDerived&>(*this).supportsSetView();
    }
};

In the case of setName , you should provide a supportsSetView check on every platform that returns true at compile-time if the view can be named. Then you perform that check on the caller side and only invoke setName if the check passes.

Example usage:

#if defined(PLATFORM_ANDROID)
struct AndroidView 
{
    // ...
    constexpr bool supportsSetView() const { return false; }
};   

using MyView = View<AndroidView>;
#else if defined(PLATFORM_WINDOWS)
struct WindowsView 
{
    // ...
    constexpr bool supportsSetView() const { return true; }
    void setName(std::string x) { /* ... */ }
};

using MyView = View<WindowsView>;
#else
#error "Unsupported platform."
#endif

Caller side:

MyView currentView;

if constexpr(currentView.supportsSetView())
{
    currentView.setName("something");
}

As if constexpr(...) 's evaluation occurs at compile-time, the code will only call setName if it is supported by MyView .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM