Consider the following C++-code with an inline
-function:
// this function is in a header-file:
// recursion prevents inlining
inline int calc(int k){
if(k<=1) return 1;
return calc(k-1)+calc(k-2);
}
// this function is in a source-file:
int fun(int k){
return calc(k);
}
Here I use recursion, to simulate the case when compiler was not able to inline the function calc
.
The resulting assembly (compiled with -Os
, see live on https://godbolt.org/):
...
.weak calc(int)
.type calc(int), @function
calc(int):
// some assembler
.text
.globl fun(int)
.type fun(int), @function
fun(int):
...
jmp calc(int)
As expected, the compiler was not able to inline calc
, and thus emitted code for it, but due to inline
-keyword it becomes a weak symbol.
Compiling the same code as C, produces a different result (with -Os
,see live on https://godbolt.org/):
.Ltext0:
.globl fun
.type fun, @function
fun:
...
jmp calc
The most notable difference: there is no code emitted for calc
, so basically the linker will not able to link the executable, as the definition of calc
is missing.
Obviously, inline
means different things in C compared to C++.
What are differences for inline
in C compared to C++? How the definition of calc
should be done, that it can be used in a header in C and C++ at the same time?
While the inline
-keyword has the same goal in C and C++: to provide the definition of a function at compile time in multiple translation units, these languages use different methods to cope with its fallout - multiple definitions of the same symbol in different translation units.
When the C- or C++-compiler really inlines the inline
-function in a translation unit, its definition is no longer needed and no corresponding symbol is emitted. As for example here:
inline int fun(){return 2;}
int doit(){
return fun();
}
the resulting assembler is (live)
doit():
movl $2, %eax
ret
However, when the function don't get inlined, the C++-standard mandates among other things the following:
This more or less describes a weak symbol - and no surprise, C++-compiler emits a weak symbol in the OP's code - it has to, because the C++-standard doesn't guarantee, that this symbol will be defined in another translation unit.
On the other hand, the C-standard gurantees, that the definition of the inline-function must be provided in another translation unit(i.e. has external linkage) and thus (in order to avoid multiple definition of the same symbol) nothing is emited for the inline function - it is just called.
Thus, there must be a c-file, in which the function is "instantiated" via
inline int calc(int k)
This doesn't work well with the idea of having a header-only library. An alternative is, the use internal linkage via static
-keyword, i.e.
static inline int calc(int k){
if(k<=1) return 1;
return calc(k-1)+calc(k-2);
}
This will emit a local symbol in every translation unit (if not inlined), independent of whether compiled as C or C++. That means, there are multipe definitions (i.e. in every translation object the function has a different address) of the same function, which will possible lead to a bigger executable, and is a setback for compared to C++-default behavior.
An alternative is to use the following definiton of inline
:
#ifdef __cplusplus
#define INLINE inline
#else
#define INLINE static inline
#endif
and use as
INLINE int calc(int k){
...
}
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