免费和成员函数的自定义 std::function-like 实现

Custom std::function-like implementation for both free and member functions

我需要像 std::function 这样的东西,但后来我发现 this 更快(正如作者所说),甚至可以用 == 运算符进行比较。我对其进行了调整以允许动态 return 类型和参数,如下所示:

template<typename TReturn, typename... TArgs>
class Delegate {};

template<typename TReturn, typename... TArgs>
class Delegate<TReturn(TArgs...)> final
{
private:
    typedef void* InstancePointer;
    typedef TReturn (*InternalFunction)(InstancePointer, TArgs...);

private:
    // Turns a free function into our internal function stub
    template <TReturn (*FreeFunction)(TArgs...)>
    static TReturn FreeFunctionStub(InstancePointer instance, TArgs... args) {
        // We don't need the instance pointer because we're dealing with free functions
        return (FreeFunction)(std::forward<TArgs>(args)...);
    }

    // Turns a member function into our internal function stub
    template <class TClass, TReturn (TClass::*MemberFunction)(TArgs...)>
    static TReturn MemberFunctionStub(InstancePointer instance, TArgs... args) {
        // Cast the instance pointer back into the original class instance
        return (static_cast<TClass*>(instance)->*MemberFunction)(std::forward<TArgs>(args)...);
    }

public:
    Delegate() = default;

    // Resets this delegate to a new free function
    template <TReturn(*FreeFunction)(TArgs...)>
    void reset() {
        m_instance = nullptr;
        m_function = &FreeFunctionStub<FreeFunction>;
    }

    // Resets this delegate to a new member function
    template <class TClass, TReturn(TClass::*MemberFunction)(TArgs...)>
    void reset(TClass* instance) {
        m_instance = instance;
        m_function = &MemberFunctionStub<TClass, MemberFunction>;
    }

    // Resets this delegate to a new free function
    void specialReset(TReturn(*FreeFunction)(TArgs...)) {
        m_instance = nullptr;
        m_function = ???
    }

    // Resets this delegate to a new member function
    template<class TClass>
    void specialReset(TClass *instance, TReturn(TClass::*MemberFunction)(TArgs...)) {
        m_instance = instance;
        m_function = ???
    }

    // Invokes this delegate
    TReturn invoke(TArgs... args) const {
        if (m_function == nullptr)
            throw new std::runtime_error(""Unbound delegate! Call reset() first."");

        return m_function(m_instance, std::forward<TArgs>(args)...);
    }

private:
    InstancePointer m_instance;
    InternalFunction m_function;
};

用法是这样的:

Delegate<void()> del1;
Delegate<int(double)> del2;

del1.reset<&someParameterlessVoidFreeFunction>();
del1.invoke();

del2.reset<SomeClass, &SomeClass::someIntMemberFunction>(&someClassInstance);
del2.invoke(24.2);

我想做的是实现这样的目标(IMO,更简洁直观):

Delegate<void()> del1;
Delegate<int(double)> del2;

del1.reset(&someParameterlessVoidFreeFunction);
del1.invoke();

del2.reset(&SomeClass::someIntMemberFunction, &someClassInstance);
del2.invoke(24.2);

不过,我不太明白m_function这个概念。我想要达到的目标甚至是可能的?我怎么能那样做?

此外,class 委托的 <TReturn(TArgs...)> 部分到底是什么?为什么我们需要先定义一个 class Delegate {};

基本上,您的语法的解决方案是更接近我们为实现 std::function 所做的事情并牺牲速度以获得灵活性(由于使用 run-time 多态性而不是纯模板)

可以通过向 Delegate class 添加另一个成员变量来保存自由函数来解决第一个调用:

template<typename TReturn, typename... TArgs>
class Delegate<TReturn(TArgs...)> final
{
    // ...
    typedef TReturn(*FreeFunctionType)(TArgs...);
    FreeFunctionType m_free_function = nullptr;
};

现在您可以在 specialReset 内设置 m_free_function,同时重置其他成员:

void specialReset(TReturn(*FreeFunction)(TArgs...)) {
    m_instance = nullptr;
    m_function = nullptr;
    m_free_function = FreeFunction;
}

并调用它执行额外的空检查:

