将模板lambda转换为`void *`是否安全?

时间:2017-07-03 13:07:34

标签: c++ function lambda coroutine reinterpret-cast

我正在使用汇编程序中实现的协同程序来实现光纤。协同程序通过cocall来更改堆栈。

我想使用更高级别的接口在C ++中公开它,因为cocall程序集只能处理单个void*参数。

为了处理模板lambdas,我已经尝试将它们转换为void*,并发现在编译和工作时,我想知道这样做是否安全,假设所有权语义为堆叠(由纤维保存)。

template <typename FunctionT>
struct Coentry
{
    static void coentry(void * arg)
    {
        // Is this safe?
        FunctionT * function = reinterpret_cast<FunctionT *>(arg);

        (*function)();
    }

    static void invoke(FunctionT function)
    {
        coentry(reinterpret_cast<void *>(&function));
    }
};

template <typename FunctionT>
void coentry(FunctionT function)
{
    Coentry<FunctionT>::invoke(function);
}


int main(int argc, const char * argv[]) {
    auto f = [&]{
        std::cerr << "Hello World!" << std::endl;
    };

    coentry(f);
}

这是否安全,另外,它是否有效?通过转换为void* am我强制编译器选择效率较低的表示形式?

此外,通过在另一个堆栈上调用coentry(void*),但原始invoke(FunctionT)已返回,是否有可能无法恢复堆栈? (类似于,我想在std::thread内调用)。

1 个答案:

答案 0 :(得分:1)

上面所做的一切都是定义的行为。唯一的性能打击是内联某些别名的东西,一个空指针可能会稍微困难一些。

但是,lambda是一个实际值,如果存储在自动存储器中,只会持续存储的堆栈帧。

您可以通过多种方式解决此问题。 std::function是一个,另一个是将lambda存储在shared_ptr<void>unique_ptr<void, void(*)(void*)>中。如果您不需要类型擦除,您甚至可以将lambda存储在具有推导类型的结构中。

前两个很容易。第三个;

template <typename FunctionT>
struct Coentry {
  FunctionT f;
  static void coentry(void * arg)
  {
     auto* self = reinterpret_cast<Coentry*>(arg);

    (self->f)();
  }
  Coentry(FunctionT fin):f(sts::move(fin)){}
};
template<class FunctionT>
Coentry<FunctionT> make_coentry( FunctionT f ){ return {std::move(f)}; }

现在让你的Coentry保持足够长的时间,直到任务完成。

您如何管理生命周期的细节取决于问题其余部分的结构。