以多态方式处理非多态对象,而没有性能开销 [英] treat non-polymorphic objects in a polymorphic way with no performance overhead

查看:162
本文介绍了以多态方式处理非多态对象,而没有性能开销的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题的灵感来自这个问题,问题是调用相同的方法

This question is inspired by this question which was asking about calling the same method on dissimilar types when those types are known at compile time.

这让我想到了。假设我有不同的非多态类型,但我想要使用它们多态。此外,我不想调用 new delete ,因为这些都是已知的性能瓶颈。

This got me thinking. Suppose I had dissimilar non-polymorphic types but I wanted use them polymorphically. Furthermore, I want to do that without ever invoking new and delete since these are known performance bottlenecks.

我该如何做?

注意这是一个Q& A风格问题。我提供了我想出的答案。这不是为了吸引upvote(虽然这总是很好),但分享我在这个问题工作时获得的见解。

Note this is a Q&A style question. I have provided the answer I came up with. This is not to attract upvotes (although that's always nice), but to share the insight I gained while working on this problem.

其他答案当然是邀请。

Other answers are certainly invited. The more knowledge we share, the better we all become.

推荐答案

这个答案的灵感来自于我们所做的出色工作Beman Dawes的boost :: system_error库。

This answer was in-part inspired by the excellent work done by Beman Dawes on the boost::system_error library.

我通过研究他的梦幻般的工作学习了静态多态性的想法,现在已成为c ++ 11标准的一部分。 Beman,如果你读过这个,请带弓。

I learned the idea of static polymorphism by studying his fantastic work, which has now become part of the c++11 standard. Beman, if you ever read this please take a bow.

另一个灵感来源是一个非常好的演讲,叫做继承是邪恶的基础类型。我完全推荐每个c ++开发人员来观看它。

The other source of inspiration was the excellent talk called Inheritance is the base class of evil by the truly gifted Sean Parent. I thoroughly recommend every c++ developer to watch it.

足够的,这里是我的解决方案:

So enough of that, here's (my) solution:

问题:

我有一些UI对象类型没有多态性(出于性能原因)。但是,有时我希望对这些对象的组调用 show() hide()方法。

I have a number of UI object types that are not polymorphic (for performance reasons). However, sometimes I wish to call show() or hide() methods on groups of these objects.

此外,我希望这些对象的引用或指针是多态的。

Furthermore I want the references or pointers to these objects to be polymorphic.

不是所有的对象都支持 show() hide()方法, 。

Furthermore not all the objects even support the show() and hide() methods, but that shouldn't matter.

此外,运行时性能开销应尽可能接近零。

Furthermore the runtime performance overhead should be as close to zero as possible.

非常感谢@ Jarod42为 showable 建议一个不那么复杂的构造函数。

Many thanks to @Jarod42 for suggesting a less complicated constructor for showable.

我的解决方案:

#include <iostream>
#include <vector>
#include <utility>
#include <typeinfo>
#include <type_traits>

// define an object that is able to call show() on another object, or emit a warning if that
// method does not exist
class call_show {

    // deduces the presence of the method on the target by declaring a function that either
    // returns a std::true_type or a std::false_type.
    // note: we never define the function. we just want to deduce the theoretical return type

    template<class T> static auto test(T* p) -> decltype(p->show(), std::true_type());
    template<class T> static auto test(...) -> decltype(std::false_type());

    // define a constant based on the above test using SFNAE
    template<class T>
    static constexpr bool has_method = decltype(test<T>(nullptr))::value;

public:

    // define a function IF the method exists on UIObject
    template<class UIObject>
    auto operator()(UIObject* p) const
    -> std::enable_if_t< has_method<UIObject>, void >
    {
        p->show();
    }

    // define a function IF NOT the method exists on UIObject
    // Note, we could put either runtime error handling (as below) or compile-time handling
    // by putting a static_assert(false) in the body of this function
    template<class UIObject>
    auto operator()(UIObject* p) const
    -> std::enable_if_t< not has_method<UIObject>, void >
    {
        std::cout << "warning: show is not defined for a " << typeid(UIObject).name() << std::endl;
    }
};

// ditto for the hide method
struct call_hide
{
    struct has_method_ {
        template<class T> static auto test(T* p) -> decltype(p->hide(), std::true_type());
        template<class T> static auto test(...) -> decltype(std::false_type());
    };

    template<class T>
    static constexpr bool has_method = decltype(has_method_::test<T>(nullptr))::value;

    template<class UIObject>
    auto operator()(UIObject* p) const
    -> std::enable_if_t< has_method<UIObject>, void >
    {
        p->hide();
    }

    template<class UIObject>
    auto operator()(UIObject* p) const
    -> std::enable_if_t< not has_method<UIObject>, void >
    {
        std::cout << "warning: hide is not defined for a " << typeid(UIObject).name() << std::endl;
    }
};

// define a class to hold non-owning REFERENCES to any object
// if the object has an accessible show() method then this reference's show() method will cause
// the object's show() method to be called. Otherwise, error handling will be invoked.
//
class showable
{
    // define the POLYMORPHIC CONCEPT of a thing being showable.
    // In this case, the concept requires that the thing has a show() and a hide() method
    // note that there is no virtual destructor. It's not necessary because we will only ever
    // create one model of this concept for each type, and it will be a static object
    struct concept {
        virtual void show(void*) const = 0;
        virtual void hide(void*) const = 0;
    };

    // define a MODEL of the CONCEPT for a given type of UIObject
    template<class UIObject>
    struct model final
    : concept
    {
        // user-provided constructor is necessary because of static construction (below)
        model() {};

        // implement the show method by indirection through a temporary call_show() object
        void show(void* p) const override {
            // the static_cast is provably safe
            call_show()(static_cast<UIObject*>(p));
        }

        // ditto for hide
        void hide(void* p) const override {
            call_hide()(static_cast<UIObject*>(p));
        }
    };

    // create a reference to a static MODEL of the CONCEPT for a given type of UIObject
    template<class UIObject>
    static const concept* make_model()
    {
        static const model<UIObject> _;
        return std::addressof(_);
    }

    // this reference needs to store 2 pointers:

    // first a pointer to the referent object
    void * _object_reference;

    // and secondly a pointer to the MODEL appropriate for this kind of object
    const concept* _call_concept;

    // we use pointers because they allow objects of the showable class to be trivially copyable
    // much like std::reference_wrapper<>

public:

    // PUBLIC INTERFACE

    // special handling for const references because internally we're storing a void* and therefore
    // have to cast away constness
    template<class UIObject>
    showable(const UIObject& object)
    : _object_reference(const_cast<void*>(reinterpret_cast<const void *>(std::addressof(object))))
    , _call_concept(make_model<UIObject>())
    {}

    template<class UIObject>
    showable(UIObject& object)
    : _object_reference(reinterpret_cast<void *>(std::addressof(object)))
    , _call_concept(make_model<UIObject>())
    {}

    // provide a show() method.
    // note: it's const because we want to be able to call through a const reference
    void show() const {
        _call_concept->show(_object_reference);
    }

    // provide a hide() method.
    // note: it's const because we want to be able to call through a const reference
    void hide() const {
        _call_concept->hide(_object_reference);
    }
};


//
// TEST CODE
//

// a function to either call show() or hide() on a vector of `showable`s
void show_or_hide(const std::vector<showable>& showables, bool show)
{
    for (auto& s : showables)
    {
        if (show) {
            s.show();
        }
        else {
            s.hide();
        }
    }
}

// a function to transform any group of object references into a vector of `showable` concepts
template<class...Objects>
auto make_showable_vector(Objects&&...objects)
{
    return std::vector<showable> {
        showable(objects)...
    };
}


int main()
{
    // declare some types that may or may not support show() and hide()
    // and create some models of those types

    struct Window{
        void show() {
            std::cout << __func__ << " Window\n";
        }
        void hide() {
            std::cout << __func__ << " Window\n";
        }

    } w1, w2, w3;

    struct Widget{

        // note that Widget does not implement show()

        void hide() {
            std::cout << __func__ << " Widget\n";
        }

    } w4, w5, w6;

    struct Toolbar{
        void show()
        {
            std::cout << __func__ << " Toolbar\n";
        }

        // note that Toolbar does not implement hide()

    } t1, t2, t3;

    struct Nothing {
        // Nothing objects don't implement any of the functions in which we're interested
    } n1, n2, n3;

    // create some polymorphic references to some of the models
    auto v1 = make_showable_vector(w3, w4, n1, w5, t1);
    auto v2 = make_showable_vector(n3, w1, w2, t2, w6);

    // perform some polymorphic actions on the non-polymorphic types
    std::cout << "showing my UI objects\n";
    show_or_hide(v1, true);
    show_or_hide(v2, true);

    std::cout << "\nhiding my UI objects\n";
    show_or_hide(v2, false);
    show_or_hide(v1, false);

    return 0;
}

示例输出:

showing my UI objects
show Window
warning: show is not defined for a Z4mainE6Widget
warning: show is not defined for a Z4mainE7Nothing
warning: show is not defined for a Z4mainE6Widget
show Toolbar
warning: show is not defined for a Z4mainE7Nothing
show Window
show Window
show Toolbar
warning: show is not defined for a Z4mainE6Widget

hiding my UI objects
warning: hide is not defined for a Z4mainE7Nothing
hide Window
hide Window
warning: hide is not defined for a Z4mainE7Toolbar
hide Widget
hide Window
hide Widget
warning: hide is not defined for a Z4mainE7Nothing
hide Widget
warning: hide is not defined for a Z4mainE7Toolbar

这篇关于以多态方式处理非多态对象,而没有性能开销的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