【发布时间】:2017-12-06 18:29:52
【问题描述】:
我正在使用在汇编器中实现的协程来实现纤程。协程由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 中调用)。
【问题讨论】:
-
C 是 not allow casting function pointers to
void*,我知道它在 C++ 中是一样的——除非最近发生了变化(我不知道),否则无论如何你都在做非法的事情...... -
@VTT 不应该所有指针的大小都相同吗?
-
不,指针的大小可能会有所不同。在 x86 上,指向常规函数的指针的大小(在您的情况下,lambda 只是一个普通函数,因为它不捕获任何内容)与数据指针的大小相同。但它可能会有所不同。指向成员函数的指针的大小要大得多。同样在这个例子中,你实际上可以传递一个指向 lambda 对象的指针,而不是一个函数。
-
-
@ioquatix 可能有一些特殊的cpu,其中函数指针和数据指针的大小不同(它与地址、数据和指令的大小有关)总线),但在今天的普通 cpu 上它们具有相同的大小。
标签: c++ function lambda coroutine reinterpret-cast