如何强制g++使用内联函数

How to force g++ to inline functions?

本文关键字:函数 何强制 g++      更新时间:2023-10-16

我最近在使用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.ogrep作为工具被冒犯的符号时,您应该看到它们使用不同于正常符号(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为指令数