将模板 lambda 转换为 `void *` 是否安全?
Is it safe to convert a template lambda to a `void *`?
我正在使用在汇编程序中实现的协程来实现纤程。协同程序通过 cocall
更改堆栈。
我想使用更高级别的接口在 C++ 中公开它,因为 cocall
程序集只能处理单个 void*
参数。
为了处理模板 lambda,我尝试将它们转换为 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*
我是否强迫编译器选择效率较低的表示?
此外,通过在不同的堆栈上调用 coentry(void*)
,但原始的 invoke(FunctionT)
已经返回,是否有可能堆栈可能无效以恢复? (类似于,我猜是在 std::thread
内调用)。
上面所做的一切都是定义的行为。唯一的性能影响是内联一些别名 thro7gh 的 void 指针可能会稍微困难一些。
但是,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
保持足够长的时间,直到任务完成。
如何管理生命周期的细节取决于问题其余部分的结构。
我正在使用在汇编程序中实现的协程来实现纤程。协同程序通过 cocall
更改堆栈。
我想使用更高级别的接口在 C++ 中公开它,因为 cocall
程序集只能处理单个 void*
参数。
为了处理模板 lambda,我尝试将它们转换为 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*
我是否强迫编译器选择效率较低的表示?
此外,通过在不同的堆栈上调用 coentry(void*)
,但原始的 invoke(FunctionT)
已经返回,是否有可能堆栈可能无效以恢复? (类似于,我猜是在 std::thread
内调用)。
上面所做的一切都是定义的行为。唯一的性能影响是内联一些别名 thro7gh 的 void 指针可能会稍微困难一些。
但是,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
保持足够长的时间,直到任务完成。
如何管理生命周期的细节取决于问题其余部分的结构。