I have recently encountered an issue with C++ inline functions when using Haskell FFI to C/C++.
Namely, g++ does not really inline functions that are declared inline
, and generate symbols for them. Ultimately, this generates linker error when ghci tries to load an object file which calls the inline function:
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'
Here, _ZN5Eigen8internal19throw_std_bad_allocEv
is an inline
function in the header-only Eigen C++ library somehow being treated as a real function and given a linker symbol. solveeq.o
is my object file which makes (indirect) calls to that function. The environment is Ubuntu 12.04 64bit, ghc 7.4.1.
The issue is this: I can use extern "C"
to prevent C++ decoration of functions names for my own functions. But I cannot/shouldn't change the C++ headers defined by others (for obvious reasons). In my opinion, the compiler shouldn't create a function for this inline definition in the first place to cause this error. The reason is simple. If the function in question is truly inlined, I wouldn't get an linker error. If the compiler gets smart and decides to create a real function for it, I get errors like this (or multiple definitions of the same function, as I read elsewhere). So now, the correctness of compilation/linking depends on the mood of the compiler.
Also, I think linker issues like this practically defeats header-only C++ libraries (which are appealing for their portability) because now they can not be exported using extern "C"
.
Is this a design problem of c++ or is this just a g++ issue? My question is, is there a way to prevent c++ compilers or g++ from not inlining inline functions? For example, is there a command line option for this? (Modifying source codes is out of question as they are library codes.)
Also, I am curious how does the C++ STL deal with this issue. Are they also headers-only?
Functions being inline
is a hint to the compilers which the compiler will happily ignore whenever it sees fit. Some compiler do warning about functions declared inline
but not being inline, e.g. gcc's -Winline
, but disabling all inline functions is bound to be non-workable: there are some rather sizable functions dealing with data structures defined as templates. All of the template code which is implicitly may be inline and instantiations of these template may be emitted in multiple translation units.
Instead of trying to shoehorn C++'s behavior for the purpose of integrating it with some other tool the other tool should smarten up and ignore symbols it isn't interested in. In particular, if it isn't really interested in unwanted instantiations of inline
functions, it should just ignore these. Since they can appear in multiple translation units they normally use weak symbols. When you use nm -po your-object-file.o
on a UNIX system and grep
for the symbols your tool is offended by you should see them tagged using something different than normal symbols (T
).
Another approach could consist in linking a shared object which only exposes the symbols you actually want to expose and use this object with the tool. That said, although I know that this approach is doable, I haven't really used it myself.
As another answer already points out, if the compiler decides not to inline a function or a method, the compiler is going to generate it, and put it into the object code file. This does not result in an error. The fact that, supposedly, the compiler decided not to inline particular function is not your problem here.
I've seen the error you're seeing from the linker before, and it is a very misleading error message. What it's really telling you is that you forgot to include a header file with a template definition. Say you have:
widget_decl_fwd.H:
template<typename C> void widget(C c_arg);
And then:
widget_decl_impl.H:
template<typename C> void widget(C c_arg)
{
// Whatever widget() needs to do, here
}
Then, you innocently go along and write:
widget_factory.C:
#include "widget_decl_fwd.H"
void make_widgets(int n)
{
widget(n);
}
Then you try to compile this. It'll compile fine, but fail to link. The compiler saw the declaration of the template function, but not its definition. So, the compiler made the call to widget() an external symbol reference. If it just so happens that some other module that gets linked together pulled in the declaration, instantiated the template function, and exported the right symbol, the link might just succeed, but it will likely to fail due to an unresolved symbol.
Inline function are similar to templates, in this context. So, the linker is complaining about an unresolved symbol from solveeq.C
That means that you need to check what solveeq.C
includes, and verify that it's actually pulling in the actual definition of this inline function or a template, and not just its declaration, from somewhere. If the header inclusions are confusing, use the -E option, and grep the output.
GCC is not designed to do that and it's better to fix the root cause of the problem rather than trying to shoehorn a tool into something it is not supposed to do.
Inability to load weak symbols is a long-standing GHCi bug. It is fixed in the 7.8.1 release (I have checked 7.8.3). Just upgrade your GHC and the problem goes away.
If you are unable to upgrade GHC, you can build a shared library and tell GCC to hide inline functions. This is done with the -fvisibility-inlines-hidden
flag. I have checked with GHCi 7.0.1 that this method indeed works. You may have to specify an absolute path to your shared object (i.e. ghci /full/path/to/your.so
), or place the shared object at some place where the dynamic loader can find it. Relative paths don't work for some reason.
If you don't want to inline functions, there is option
-fno-inline
"Do not expand any functions inline apart from those marked with the always_inline attribute. This is the default when not optimizing"
THere is also more control over inlining function size
-finline-limit=n
where n
is number of instructions