我正在使用汇编程序中实现的协同程序来实现光纤。协同程序通过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
内调用)。
答案 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
保持足够长的时间,直到任务完成。
您如何管理生命周期的细节取决于问题其余部分的结构。