c++ - FastDelegate 和 lambdas - 无法让它们工作(Don Clugston 最快的委托(delegate))

标签 c++ c++11 lambda delegates std-function

我正在尝试创建 Don Clugston's Member Function Pointers and the Fastest Possible C++ Delegates 的 C++11 实现, 并使其作为插入式 std::function 的替代品。

This is what I got so far.

我这样构建 lambda FastDelegates:

// FastFunc is my name for FastDelegate
template<typename LambdaType> FastFunc(LambdaType lambdaExpression)
{
    this->m_Closure.bindmemfunc(&lambdaExpression, &LambdaType::operator());
}

现在,一些测试:

FastFunc<void()> test = []{ std::cout << "hello" << std::endl; };
test();
// Correctly prints "hello"

bool b{false};
FastFunc<void()> test2 = [&b]{ std::cout << b << std::endl; };
test2();
// Crash!

如您所见,当 lambda 是“微不足道的”(没有捕获)时,按值复制它并获取它的地址是有效的。但是当 lambda 存储某种状态(捕获)时,我不能将它按值复制到 FastFunc 中。

我尝试通过引用获取 lambda,但是当它像示例中那样是临时的时我无法这样做。

我必须以某种方式存储 FastFunc 中的 lambda,但我不想使用 std::shared_ptr,因为它很慢(I tried a different fastdelegate implementation that used it, and its performance was comparable to std::function)。

我如何才能使 Don Clugston 尽可能快的 C++ 委托(delegate)的实现与捕获状态的 lambda 一起工作,从而保持 fastdelegates 的惊人性能?

最佳答案

您已经很好地诊断了情况:您需要存储状态。

由于 lambda 是一个临时对象,实际上允许您从它移动(通常),如果可能的话,这应该比复制更受欢迎(因为移动比复制更通用)。

现在,您需要做的就是为它保留一些存储空间,如果这需要动态分配,您确实可能会降低性能。另一方面,一个对象需要有一个固定的足迹,所以?

一种可能的解决方案是提供可配置(但有限)的存储容量:

static size_t const Size = 32;
static size_t const Alignment = alignof(std::max_align_t);

typedef std::aligned_storage<Size, Alignment>::type Storage;
Storage storage;

现在您可以(根据需要使用 reinterpret_cast)将 lambda 存储在 storage 中,前提是它的大小合适(可以使用 static_assert 检测到)。

终于设法得到一个工作示例(不得不从头开始,因为上帝是那么快速的委托(delegate)代码冗长!!),你可以 see it in action here (代码如下)。

我只触及了皮毛,主要是因为它缺少复制和移动运算符。为了正确地做到这一点,这些操作需要按照与其他两个操作相同的模式添加到处理程序中。

代码:

#include <cstddef>

#include <iostream>
#include <memory>
#include <type_traits>

template <typename, size_t> class FastFunc;

template <typename R, typename... Args, size_t Size>
class FastFunc<R(Args...), Size> {
public:
    template <typename F>
    FastFunc(F f): handler(&Get<F>()) {
        new (&storage) F(std::move(f));
    }

    ~FastFunc() {
        handler->destroy(&storage);
    }

    R operator()(Args&&... args) {
      return handler->apply(&storage, std::forward<Args>(args)...);
    }

private:
    using Storage = typename std::aligned_storage<Size, alignof(max_align_t)>::type;

    struct Handler {
        R (*apply)(void*, Args&&...);
        void (*destroy)(void*);
    }; // struct Handler

    template <typename F>
    static R Apply(void* f, Args&&... args) {
        (*reinterpret_cast<F*>(f))(std::forward<Args>(args)...);
    }

    template <typename F>
    static void Destroy(void* f) {
        reinterpret_cast<F*>(f)->~F();
    }

    template <typename F>
    Handler const& Get() {
        static Handler const H = { &Apply<F>, &Destroy<F> };
        return H;
    } // Get

    Handler const* handler;
    Storage storage;
}; // class FastFunc

int main() {
    FastFunc<void(), 32> stateless = []() { std::cout << "stateless\n"; };
    stateless();

    bool b = true;
    FastFunc<void(), 32> stateful = [&b]() { std::cout << "stateful: " << b << "\n"; };
    stateful();

    b = false;
    stateful();

    return 0;
}

关于c++ - FastDelegate 和 lambdas - 无法让它们工作(Don Clugston 最快的委托(delegate)),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18633697/

相关文章:

c++ - C/C++ 中的两个 'main' 函数

c++ - 初始化输出后获取控制台大小

c++ - 与 std::future 关联的存储是如何分配的?

c++ - 在模板函数 C++ 中的 F&&,Args &&... 参数之后添加另一个函数作为参数

c++ - 使用 const 后缀自动解析成员函数

Java 8 lambda Void 参数

java - 捕获 C++ lambda 表达式错误中的 JNIENV*

c++ - 如何获得唯一指针列表?

C# 套接字多线程 Lambda

c++ - Qt QTcpSocket 读取数据时不发出信号