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?
There is no guarantee that functions will be inlined. You cannot force the compiler to inline a particular function, even with the __forceinline keyword. When compiling with /clr, the compiler will not inline a function if there are security attributes applied to the function.
A class's member functions can be declared inline, either by using the inline keyword or by placing the function definition within the class definition. The __inline keyword is equivalent to inline. Even with __forceinline, the compiler can't inline code in all circumstances. The compiler can't inline a function if:
When the inline function is called whole code of the inline function gets inserted or substituted at the point of inline function call. This substitution is performed by the C++ compiler at compile time. Inline function may increase efficiency if it is small. The syntax for defining the function inline is:
For more information on using the inline specifier, see: Inline functions are best used for small functions such as accessing private data members. The main purpose of these one- or two-line "accessor" functions is to return state information about objects. Short functions are sensitive to the overhead of function calls.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With