What does extern inline do?
Asked Answered
W

7

117

I understand that inline by itself is a suggestion to the compiler, and at its discretion it may or may not inline the function, and it will also produce linkable object code.

I think that static inline does the same (may or may not inline) but will not produce linkable object code when inlined (since no other module could link to it).

Where does extern inline fit into the picture?

Assume I want to replace a preprocessor macro by an inline function and require that this function gets inlined (e.g., because it uses the __FILE__ and __LINE__ macros which should resolve for the caller but not this called function). That is, I want to see a compiler or linker error in case the function does not get inlined. Does extern inline do this? (I assume that, if it does not, there is no way to achieve this behavior other than sticking with a macro.)

Are there differences between C++ and C?

Are there differences between different compiler vendors and versions?

Whicker answered 19/10, 2008 at 15:9 Comment(2)
If you are actually using C++20 then there is the std::source_location struct.Grenadier
Is "inline" without "static" or "extern" ever useful in C99? shows the actual syntax necessary in the .h and the extern inline declaration in exactly one .c to instantiate a non-inline definition and make a working C program with a non-static inline function defined in a header and thus able to actually inline, but still link if it doesn't. In C, should inline functions in headers be externed in the .c file? is also about that, with a concise answer.Adamite
M
158

in K&R C or C89, inline was not part of the language. Many compilers implemented it as an extension, but there were no defined semantics regarding how it worked. GCC was among the first to implement inlining, and introduced the inline, static inline, and extern inline constructs; most pre-C99 compiler generally follow its lead.

GNU89:

  • inline: the function may be inlined (it's just a hint though). An out-of-line version is always emitted and externally visible. Hence you can only have such an inline defined in one compilation unit, and every other one needs to see it as an out-of-line function (or you'll get duplicate symbols at link time).
  • 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.
  • static inline will not generate a externally visible out-of-line version, though it might generate a file static one. The one-definition rule does not apply, since there is never an emitted external symbol nor a call to one.

C99 (or GNU99):

  • inline: like GNU89 "extern inline"; no externally visible function is emitted, but one might be called and so must exist (compilation might occasionally succeed depending on compiler options).
  • extern inline: like GNU89 "inline": externally visible code is emitted, so at most one translation unit can use this.
  • static inline: like GNU89 "static inline". This is the only portable one between gnu89 and c99

C++:

A function that is inline anywhere must be inline everywhere, with the same (full) definition. The compiler/linker will sort out multiple instances of the symbol. There is no definition of static inline or extern inline, though many compilers have them (typically following the gnu89 model). Some compilers will omit exporting the symbol if it isn't used locally as an optimization.

