views:

145

answers:

4

If a header file contains a function definition it can be inlined by the compiler. If the function is exported, the function's name and implementation must also be made available to clients during linkage. How does a compiler achieve this? Does it both inline the function and provide an implementation for external callers?

Consider Foo.h:

class Foo
{
    int bar() { return 1; }
};

Foo::bar may be inlined or not in library foo.so. If another piece of code includes Foo.h does it always create its own copy of Foo::bar, whether inlined or not?

A: 

It usually means that it ends up creating a separate inlined method for every obj file that uses it at link time. It can also fail or refuse to inline many things, so this can cause a problem because you can wind up with bloated objs without getting the performance benefitting of inlining. The same thing can happen with virtual method inlining so it can be worth forcing inining and setting warning for inline failure (about the only useful warning message compilers give).

Charles Eli Cheese
The standard requires that an extern inline function have the same address in all translation units, ie no separate copies in different obj files.
Potatoswatter
The fact that the result of the *address-of* operator returns the same address in all translation units does not mean that the function cannot be inlined. In fact the linker can inline functions that were not declared `inline` and the address-of operator should still work and report the same (single) address in all translation units.
David Rodríguez - dribeas
I am not 100% sure to be honest when it is done inlining, but I do know you can wind up with inlines that are separate copies for each obj is some circumstances, especially when virtual inlines fail.
Charles Eli Cheese
A: 

inlined functions do not exist in the compiled binary: that is because they are taken and placed directly at the call site (so called IN-LINE). Each usage of the inlined function results in the complete code to be pulled in at that place.

So inlined functions cannot be exported because they do not exist. But you can still use them if you have a definition in one header. And yes, you MUST provide a definition for an inlined function, otherwise you cannot use it.

If you managed to export an inlined function then it is sure that it is not inline anymore: inline is not a strict semantic element. Depending on the compiler and compiler settings, one compiler might choose to inline, another not, sometimes provide a warning, sometimes even an error (which personnally I would prefer being the default behaviour, because it shows up the places where unintended things occur)

jdehaan
Of course inline functions exist. You can take the address of one, and `extern inline` is perfectly valid and well-defined. See my answer.
Potatoswatter
-1 Misleading; if an inline function is exported from a DLL, for instance, the compiler _will_ produce an out-of-line version of it.
Martin B
@Martin, that is actually what I meant with "If you managed to export an inlined function then it is sure that it is not inline anymore"! :-)
jdehaan
@Potatoswatter, I think that what the standard defines really smells because it might do either the one or the other depending on what happens in one specific compilation unit. Maybe it is just a matter of definition of what you call inline. For me if the compiler generates a function then it is not inline anymore.
jdehaan
@jdehaan: It's still inline after you export it. It's inline in every translation unit where it's declared inline, and it must be declared inline in every translation unit. I'm not sure what the standard intends for DLL's, whether they're considered more translation units for the same program, but you can certainly end up with an `extern` function in your binary that never directly gets called.
Potatoswatter
@jdehaan: Dead functions are stripped by the linker. If all calls to a function are inlined, and it's not exported, it will be erased from the binary. Does that make it inline "for you"?
Potatoswatter
@Potatoswatter, gosh, now I got it. I would even downrank myself :-D. I only saw half of the problem, I would tend to delete my post. I still dislike this duality but now understand better why this functionality is provided (avoid mismatching implementations and code duplication). Thanks, Regards.
jdehaan
A: 

By export, I'm guessing you mean something such as getting a pointer to the function and later calling the function through the pointer.

Yes, in that case, the compiler will generate a regular function so that it can be invoked from a pointer.

One way to do this is with a link-once section. The idea is that in translation unit gets the code in a special type of section that has a name based on the function name. During linking, the linker will only keep one instance of identically named link-once sections.

R Samuel Klatchko
+2  A: 

Header files are just copy-pasted into the source file — that's all #include does. A function is only inline if declared using that keyword or if defined inside the class definition, and inline is only a hint; it doesn't force the compiler to produce different code or prohibit you from doing anything you could otherwise do.

You can still take the address of an inline function, or equivalently, as you mention, export it. For those uses, the compiler simply treats it as non-inline and uses a One Definition Rule (the rule which says the user can't apply two definitions to the same function, class, etc) to "ensure" the function is defined once and only one copy is exported. Normally you are only allowed to have one definition among all sources; an inline function must have one definition which is repeated exactly in each source it is used.

Here is what the standard has to say about inline extern functions (7.1.2/4):

An inline function shall be defined in every translation unit in which it is used and shall have exactly the same definition in every case (3.2). [Note: a call to the inline function may be encountered before its defi- nition appears in the translation unit. ] If a function with external linkage is declared inline in one transla- tion unit, it shall be declared inline in all translation units in which it appears; no diagnostic is required. An inline function with external linkage shall have the same address in all translation units. A static local variable in an extern inline function always refers to the same object. A string literal in an extern inline function is the same object in different translation units.

Potatoswatter
I was reading that some time ago, and I did not conclude that the compiler will ignore the `inline` keyword. It can still inline the code in all call places but generate the non-inlined version and use that for the address-of. That was, of course, just a personal conclusion and I am not sure whether it holds true in any compiler. One other thing is that I know of no compiler that actually enforces de ODR, they mostly assume it --the linker will remove all but one of the definitions and assume that all others were exactly the same.
David Rodríguez - dribeas
BTW, the *inline* keyword does make the compiler behave differently, quoting 7.1.2 [dcl.fct.spec]/2, at the end of the paragraph: 'An implementation is not required to perform this inline substitution at the place of call; however, even if this inline substitution is omitted, the other rules for inline functions defined in 7.2.1 shall still be respected.', which basically refer to what you already quoted - the function will be defined in all translation units and that will not be a violation of the ODR. With templates and inlined functions the ODR rules are different than for regular functions
David Rodríguez - dribeas
@David: I doubt any compiler will ignore `inline` outright, but it's discretionary whether an `inline` is called normally or a locally-defined `extern` is inlined. The only thing 7.1.2 actually requires is the alternative ODR allowing multiple identical definitions. Addresses of inline functions are quite common so I imagine most compilers are 100% compliant on this issue. But that's just recapping what I already said… do you disagree or have a question… ?
Potatoswatter
I was just trying to clarify that while the function can be non-inlined, the compiler cannot (literally) 'ignore the inline and use the One Definition Rule' -- the ODR is different for 'inline'd functions than for non-inlined, so I find that sentence a little misleading. In particular, the compiler will generate many definitions (the standard requires it) of the inlined function and then discard all but one in the link step, and I don't know of any compiler that actually enforces the rule (gcc was working on it) that all definitions of the inline/template are the same. +1 anyway
David Rodríguez - dribeas