c ++中的事件委托

时间:2017-05-20 10:05:30

标签: c++ c++11

我有以下委托类:

    template <typename RetVal, typename ...Args>
    class KxCEventDelegate
    {
        union InstancePtr
        {
            InstancePtr(void) : as_void(nullptr) {}

            void* as_void;
            const void* as_const_void;
        };

        typedef RetVal(*InternalFunction)(InstancePtr, Args&& ...args);
        typedef std::pair<InstancePtr, InternalFunction> Stub;

        // Turns a free function into internal function stub
        template <RetVal(*Function)(Args ...args)>
        static KX_INLINE RetVal FunctionStub(InstancePtr, Args&& ...args)
        {
            // we don't need the instance pointer because we're dealing with free functions
            return (Function)(std::forward<Args>(args)...);
        }

        // Turns a member function into internal function stub
        template <class C, RetVal (C::*Function)(Args ...args)>
        static KX_INLINE RetVal ClassMethodStub(InstancePtr instance, Args&& ...args)
        {
            // cast the instance pointer back into the original class instance
            return (static_cast<C*>(instance.as_void)->*Function)(std::forward<Args>(args)...);
        }

        // Turns a member function into internal function stub
        template <class C, RetVal(C::*Function)(Args ...args) const>
        static KX_INLINE RetVal ClassMethodStubConst(InstancePtr instance, Args&& ...args)
        {
            // cast the instance pointer back into the original class instance
            return (static_cast<const C*>(instance.as_const_void)->*Function)(std::forward<Args>(args)...);
        }

    public:
        // Binds a free function
        template <RetVal(*Function)(Args ...args)>
        void Bind(void)
        {
            m_stub.first.as_void = nullptr;
            m_stub.second = &FunctionStub<Function>;
        }

        // Binds a class method
        template <class C, RetVal (C::*Function)(Args ...args)>
        void Bind(C* instance)
        {
            m_stub.first.as_void = instance;
            m_stub.second = &ClassMethodStub<C, Function>;
        }

        // Binds a class method
        template <class C, RetVal(C::*Function)(Args ...args) const>
        void BindConst(const C* instance)
        {
            m_stub.first.as_const_void = instance;
            m_stub.second = &ClassMethodStubConst<C, Function>;
        }

        // Invokes the delegate
        RetVal Invoke(Args ...args) const
        {
            KX_ASSERT(m_stub.second != nullptr, "Cannot invoke unbound delegate. Call Bind() first.", m_stub.second, nullptr);
            return m_stub.second(m_stub.first, std::forward<Args>(args)...);
        }

    private:
        Stub m_stub;
    };

用法是这样的(对于自由函数):

    int FreeFunctionInt(int i)
    {
        return i;
    }

    KxCEventDelegate<int, int> delegate;
    delegate.Bind<&FreeFunctionInt>();
    int ret = delegate.Invoke(10);

现在,我正在尝试实现一个通用的GetEventDelegate函数,类似于c#。这就是我的意思:

    // Gets event delegate.
    template <typename RetVal, typename ...Args>
    KxCEventDelegate<RetVal, Args...> GetEventDelegate(RetVal(*Function)(Args ...args))
    {
        KxCEventDelegate<RetVal, Args...> delegate;
        delegate.Bind<Function>();
        return delegate;
    }

这是我无法弄清楚出了什么问题的部分。这似乎是delegate.Bind<Function>();的一个问题。编译器给出了以下错误:

1>------ Build started: Project: Tests, Configuration: Debug x64 ------
1>Main.cpp
1>c:\sdk\kx\kxengine\include\events\delegate.h(88): error C2672: 'kx::events::KxCEventDelegate<int,int>::Bind': no matching overloaded function found
1>c:\sdk\kx\tests\eventstests.h(76): note: see reference to function template instantiation 'kx::events::KxCEventDelegate<int,int> kx::events::GetEventDelegate<int,int>(RetVal (__cdecl *)(int))' being compiled
1>        with
1>        [
1>            RetVal=int
1>        ]
1>c:\sdk\kx\kxengine\include\events\delegate.h(88): error C2974: 'kx::events::KxCEventDelegate<int,int>::Bind': invalid template argument for 'C', type expected
1>c:\sdk\kx\kxengine\include\events\delegate.h(58): note: see declaration of 'kx::events::KxCEventDelegate<int,int>::Bind'
1>c:\sdk\kx\kxengine\include\events\delegate.h(88): error C2975: 'Function': invalid template argument for 'kx::events::KxCEventDelegate<int,int>::Bind', expected compile-time constant expression
1>c:\sdk\kx\kxengine\include\events\delegate.h(49): note: see declaration of 'Function'
1>Done building project "Tests.vcxproj" -- FAILED.

1 个答案:

答案 0 :(得分:2)

在此代码段中,您的通用GetEventDelegate功能:

template <typename RetVal, typename ...Args>
KxCEventDelegate<RetVal, Args...> GetEventDelegate(RetVal(*Function)(Args ...args)) {
    KxCEventDelegate<RetVal, Args...> delegate;
    delegate.Bind<Function>();
    return delegate;
}

Function不是常量表达式,因为如果您打算将其用作模板参数,它应该是。 在C ++ 11/14中,您可以使用另一个间接层来解决它。像这样:

template <typename RetVal, typename ...Args>
struct Factory {
    template<RetVal(*Function)(Args...)>
    static KxCEventDelegate<RetVal, Args...> GetEventDelegate() {
        KxCEventDelegate<RetVal, Args...> delegate;
        delegate.Bind<Function>();
        return delegate;
    }
};

您可以按照以下方式使用:

auto delegate = Factory<int, int>::GetEventDelegate<&FreeFunctionInt>();

无论如何,我建议将静态函数添加到委托类中,并将其用作直接嵌入类型本身的工厂方法。
你最终会调用的东西:

auto delegate = KxCEventDelegate<int, int>::create<&FreeFunctionInt>();

读者更容易理解幕后发生的事情,至少从我的角度来看是这样。