Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

extern "C" inline functions

Will this code result in undefined behavior?

header.h

#ifdef __cplusplus
extern "C"
{
#endif

inline int foo(int a)
{
    return a * 2;
}

#ifdef __cplusplus
}
#endif

def.c

#include "header.h"

extern inline int foo(int a);

use.c

#include "header.h"

int bar(int a)
{
    return foo(a + 3);
}

main.cpp

#include <stdio.h>
#include "header.h"

extern "C"
{
    int bar(int a);
}

int main(int argc, char** argv)
{
    printf("%d\n", foo(argc));
    printf("%d\n", bar(argc));
}

This is a example of a program where an inline function has to be used in both C and C++. Would it work if def.c was removed and foo was not used in C? (This is assuming that the C compiler is C99.)

This code works when compiled with:

gcc -std=c99 -pedantic -Wall -Wextra -c -o def.o def.c
g++ -std=c++11 -pedantic -Wall -Wextra -c -o main.o main.cpp
gcc -std=c99 -pedantic -Wall -Wextra -c -o use.o use.c
g++ -std=c++11 -pedantic -Wall -Wextra -o extern_C_inline def.o main.o use.o

foo is only in extern_C_inline once because the different versions that the compiler outputs in different object files get merged, but I would like to know if this behavior is specified by the standard. If I remove extern definition of foo and make it static then foo will appear in the extern_C_inline multiple times because the compiler outputs it in each compilation unit.

like image 478
qbt937 Avatar asked Oct 28 '14 23:10

qbt937


People also ask

Can we extern inline function in C?

Similarly, if you define a function as extern inline , or redeclare an inline function as extern , the function simply becomes a regular, external function and is not inlined. End of C only. Beginning of C++ only.

What is extern inline?

extern inline will not generate an out-of-line version, but might call one (which you therefore must define in some other compilation unit. The one-definition rule applies, though; the out-of-line version must have the same code as the inline offered here, in case the compiler calls that instead.

Does C support inline functions?

Standard supportC++ and C99, but not its predecessors K&R C and C89, have support for inline functions, though with different semantics. In both cases, inline does not force inlining; the compiler is free to choose not to inline the function at all, or only in some cases.

Can inline function return value?

Inline functions are more than a simple copy-paste procedure (in contrast to, say, preprocessor macros). They behave like normal functions, so any return value would be reflected to the caller just like a normal function.


1 Answers

The program is valid as written, but def.c is required to ensure the code always works with all compilers and any combination of optimisation levels for the different files.

Because there is a declaration with extern on it, def.c provides an external definition of the function foo(), which you can confirm with nm:

$ nm def.o
0000000000000000 T foo

That definition will always be present in def.o no matter how that file is compiled.

In use.c there is an inline definition of foo(), but according to 6.7.4 in the C standard it is unspecified whether the call to foo() uses that inline definition or uses an external definition (in practice whether it uses the inline definition depends on whether the file is optimised or not). If the compiler chooses to use the inline definition it will work. If it chooses not to use the inline definition (e.g. because it is compiled without optimisations) then you need an external definition in some other file.

Without optimisation use.o has an undefined reference:

$ gcc -std=c99 -pedantic -Wall -Wextra -c -o use.o use.c
$ nm use.o
0000000000000000 T bar
                 U foo

But with optimisation it doesn't:

$ gcc -std=c99 -pedantic -Wall -Wextra -c -o use.o use.c -O3
$ nm use.o
0000000000000000 T bar

In main.cpp there will be a definition of foo() but it will typically generate a weak symbol, so it might not be kept by the linker if another definition is found in another object. If the weak symbol exists, it can satisfy any possible reference in use.o that requires an external definition, but if the compiler inlines foo() in main.o then it might not emit any definition of foo() in main.o, and so the definition in def.o would still be needed to satisfy use.o

Without optimisation main.o contains a weak symbol:

$ g++ -std=c++11 -pedantic -Wall -Wextra -c -o main.o main.cpp
$ nm main.o
                 U bar
0000000000000000 W foo
0000000000000000 T main
                 U printf

However compiling main.cpp with -O3 inlines the call to foo and the compiler does not emit any symbol for it:

$ g++ -std=c++11 -pedantic -Wall -Wextra -c -o main.o main.cpp -O3
$ nm main.o
                 U bar
0000000000000000 T main
                 U printf

So if foo() is not inlined in use.o but is inlined in main.o then you need the external definition in def.o

Would it work if def.c was removed and foo was not used in C?

Yes. If foo is only used in the C++ file then you do not need the external definition of foo in def.o because main.o either contains its own (weak) definition or will inline the function. The definition in foo.o is only needed to satisfy non-inlined calls to foo from other C code.


Aside: the C++ compiler is allowed to skip generating any symbol for foo when optimising main.o because the C++ standard says that a function declared inline in one translation unit must be declared inline in all translation units, and to call a function declared inline the definition must be available in the same file as the call. That means the compiler knows that if some other file wants to call foo() then that other file must contain the definition of foo(), and so when that other file is compiled the compiler will be able to generate another weak symbol definition of the function (or inline it) as needed. So there is no need to output foo in main.o if all the calls in main.o have been inlined.

These are differnet semantics from C, where the inline definition in use.c might be ignored by the compiler, and the external definition in def.o must exist even if nothing in def.c calls it.

like image 81
Jonathan Wakely Avatar answered Oct 19 '22 09:10

Jonathan Wakely