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 callingUNIQUE_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: ifUNIQUE_NAME
was used in aninline
function, included in two different translation units, then the expanded code of theinline
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.
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.