TReturn invoke(TArgs... args) const {
    if (m_function == nullptr && m_free_function == nullptr)
        throw new std::runtime_error("Unbound delegate! Call reset() first.");
    else if (m_function)
        return m_function(m_instance, std::forward<TArgs>(args)...);
    else return m_free_function(std::forward<TArgs>(args)...);
}

和一个测试:

void foo()
{
    std::cout << "Called foo()\n";
}

int main()
{
    Delegate<void()> del1;

    del1.specialReset(&::foo);
    del1.invoke();
}

第二个有点棘手。

解决这个问题的想法是声明一个抽象基 class 指针,我们可以从中派生稍后进行调用:

template<typename TReturn, typename... TArgs>
class Delegate<TReturn(TArgs...)> final
{
private:
    //...
    struct ICall
    {
        virtual TReturn doCall(TArgs... args) = 0;
    };
     std::unique_ptr<ICall> caller;
};

通常这个指针是nullptr,我们可以检查,否则我们可以调用invoke中的函数:

// Invokes this delegate
TReturn invoke(TArgs... args) const {
    if (m_function == nullptr && m_free_function == nullptr && caller == nullptr)
        throw new std::runtime_error("Unbound delegate! Call reset() first.");
    else if (m_function)
        return m_function(m_instance, std::forward<TArgs>(args)...);
    else if (caller)
        return caller->doCall(std::forward<TArgs>(args)...);
    else 
        return m_free_function(std::forward<TArgs>(args)...);
}

最后是我们第二个 specialReset 的实现,我们创建一个派生 class 并设置我们的指针:

template<class TClass>
void specialReset(TClass *instance, TReturn(TClass::*MemberFunc)(TArgs...)) {
    m_instance = nullptr;
    m_function = nullptr;
    m_free_function = nullptr;
    struct DerivedCall : public ICall
    {
        DerivedCall(TClass* _instance, TReturn(TClass::*_func)(TArgs...)) : m_instance(_instance), m_func(_func){}
        TReturn doCall(TArgs... args) override
        {
            return (m_instance->*m_func)(std::forward<TArgs>(args)...);
        }
        TClass* m_instance;
        TReturn(TClass::*m_func)(TArgs...);
    };
    caller = std::make_unique<DerivedCall>(instance, MemberFunc);
}

还有一个测试:

struct A{
    int foo(double){std::cout << "Called A::foo\n"; return 42;}
};

int main()
{ 
    Delegate<int(double)> del2;
    A a;

    del2.specialReset(&a, &A::foo);
    del2.invoke(24.2);
}

Demo

我相信比我聪明的人一定能想出更好的办法。就像我说的。由于这一切的运行时间,我们可能已经失去了您提倡的超过 std::function 的宝贵速度,而且我不确定它对 operator== 做了什么(第二次可能变得不可行我创建了 m_free_function)。我们不能在此处将 specialReset 与 lambda 一起使用(...还)。

是的,经过几天的研究、开发和测试,我编造了一些确实可以更快 std::function/std::bind 且语法更好的东西。

对于那些想知道的人,请看一看 Delegator。我不能在这里粘贴整个代码,因为它太大了,无法在这里发布。此外,在此 link 您将始终拥有最新版本。

使用 Delegator,您可以执行以下操作:

Delegate<void(void)> d1; // Blank delegate
Delegate<void(void)> d2(&freeFunction); // Delegate to a free function
Delegate<void(void)> d3(&SomeClass::someClassStaticFunction); // Delegate to a static function
Delegate<void(void)> d4(&SomeClass::someClassFunction, &someClassInstance); // Delegate to a member function
Delegate<void(void)> d5(&SomeClass::someClassConstFunction, &someClassInstance); // Delegate to a member const function

d1.reset(); // Resets the delegate
d1.reset(&freeFunction); // Resets the delegate to a free function
d1.reset(&SomeClass::someClassStaticFunction); // Resets the delegate to a static function
d1.reset(&SomeClass::someClassFunction, &someClassInstance); // Resets the delegate to a member function
d1.reset(&SomeClass::someClassConstFunction, &someClassInstance); // Resets the delegate to a member const function
d1.reset(&d2); // Resets the delegate to d2's state