如何强制g++使用内联函数
How to force g++ to inline functions?
我最近在使用Haskell FFI到C/c++时遇到了c++内联函数的问题。也就是说,g++并没有真正内联声明为inline
的函数,并为它们生成符号。最终,当ghci试图加载调用内联函数
Loading object (static) solveeq.o ... done
Loading object (dynamic) /usr/lib/gcc/x86_64-linux-gnu/4.6/libstdc++.so ... done
final link ... ghc: solveeq.o: unknown symbol `_ZN5Eigen8internal19throw_std_bad_allocEv'
在这里,_ZN5Eigen8internal19throw_std_bad_allocEv
是一个inline
函数在头文件专用的Eigen c++库中,以某种方式被视为一个实函数并给定一个链接器符号。solveeq.o
是我的对象文件,它(间接)调用该函数。环境是Ubuntu 12.04 64位,ghc 7.4.1.
问题是这样的:我可以使用extern "C"
来防止c++为我自己的函数修饰函数名。但是我不能/不应该改变别人定义的c++头文件(原因很明显)。在我看来,编译器不应该首先为这个内联定义创建一个函数来导致这个错误。原因很简单。如果有问题的函数是真正内联的,我就不会得到链接器错误。如果编译器变得聪明并决定为它创建一个真正的函数,我会得到这样的错误(或者同一函数的多个定义,正如我在其他地方看到的那样)。所以现在,编译/链接的正确性取决于编译器的心情。
而且,我认为像这样的链接器问题实际上挫败了仅头文件的c++库(这些库因其可移植性而吸引人),因为现在它们不能使用extern "C"
导出。
这是c++的设计问题还是c++的问题?我的问题是,有没有一种方法可以防止c++编译器或g++不内联内联函数?例如,是否有一个命令行选项?(修改源代码是不可能的,因为它们是库代码。)
另外,我很好奇c++ STL是如何处理这个问题的。它们也是头文件吗?
函数为inline
是对编译器的一个提示,编译器在它认为合适的时候会很高兴地忽略它。有些编译器会警告声明inline
但不是内联的函数,例如gcc的-Winline
,但禁用所有内联函数必然是不可行的:有一些相当大的函数处理定义为模板的数据结构。所有隐式的模板代码都可以内联,并且这些模板的实例化可以在多个翻译单元中发出。
而不是试图硬塞c++的行为,目的是与其他工具集成,其他工具应该聪明起来,忽略它不感兴趣的符号。特别是,如果它对不需要的inline
函数的实例化不感兴趣,它应该忽略这些。由于它们可以出现在多个翻译单元中,因此通常使用弱符号。当您在UNIX系统上使用nm -po your-object-file.o
和grep
作为工具被冒犯的符号时,您应该看到它们使用不同于正常符号(T
)的标记。
另一种方法可能包括链接一个共享对象,该对象只公开您实际想要公开的符号,并将该对象与工具一起使用。也就是说,虽然我知道这种方法是可行的,但我自己并没有真正使用过。
另一个答案已经指出,如果编译器决定不内联函数或方法,编译器将生成它,并将其放入目标代码文件中。这不会导致错误。事实上,假设编译器决定不内联特定的函数,这不是你的问题。
我以前看到过你从链接器中看到的错误,这是一个非常具有误导性的错误信息。它真正告诉您的是,您忘记了在模板定义中包含头文件。假设你有:
widget_decl_fwd.H:
template<typename C> void widget(C c_arg);
然后:
widget_decl_impl.H:
template<typename C> void widget(C c_arg)
{
// Whatever widget() needs to do, here
}
然后,你天真地继续写:
widget_factory.C:
#include "widget_decl_fwd.H"
void make_widgets(int n)
{
widget(n);
}
然后尝试编译这个。它可以很好地编译,但是无法链接。编译器看到了模板函数的声明,但没有看到它的定义。因此,编译器将对widget()的调用作为外部符号引用。如果碰巧链接在一起的其他模块在声明中被拉入,实例化了模板函数,并导出了正确的符号,那么链接可能会成功,但很可能会因为未解析的符号而失败。
在这种情况下,内联函数类似于模板。所以,链接器抱怨solveeq.C
这意味着您需要检查solveeq.C
包含什么,并验证它实际上是从某个地方拉入这个内联函数或模板的实际定义,而不仅仅是它的声明。如果头包含内容令人困惑,请使用-E选项,并对输出进行grep。
GCC不是设计来做这些的,最好是解决问题的根本原因,而不是试图把一个工具硬塞进它不应该做的事情。
无法加载弱符号是一个长期存在的GHCi错误。它在7.8.1版本中被修复了(我已经检查了7.8.3)。只要升级你的GHC,问题就解决了。
如果你无法升级GHC,你可以构建一个共享库并告诉GCC隐藏内联函数。这是用-fvisibility-inlines-hidden
标志完成的。我已经检查了GHCi 7.0.1,这种方法确实有效。您可能必须为共享对象指定一个绝对路径(即ghci /full/path/to/your.so
),或者将共享对象放在动态加载器可以找到它的地方。由于某些原因,相对路径不起作用。
如果不想内联函数,可以选择
-fno-inline
"不要展开任何内联函数,除了那些标有always_inline属性的函数。这是不优化
时的默认值。对于内联函数的大小也有更多的控制
-finline-limit=n
其中n
为指令数
- 函数何时会在c++中包含stack_Unwind_Resume调用
- 如何修复函数样式强制转换或类型构造的预期"("?
- enable_if转换构造函数(静态强制转换,is_base_of)
- 为什么或何时应在调用之前将可调用函数参数强制转换为右值?
- 是否可以合法地将成员函数指针强制转换为函数指针
- 确定是调用构造函数还是强制转换运算符的因素
- 是否有必要在模板化函数中强制转换文本常量?
- 调用基类函数时强制使用类名?
- 如何在 constexpr 函数中强制编译错误,而不是让它衰减到非 constexpr 上下文中?
- 在C++中,是否允许将函数指针强制转换为将指向基类或派生类的指针作为参数获取的函数指针
- C++显式构造函数和强制转换
- 将成员函数指针强制转换为另一个类中的一个然后再返回是否安全
- C++模板函数中强制实施第二遍名称查找
- 将 std 函数对象强制转换回函子结构
- 编写默认构造函数会强制零初始化
- C++函数指针强制转换
- C++ - 为什么常量函数不强制成员指针上的恒常性?
- 如何在c++中将函数输入强制转换为智能指针
- 用于函数样式强制转换或类型构造的预期 c++ Xcode '('
- 函数样式强制转换或构造类型 Xcode 错误的'('