2

So I was reading some code and ran across this little snippet:

//file.h

extern inline void foo();

With the implementation that looks like this:

//file.c
MyType_t instance;
inline void foo()
{
    instance.someField++;
}

Perhaps maybe I'm misunderstanding something, but it looks to me like the author intended to have an externally visible function which was also inline (which has internal linkage by default I believe). Because it's a tiny modifier function, I think the intention was to leverage inline substitution to avoid function overhead (which makes sense, as this runs on an embedded platform). But is this code actually eligible for inline substitution?

My understanding is that inline provides that the compiler won't complain about multiple definitions (as per this link), and that the function may be eligible for inline substitution (as an alternative to function-like macros).

In this case though, because the definition is not in the header, there will still only be one "copy" of the inline function, so it is not eligible for inline substitution in files which include file.h (as far as I can tell).

(from the second link above):

the compiler must at least have seen a definition of the function. But if you have a classical big C project with functions that are implemented in different compilation units the compiler can’t do that integration for you. So you’d have to put the implementation of such candidate functions in header files.

Therefore, it's only property remaining would be that the compiler won't complain about the multiply defined symbols, so that it could perhaps be defined differently in a different compilation unit. Though it's not so in this case, the symbol is only defined once for the record.

So the crux of my question is: am I correct in concluding that this inline doesn't really serve much better purpose than this code:

//file.h
extern void foo();

//file.c
MyType_t instance;
void foo()
{
    instance.someField++;
}

And if the author wanted to make it eligible for inline substitution, would the solution have been to put the inline declaration and function body in the header?

Scorch
  • 437
  • 1
  • 3
  • 14
  • 1
    The `inline` keyword does not affect the linkage of the identifier to which it is applied. Inline functions can have either external or internal linkage, and like other functions, they have external linkage by default. But the combination of an explicit `extern` specifier with `inline` has special additional meaning. – John Bollinger Jun 04 '19 at 19:54
  • 1
    Or, to be precise, I should say *the identifier of* an inline function can have either external or internal linkage. – John Bollinger Jun 04 '19 at 20:00
  • 1
    See: (1) https://stackoverflow.com/questions/5229343/how-to-declare-an-inline-function-in-c99-multi-file-project, (2) https://stackoverflow.com/questions/6312597/is-inline-without-static-or-extern-ever-useful-in-c99, (3) https://stackoverflow.com/questions/216510/extern-inline/ — probably amongst a number of other Q&A on AO. – Jonathan Leffler Jun 04 '19 at 20:04
  • @JonathanLeffler Thanks for the resources. I've read a few similar questions, but I can't seem to get it to click. I think I'm thrown off by the declaration in the header and definition in the source file, which seems to counter to my current understanding of how to use `inline` – Scorch Jun 04 '19 at 20:08
  • 1
    In case of doubt, use `static inline …` in the header and then you don't have to worry about anything much. If the inline function is inlined, then there's no problem. If it isn't inlined, then each file that uses it has a private copy of the function (that's the `static` part). If that overhead is a worry, wonder about why you're declaring a function `inline` when it cannot actually be inlined. – Jonathan Leffler Jun 04 '19 at 20:10
  • 1
    And that previous comment doesn't address your concern. Yes, the behaviour is a bit convoluted, but again, it usually isn't a problem if the function marked `inline` is actually inlined. If it's not inlined, then the compiler generates a call to a regular function, and somewhere in the program, you need to define that function. And the mechanism used is `extern inline …` in one source file. Note that placing the `extern inline` declaration in a header is odd — not what you'd normally do. The plain `inline` definition (no `extern`) goes in a header; the `extern inline` goes in a source file. – Jonathan Leffler Jun 04 '19 at 20:14
  • In any case the example code that you found is of course wrong, the behaviour matches exactly that of the first duplicate, even though the *function declaration* has `inline` in it. – Antti Haapala -- Слава Україні Jun 05 '19 at 00:27

1 Answers1

4

C99 inline functions are not like C++ inline functions.

They aren't merged. You're expected to provide an extern definition if you don't make it static.

The way you do that for an inline function f is by providing an extern declaration (e.g., int f(void);, extern int f(void);, or extern inline int f(void);) in a translation unit where an inline definition for f (e.g., inline f(void){ return 42; }) is also provided.

The order is not important.

inline int f(void) { return 42; }
int f(void);

provides an external definition just like

int f(void);
inline int f(void) { return 42; }

does.

A translation unit with just inline declarations/definitions without an extern declaration will not provide an extern definition:

 inline int f(void); 
//^if an inline decl is present a definition MUST follow or else you get a diagnostic
 inline int f(void) { return 42; }
 inline int f(void);  //a pointless but legal redeclaration

Usually you'll have an inline definition in a header like in your example, and then to instantiate it, you'll include the header and provide an extern declaration. Including extern or extern inline in the declaration is technically unnecessary but it makes it clear that that declaration's goal is to instantiate an extern definition of an inline function.

More info at 6.7.4p7. (I find this particular part of the C standard somewhat cryptic but usage examples are provided.)

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
  • 1
    Minor correction: the needed external function definition does not have to have scope overlapping that of any inline definition of the same function. In fact, I don't think it *can* do, though it can (but does not need to) share scope with any number of *declarations* of that function bearing the `inline` specifier. – John Bollinger Jun 04 '19 at 20:04
  • 2
    There must be an `inline` definition of the function visible at the time you instantiate the function with `extern` — otherwise, how is the compiler to know how to instantiate the function? – Jonathan Leffler Jun 04 '19 at 20:06
  • 1
    I don't follow, @JonathanLeffler. An inline definition of a given function does not provide and does not preclude an external definition of the same function. In the case we're discussing, an external definition is required. But in compiling the external definition, it is not necessary to know whether there are any internal ones. All the declarations must be compatible, but inlinedness is not a factor in that. – John Bollinger Jun 04 '19 at 20:22
  • Addendum: I meant to say "But in compiling the external definition, it is not necessary to know whether there are any ***inline*** ones," though what I actually did say is true, too. – John Bollinger Jun 04 '19 at 20:31
  • @JohnBollinger I guess if the standard doesn't prohibit independent definitions where the corresponding inline isn't visible, it should be allowed. Thanks for the comment! – Petr Skocik Jun 04 '19 at 20:39
  • 1
    See C11 [§6.7.4 Function specifiers ¶10 (Example 1)](http://port70.net/~nsz/c/c11/n1570.html#6.7.4p10), which also references [§6.9 External definitions](http://port70.net/~nsz/c/c11/n1570.html#6.9), though I'm not immediately sure why — there's only one mention of `inline` there. Also very relevant is [§6.7.4 ¶7](http://port70.net/~nsz/c/c11/n1570.html#6.7.4p7). – Jonathan Leffler Jun 04 '19 at 21:01