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

时间:2017-01-17 19:56:03

标签: c++ templates delegates variadic-templates std-function

我需要像std :: function这样的东西,但后来我发现this更快(正如作者声称的那样),甚至可以通过==运算符进行比较。我改编它以允许动态返回类型和参数,如下所示:

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 Delegate的<TReturn(TArgs...)>部分到底是什么,为什么我们需要首先定义class Delegate {};

2 个答案:

答案 0 :(得分:2)

是的,所以经过几天的研究,开发和测试,我制作了一些能够以更好的语法实现更快std::function/std::bind的东西。

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

使用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

答案 1 :(得分:0)

基本上,你的语法解决方案是接近我们为实现std::function所做的工作,并牺牲灵活性的速度(由于使用运行时多态而不是纯模板)

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

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

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

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();
}

第二个有点棘手。

解决这个问题的想法是声明一个抽象的基类指针,我们可以在以后派生它来进行调用:

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的实现:

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 lambdas(......)。