如何强制g ++内联函数?

时间:2013-06-08 09:03:29

标签: c++ haskell inline ffi ghci

在使用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是仅限标题的Eigen C ++库中的inline函数,不知何故被视为实函数并给出了链接符号。 solveeq.o是我的目标文件,它对该函数进行(间接)调用。环境是Ubuntu 12.04 64bit,ghc 7.4.1。

问题是这样的:我可以使用extern "C"来防止C ++为我自己的函数修饰函数名。但我不能/不应该更改其他人定义的C ++标头(原因很明显)。在我看来,编译器不应该首先为此内联定义创建一个函数来导致此错误。原因很简单。如果有问题的函数是真正内联的,我不会得到链接器错误。如果编译器变得聪明并决定为它创建一个真正的函数,我会得到这样的错误(或者我在其他地方读到的同一函数的多个定义)。所以现在,编译/链接的正确性取决于编译器的情绪。

另外,我认为像这样的链接器问题实际上会破坏仅限标头的C ++库(这对它们的可移植性很有吸引力),因为现在无法使用extern "C"导出它们。

这是c ++的设计问题还是仅仅是一个g ++问题?我的问题是,有没有办法阻止c ++编译器或g ++不内联内联函数?例如,是否有一个命令行选项? (修改源代码是不可能的,因为它们是库代码。)

另外,我很好奇C ++ STL是如何处理这个问题的。它们也只是标题吗?

4 个答案:

答案 0 :(得分:3)

作为inline的函数是编译器的提示,只要编译器认为合适,编译器就会很乐意忽略它们。某些编译器会对声明为inline但未内联的函数发出警告,例如gcc-Winline,但禁用所有内联函数必然是不可行的:有一些相当大的函数处理定义为模板的数据结构。隐式地可以是内联的所有模板代码和这些模板的实例化可以在多个翻译单元中发出。

为了将C ++的行为与其他工具集成在一起,而不是试图将C ++的行为扼杀,而不是其他工具应该智能化并忽略它不感兴趣的符号。特别是,如果它不是真的对不想要的实例化感兴趣inline函数,它应该忽略这些。由于它们可以出现在多个翻译单元中,因此通常使用弱符号。当您在UNIX系统上使用nm -po your-object-file.ogrep使用符号时,您的工具会被冒犯,您应该看到它们使用与普通符号不同的标记(T)。

另一种方法可能包括链接共享对象,该对象仅公开您实际想要公开的符号,并将该对象与该工具一起使用。也就是说,虽然我知道这种方法是可行的,但我自己并没有真正使用它。

答案 1 :(得分:2)

另一个答案已经指出,如果编译器决定不内联函数或方法,编译器将生成它,并将其放入目标代码文件中。这不会导致错误。据推测,编译器决定不内联特定功能的事实不是你的问题。

我之前看到过您从链接器看到的错误,这是一个非常误导性的错误消息。它真正告诉你的是你忘了包含带有模板定义的头文件。说你有:

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输出。

答案 2 :(得分:2)

海湾合作委员会不是为了做到这一点而设计的,而是最好找出问题的根本原因,而不是试图将工具变成一种不应该做的东西。

无法加载弱符号long-standing GHCi bug。它在7.8.1版本中得到修复(我已经检查过7.8.3)。只需升级您的GHC,问题就会消失。

如果您无法升级GHC,您可以构建一个共享库并告诉GCC隐藏内联函数。这是通过-fvisibility-inlines-hidden标志完成的。我已经用GHCi 7.0.1检查过这种方法确实有效。您可能必须指定共享对象的绝对路径(即ghci /full/path/to/your.so),或将共享对象放在动态加载程序可以找到它的某个位置。相对路径由于某种原因不起作用。

答案 3 :(得分:1)

如果您不想内联函数,可以选择

-fno-inline

“除了标有always_inline属性的内容之外,不要扩展任何内联函数。这是默认情况下不优化”

这也是对内联函数大小的更多控制

-finline-limit=n

其中n是指令数