Kon*_*lph 14 c++ macos templates undefined-reference gcc4
我正在使用C库(来自C++),它提供以下接口:
void register_callback(void* f, void* data);
void invoke_callback();
Run Code Online (Sandbox Code Playgroud)
现在,我需要注册一个函数模板作为回调,这导致我的问题.请考虑以下代码:
template <typename T> void my_callback(void* data) { … }
int main() {
int ft = 42;
register_callback(reinterpret_cast<void*>(&my_callback<int>), &ft);
invoke_callback();
}
Run Code Online (Sandbox Code Playgroud)
这给了我以下链接器错误(在OS X上使用g ++(GCC)4.5.1,但适用于编译器版本/平台的大多数其他组合):
架构x86_64的未定义符号:
Run Code Online (Sandbox Code Playgroud)"void my_callback<int>(void*)", referenced from: _main in ccYLXc5w.o
我觉得这是可以理解的.
通过显式实例化模板可以轻松解决此问题:
template void my_callback<int>(void* data);
Run Code Online (Sandbox Code Playgroud)
不幸的是,这不适用于我的真实代码,因为回调是在函数模板中注册的,我不知道这个函数将被调用哪个模板参数集,因此我不能为所有的函数提供显式实例化.他们(我正在编写一个图书馆).所以我的真实代码看起来有点像这样:
template <typename T>
void do_register_callback(T& value) {
register_callback(reinterpret_cast<void*>(my_callback<T>), &value);
// Other things …
}
int main() {
int ft = 42;
do_register_callback(ft);
invoke_callback();
}
Run Code Online (Sandbox Code Playgroud)
通过调用函数隐式实例化函数模板.所以我们这样做,但要确保调用没有实际执行(该函数有副作用):
template <typename T>
void do_register_callback(T& value) {
if (false) { my_callback<T>(0); }
register_callback(reinterpret_cast<void*>(my_callback<T>), &value);
}
Run Code Online (Sandbox Code Playgroud)
这似乎有效,即使启用了优化(以便编译器删除死分支).但是我不确定这一天是不是有一天会崩溃.我还发现这是一个非常难看的解决方案,需要一个长度的解释性评论,以免未来的维护者删除这个显然不必要的代码.
如何实例化我不知道模板参数的模板?这个问题显然是胡说八道:我做不到. - 但是这周围有一种偷偷摸摸的方式吗?
除此之外,我的解决方法是否能保证成功?
代码(具体来说,我向其转换函数指针的事实void*)也会产生以下警告:
ISO C++禁止在指向函数的指针和指向对象的指针之间进行转换
编译时-pedantic.我可以以某种方式摆脱警告,而不是为库编写一个强类型的C包装器(在我的情况下这是不可能的)?
在ideone上运行代码(添加了强制转换以使其编译)
显然,真正的问题是static_cast我的原始代码中缺少:
register_callback(reinterpret_cast<void*>(&my_callback<int>), &ft);
Run Code Online (Sandbox Code Playgroud)
该编译罚款,但使用GCC 4.5时触发类似器错误.它在使用GCC 4.2时甚至不编译,而是给出以下编译错误:
没有足够的上下文信息来确定类型
一旦提供了这个"上下文信息",代码就会编译并链接:
register_callback(reinterpret_cast<void*>(
static_cast<void(*)(void*)>(my_callback<int>)), &value);
Run Code Online (Sandbox Code Playgroud)
我不知道演员是否真的需要和(如果是的话)为什么GCC 4.5允许我离开它,然后无法实例化模板.但至少我得到的代码是编译而不诉诸黑客.
这应该工作:
template <typename T>
void do_register_callback(T& value) {
void (*callback)(void*) = my_callback<T>;
register_callback(reinterpret_cast<void*>(callback), &value);
}
Run Code Online (Sandbox Code Playgroud)
第一行强制编译器实例化该函数以生成地址 - 然后您可以愉快地传递该地址.
编辑:让我再次选择这个组合.制作my_callback一个模板类的静态成员-类似如下:
template <typename T>
struct foo
{
static void my_callback(void* data) {
T& x = *static_cast<T*>(data);
std:: cout << "Call[T] with " << x << std::endl;
}
};
Run Code Online (Sandbox Code Playgroud)
现在,在你的注册人中,你甚至不需要"演员".
template <typename T>
void do_register_callback(T& value) {
register_callback(reinterpret_cast<void*>(&foo<int>::my_callback), &value);
}
Run Code Online (Sandbox Code Playgroud)
看来实例化类模板的规则与函数模板不同 - 即,为了获取类成员的地址,该类型被实例化.
POSIX 建议使用以下方法在函数指针类型和对象指针类型之间进行转换(在 C99 中未定义):
typedef void function_type(void*);
function_type *p_to_function = &my_callback<T>;
void* p_to_data = *(void**)&p_to_function;
// Undefined:
// void* p_to_data = (void*)p_to_function;
Run Code Online (Sandbox Code Playgroud)
请注意,在 C++ 领域,这将执行reinterpret_cast<void**>(&p_to_function)from function_type**。这不是未定义的,而是实现定义的,与reinterpret_cast<void*>(p_to_function). 因此,编写依赖于实现的符合 C++ 的代码可能是您的最佳选择。
| 归档时间: |
|
| 查看次数: |
2705 次 |
| 最近记录: |