2

Say I have a macro UNIQUE_NAME(PREFIX) that concatenates __LINE__ to PREFIX:

#define CONCAT2_EXPAND(a, b)    a ## b
#define CONCAT2(a, b)           CONCAT2_EXPAND(a, b)
#define UNIQUE_NAME(prefix)     CONCAT2(CONCAT2(prefix, _), __LINE__)

Now if this macro was to be used in an inline function, included in two different translation units, then the expanded code of the inline function would differ between the two translation units (unless they have the same includes).

Is this behavior defined, unspecified, implementation-defined, or undefined?


This is what I got during a code review:

You're caught between a rock and a hard place here. Using __LINE__ is a bad idea, because there's nothing preventing me from calling UNIQUE_NAME twice on the same line, and then it's not really unique any longer, is it? __COUNTER__ would give a unique name, but it'd be too unique: if UNIQUE_NAME was used in an inline function, included in two different translation units, then the expanded code of the inline function would differ between the two TUs (unless they have the same includes), which is Undefined Behavior. – @Matthieu M.

If it helps, I am confused about having a static inline function and an inline function (if it matters which one) in a header file and including it in different .c files.

Say:

[[gnu::always_inline]] static inline void foo(void)
{
    bool UNIQUE_NAME(bar);
    // ...
}

and

inline void foo(void)
{
    bool UNIQUE_NAME(bar);
    // ...
}

I believe the reviewer meant the second one, and not the first one.

20
  • Possibly a duplicate of stackoverflow.com/questions/5047597/… I believe the accepted answer is explicitly addressing your question.
    – Eugene Sh.
    Commented Jun 7 at 15:42
  • 1
    static inline is different from inline in the sense, that you can't have two TUs including the header with just inline, as it will constitute duplicate definition. static will make it "visible" only in the including TUs, so each can have it's own copy.
    – Eugene Sh.
    Commented Jun 7 at 16:11
  • 1
    IMO, using inline without static leads to confusion — I don't do it in my own code. Using static inline … avoids most (if not all) of the problems with definitions vs uses. The residual issue is "if the function is not inlined, then each object file that calls the function has a copy of the function". For me, that's an acceptable risk. If the function might not be inlined, it probably shouldn't be declared inline. GCC seems to inline functions extensively — creating a static (not inline) function that's called from one line of code in the file often leads to that function being inlined. Commented Jun 7 at 16:25
  • 1
    Why do you need a unique name inside the inline function? The local variable names in an inline function are completely disjoint from the names in any function where it is inlined — one of the many benefits of using inline functions instead of macros to achieve inlined code. Commented Jun 7 at 16:31
  • 1
    @EugeneSh.: This is allowed; when you define with inline void foo(void) { ... }, you have created an inline definition of a function with external linkage, which is not an external definition. But there still needs to be an external definition, and the compiler is allowed to use it instead of the inline definition (it is unspecified which is used). So in your example, the compiler probably declined to inline the function for whatever reason, emitted call foo instead, and then failed to link because there is no out-of-line definition of foo. Commented Jun 7 at 17:11

1 Answer 1

5

If you use static inline, then everything is fine. Then you simply have (conceptually) a static function in each translation unit, and they are fully independent of each other; the same way that static functions have always worked in C. Adding inline doesn't change these semantics; the difference between static and static inline is effectively just a hint to the compiler that inlining might be a good idea, but it remains free to inline or not, with or without the keyword.

If you just use inline, now the function has external linkage and you have created an inline definition under 6.7.4p7. This is not an external definition of foo(), and it still needs to have one under 6.9p5, so you'll have to provide one elsewhere. This could be done by including a definition of foo (without inline) in some third translation unit which does not include the header. Or, by adding the declaration extern void foo(void); to exactly one of the translation units which does include the header, in which case the definition in that translation unit is no longer an inline definition (despite the inline keyword) and becomes a regular external definition. See Example 1 in 6.7.4p10.

This does have a practical advantage (in a typical implementation) in that if there are instances where the function cannot be inlined, then all such instances from all translation units can call the same single out-of-line copy of the function. If you define it static inline then you may get a separate out-of-line copy per translation unit, which makes your executable larger, increases memory usage, makes caching less efficient, etc (though the effects may or may not actually be significant).

Note that on typical implementations, if the compiler actually is able to inline every instance of the function, it may never actually need to look for the external definition, and thus may not complain if it doesn't exist. But the standard still requires it.

As far as I know, C doesn't actually require that the inline definitions agree with each other, nor with the external definition. However, when they both exist, then in any given place where the function is called, it is unspecified which definition is actually executed, and this could lead to confusion.

The reviewer might be thinking about the rule that a function cannot have more than one external definition (whether they are identical or not). However, as it stands you don't have multiple external definitions; in fact you don't have any (which is still bad but for a different reason). If you add an external definition in one translation unit, then you'll have exactly one, which is what's required.

The reviewer might also be thinking of C++ which does have a rule that inline definitions of a function must all be identical (C++20 6.3p13). But as far as I know, C does not have such a rule.

1
  • 1
    As the reviewer, I confirm that I was not aware that this was a point a divergence between the C and C++ standards. Commented Jun 8 at 9:29

Not the answer you're looking for? Browse other questions tagged or ask your own question.