我正在创建包装函数,修改现有函数(目标)。目标在运行时确定为没有类型信息的指针 (
void*
)。基本方案是这样的:
bool wrap(void* f, void** t)
{
*t = magic(); // target determined at runtime as void*
return true;
}
// define new wrapping function and storage for target
int(*foo_target)(int);
int foo(int data)
{
return foo_target(data) + 10;
}
auto res = wrap(reinterpret_cast<void*>(&foo), reinterpret_cast<void**>(&foo_target));
wrap
函数是一个占位符,负责完成所有繁重的工作。对于每个目标,我定义一个包装函数 (foo
) 和一个变量来存储指向具有正确类型的目标函数的函数指针 (foo_target
)。
这可行,但需要丑陋的类型转换,并且我必须保持
foo_target
和 foo
的签名同步。我想要的是这样的:
template <class F>
struct wrap
{
F* target;
wrap(F&& f)
{
target = reinterpret_cast<F*>(magic());
}
};
static auto foo = wrap(
[](int data) -> int
{
return foo.target(data) + 10; // ERROR
// return 10; // WORKS
});
}
所需的单一类型转换隐藏在
wrap
函数中,并且目标函数的签名位于单个位置。当然,这是行不通的,因为 foo
不能像这样在 lambda 中使用。
如果我使用公共基类,我就可以使这个解决方案发挥作用。但是,在这种情况下,我需要在每个包装函数中进行类型转换,并且必须定义函数签名两次。
struct wrap_base
{
void* target;
};
template <class F>
struct wrap : wrap_base
{
wrap(F&& f)
{
target = magic();
}
};
static wrap_base foo = wrap(
[](int data) -> int
{
using func = int(int);
return reinterpret_cast<func*>(foo.target)(data) + 10;
});
即使使用
void*
,函数指针也无法可移植地转换为 reinterpret_cast
。来自 expr.reinterpret.cast:
有条件地支持将函数指针转换为对象指针类型,反之亦然。这种转换的含义是实现定义的,除了如果实现支持双向转换,则将一种类型的纯右值转换为另一种类型并返回(可能具有不同的 cv 限定)将产生原始指针值。