Search code examples
cinlinec99linkage

Understanding this "extern inline" call in C99


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?


Solution

  • 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.)