How to achieve dynamic polymorphism (run-time call dispatch) on unrelated types?

后端 未结 4 446
说谎
说谎 2021-02-01 23:50

GOAL:

I would like to achieve type-safe dynamic polymorphism (i.e. run-time dispatch of a function call) on unrelated types

4条回答
  •  栀梦
    栀梦 (楼主)
    2021-02-02 00:10

    I once solved this by simulating .NET delegates:

    template
    class Delegate
    {
        //static_assert(false, "T must be a function type");
    };
    
    template
    class Delegate
    {
    private:
        class HelperBase
        {
        public:
            HelperBase()
            {
            }
    
            virtual ~HelperBase()
            {
            }
    
            virtual ReturnType operator()() const = 0;
            virtual bool operator==(const HelperBase& hb) const = 0;
            virtual HelperBase* Clone() const = 0;
        };
    
        template
        class Helper : public HelperBase
        {
        private:
            Class* m_pObject;
            ReturnType(Class::*m_pMethod)();
    
        public:
            Helper(Class* pObject, ReturnType(Class::*pMethod)()) : m_pObject(pObject), m_pMethod(pMethod)
            {
            }
    
            virtual ~Helper()
            {
            }
    
            virtual ReturnType operator()() const
            {
                return (m_pObject->*m_pMethod)();
            }
    
            virtual bool operator==(const HelperBase& hb) const
            {
                const Helper& h = static_cast(hb);
                return m_pObject == h.m_pObject && m_pMethod == h.m_pMethod;
            }
    
            virtual HelperBase* Clone() const
            {
                return new Helper(*this);
            }
        };
    
        HelperBase* m_pHelperBase;
    
    public:
        template
        Delegate(Class* pObject, ReturnType(Class::*pMethod)())
        {
            m_pHelperBase = new Helper(pObject, pMethod);
        }
    
        Delegate(const Delegate& d)
        {
            m_pHelperBase = d.m_pHelperBase->Clone();
        }
    
        Delegate(Delegate&& d)
        {
            m_pHelperBase = d.m_pHelperBase;
            d.m_pHelperBase = nullptr;
        }
    
        ~Delegate()
        {
            delete m_pHelperBase;
        }
    
        Delegate& operator=(const Delegate& d)
        {
            if (this != &d)
            {
                delete m_pHelperBase;
                m_pHelperBase = d.m_pHelperBase->Clone();
            }
    
            return *this;
        }
    
        Delegate& operator=(Delegate&& d)
        {
            if (this != &d)
            {
                delete m_pHelperBase;
                m_pHelperBase = d.m_pHelperBase;
                d.m_pHelperBase = nullptr;
            }
    
            return *this;
        }
    
        ReturnType operator()() const
        {
            (*m_pHelperBase)();
        }
    
        bool operator==(const Delegate& d) const
        {
            return *m_pHelperBase == *d.m_pHelperBase;
        }
    
        bool operator!=(const Delegate& d) const
        {
            return !(*this == d);
        }
    };
    

    You can use it much like .NET delegates:

    class A
    {
    public:
        void M() { ... }
    };
    
    class B
    {
    public:
        void M() { ... }
    };
    
    A a;
    B b;
    
    Delegate d = Delegate(&a, &A::M);
    d(); // calls A::M
    
    d = Delegate(&b, &B::M);
    d(); // calls B::M
    

    This works with methods that have no arguments. If you can use C++11, you can modify it to use variadic templates to handle any number of parameters. Without C++11, you need to add more Delegate specializations to handle specific numbers of parameters:

    template
    class Delegate
    {
        ...
    };
    
    template
    class Delegate
    {
        ...
    };
    

    With this Delegate class you can also emulate .NET events, which are based on delegates.

提交回复
热议问题