Minivet answered 19/10, 2008 at 15:35 Comment(10)
In Classic classic C, 'inline' was not a keyword; it was available for use as a variable name. This would apply to C89 and pre-standard (K&R) C.Jiggle
You're correct, it seems. Fixed. I thought it had been reserved as a keyword in C89 (though not in K&R), but I seems I misrememberedMinivet
I'd like to add that for Microsoft's Visual C++, there's a __forceinline keyword that will enforce your function getting inlined. This is obviously a compiler-specific extension only for VC++.Complemental
Is there any difference between C99 "extern inline" and no specifiers at all?Koenraad
Semantically no; just like a non-inline function, extern inline is subject to the one definition rule, and this is the definition. But if the implementation-defined optimization heuristics follow the recommendation to use the inline keyword as a suggestion that "calls to the function be as fast as possible" (ISO 9899:1999 §6.7.4 (5) , extern inline countsMinivet
@Noora The gcc equivalent is __attribute__((always_inline)). It forces the function to be inlined even if optimizations are disabled. It is useful on extremely short functions that are called extremely frequently, such as in vector math libraries, that make debugging run slow when optimization is off if they are not inlined.Bream
You're definitely wrong about the "only one portable"; see my answer.Isoclinal
So, if I understood correctly, in C++, one defines inline an inline (not static inline) function in a header file, like in C99 inline, with the difference that in C++ no source (.cxx) file will have a declaration for it (in C99, exactly one source (.c) file has to provide the prototype with extern inline, which will have the one and only definition in the corresponding object .o file). Right?Maxi
According to cppreference, it seems like in C++, inline is equivalent to extern inline and static inline after a small test seems to generate a function local to that translation unit, and if another non-static inline function with the same name is defined in another translation unit, that function and this function will separate.Packhorse
c++ is a little different than extern inline, since it explicitly waives the one-definition rule (allowing multiple definitions, but with undefined behavior if they are not identical). In practice this means the symbol is emitted using something like comdat (or whatever equivalent linker feature a toolchain has)Minivet
T
34

I believe you misunderstand __FILE__ and __LINE__ based on this statement:

because it uses the __FILE__ and __LINE__ macros which should resolve for the caller but not this called function

There are several phases of compilation, and preprocessing is the first. __FILE__ and __LINE__ are replaced during that phase. So by the time the compiler can consider the function for inlining they have already been replaced.

Throughout answered 19/10, 2008 at 15:15 Comment(0)
G
15

It sounds like you're trying to write something like this:

inline void printLocation()
{
  cout <<"You're at " __FILE__ ", line number" __LINE__;
}

{
...
  printLocation();
...
  printLocation();
...
  printLocation();

and hoping that you'll get different values printed each time. As Don says, you won't, because __FILE__ and __LINE__ are implemented by the preprocessor, but inline is implemented by the compiler. So wherever you call printLocation from, you'll get the same result.

The only way you can get this to work is to make printLocation a macro. (Yes, I know...)

#define PRINT_LOCATION  {cout <<"You're at " __FILE__ ", line number" __LINE__}

...
  PRINT_LOCATION;
...
  PRINT_LOCATION;
...
Gratuity answered 19/10, 2008 at 21:49 Comment(3)
A common trick is for a macro PRINT_LOCATION to call a function printLocation, passing FILE and LINE as parameters. This can result in better debugger/editor/etc behaviour when the function body is non-trivial.Scalf
@Gratuity Look at my solution - extension of yours but more comprehensive and extensible.Sackman
@SteveJessop Something like I have listed in the solution below?Sackman
I
4

Instead of answering "what does it do?", I'm answering "how do I make it do what I want?" There are 5 kinds of inlining, all available in GNU C89, standard C99, and C++. MSVC has some of them (note that I haven't tested the MSVC code)

always inline, unless the address is taken

Add __attribute__((always_inline)) to any declaration, then use one of the below cases to handle the possibility of its address being taken.

You should probably never use this, unless you need its semantics (e.g. to affect the assembly in a certain way, or to use alloca). The compiler usually knows better than you whether it's worth it.

MSVC has __forceinline which appears mostly the same, but apparently it refuses to inline in quite a few common circumstances (e.g. when optimization is off) where other compilers manage just fine.

inline and emit a weak symbol (like C++, aka "just make it work")

__attribute__((weak))
void foo(void);
inline void foo(void) { ... }

Note that this leaves a bunch of copies of the same code lying around, and the linker picks one arbitrarily.

MSVC doesn't appear to have an exact equivalent in C mode, although there are a couple of similar things. __declspec(selectany) appears to be talking about data only, so might not apply to functions? There is also linker support for weak aliases, but does that work here?

inline, but never emit any symbol (leaving external references)

__attribute__((gnu_inline))
extern inline void foo(void) { ... }

MSVC's __declspec(dllimport), combined with an actual definition (otherwise unusual), supposedly does this.

emit always (for one TU, to resolve the preceding)

The hinted version emits a weak symbol in C++, but a strong symbol in either dialect of C:

void foo(void);
inline void foo(void) { ... }

Or you can do it without the hint, which emits a strong symbol in both languages:

void foo(void) { ... }

Generally, you know what language your TU is when you're providing the definitions, and probably don't need much inlining.

MSVC's __declspec(dllexport) supposedly does this.

inline and emit in every TU

static inline void foo(void) { ... }

For all of these except the static one, you can add a void foo(void) declaration above. This helps with the "best practice" of writing clean headers, then #includeing a separate file with the inline definitions. Then, if using C-style inlines, #define some macro differently in one dedicated TU to provide the out-of-line definitions.

Don't forget extern "C" if the header might be used from both C and C++!


There are also a couple of related things:

never inline

Add __attribute__((noinline)) to any declaration of the function.

MSVC has __declspec(noinline) and [[msvc::noinline]] but it is documented to only work for member functions. However, I've seen mention of "security attributes" which might prevent inlining?

force other functions to be inlined into this one if possible.

Add __attribute__((flatten)) to any declaration of the function.

Note that noinline is more powerful than this (not tested on MSVC), as are functions whose definition isn't known at compile-time.

MSVC now documents [[msvc::flatten]]; note that it applies to a scope rather than to a function. Formerly there was no equivalent, since [[msvc::forceinline_calls]] is not recursive.

Isoclinal answered 8/7, 2018 at 7:2 Comment(5)
Missing: what about MSVC? It has some C89 dialect extensions, but I never use MSVC and don't know how to run its nm equivalent.Isoclinal
Case #4 void foo(void); inline void foo(void) { ... } title is "emit always (for one TU, to resolve the preceding)". The other 4 begin with "inline ....". In case 4: does it inline too?Substandard
@chux-ReinstateMonica Inlining is more about symbol behavior than about performance. The compiler is allowed to inline for performance whether the keyword is present or not (subject to interposition rules). The critical part of case #4 is how it relates to case #3's unresolved symbols; often there aren't (m)any callers in that TU anyway.Isoclinal
My comment was more about why did this answer in cases 1,2,3,5 have inline in their title description, but 4 did not. Are you suggesting that in case 4 code is never inlined, always inlined, sometimes inlined? Are case #4 inline possibilities different than the other 4?Substandard
forceinline_calls is now mentioned in the docs: learn.microsoft.com/en-us/cpp/cpp/attributes?view=msvc-170Slumber
A
3

The situation with inline, static inline and extern inline is complicated, not least because gcc and C99 define slightly different meanings for their behavior (and presumably C++, as well). You can find some useful and detailed information about what they do in C here.

Astilbe answered 19/10, 2008 at 21:59 Comment(0)
S
3

Macros are your choice here rather than the inline functions. A rare occasion where macros rule over inline functions. Try the following: I wrote this "MACRO MAGIC" code and it should work! Tested on gcc/g++ Ubuntu 10.04

//(c) 2012 enthusiasticgeek (LOGGING example for StackOverflow)

#ifdef __cplusplus

#include <cstdio>
#include <cstring>

#else

#include <stdio.h>
#include <string.h>

#endif

//=========== MACRO MAGIC BEGINS ============

//Trim full file path
#define __SFILE__ (strrchr(__FILE__,'/') ? strrchr(__FILE__,'/')+1 : __FILE__ )

#define STRINGIFY_N(x) #x
#define TOSTRING_N(x) STRINGIFY_N(x)
#define _LINE (TOSTRING_N(__LINE__))

#define LOG(x, s...) printf("(%s:%s:%s)"  x "\n" , __SFILE__, __func__, _LINE, ## s);

//=========== MACRO MAGIC ENDS ============

int main (int argc, char** argv) {

  LOG("Greetings StackOverflow! - from enthusiasticgeek\n");

  return 0;
}

For multiple files define these macros in a separate header file including the same in each c/cc/cxx/cpp files. Please prefer inline functions or const identifiers (as the case demands) over macros wherever possible.

Sackman answered 6/8, 2012 at 21:32 Comment(0)
V
0

C++ only:

As others have pointed out, macros (here __FILE__ and __LINE__) are evaluated before compiling and linking; So if you have a function that uses those and you want them to be different for each file, you need the opposite of inline. Since the __FILE__ and __LINE__ values are going to be different for each file, then the definition (body) of the function is going to be different for each file. But (non-static) inline means that if the function is defined in multiple translation units, they all must have the same definition.

You could define (not declare) a normal function or static or static inline function in a header file and include it anywhere you want. This way each translation unit (source file) gets its own copy of the function with different __FILE__ and __LINE__ values. Although, I think in the case of static inline, the inline keyword is useless in most cases.

Vacancy answered 10/9, 2022 at 5:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.