C++ Logo


Advanced search

Re: [std-proposals] Drop same sequence of tokens for inline

From: Alejandro Colomar <alx.manpages_at_[hidden]>
Date: Fri, 5 May 2023 17:20:24 +0200
Hi David,

On 5/5/23 12:51, David Brown via Std-Proposals wrote:
> On 05/05/2023 02:07, Alejandro Colomar via Std-Proposals wrote:
>> Hi,
>> On 5/4/23 21:24, Tom Honermann via Std-Proposals wrote:
>>> On 5/4/23 1:11 PM, sasho648 via Std-Proposals wrote:
>>>> So why does inline functions need to have the same sequence of tokens
>>>> in different TU - imagine in a TU there is a preprocessor define that
>>>> changes the function definition - it would make sense this not to be UB.
>>>> In C inline functions have internal linkage - it would make the same
>>>> sense for C++.
>> What? C inline functions have the same linkage as any other function:
>> external by default; internal if you use 'static'.
>> Here's how you should use inline in C:
>> // foo.h:
>> inline int
>> foo(void)
>> {
>> return 42;
>> }
>> // foo.c:
>> extern inline int foo(void);
>> Here's a great description of how C inline works:
>> <https://www.greenend.org.uk/rjk/tech/inline.html>
> That link is rather dated - as is your advice on how you "should" use
> inline in C.
> The prime use of "inline" in modern C on modern compilers is as
> information to the human programmers reading the code. There are very
> few use-cases where "inline" makes sense without "static",

> and then the
> "inline" modifier is pretty much redundant except to tell readers that
> this is something you view as a small helper function

Yes, inline is redundant in static inline. I never write static inline
functions, since I would only write them in .c files, but then static is

> - the kind of
> function previous generations might have written as function-like macros.

For this, extern inline is best. Let me explain.

> Consider the difference between a static inline function "foo", and a
> non-static inline function "bar" with the same functionality.
> With "foo", you always know exactly what definition is used for the
> function. With "bar", there is a guessing game - the compiler might use
> the local inline definition, or it might use an external definition.

You probably refer to C11::6.7.4p7:
 It is unspecified whether a call to the function uses the
 inline definition or the external definition.

While the standard allows programmers to write such guessing games, it's
the programmer's fault if they write more than one definition for an
inline function. The model I showed where there's exactly one
source-code definition in the header file, and one extern prototype
(used for the external-linkage definition) in a single TU doesn't have
guessing issues.

So, this one is not really an issue.

> In
> neither case do you have any guarantees that the code is actually
> generated inline in the caller

That's true, and that shouldn't be a concern. As far as the abstract
machine is concerned, inline is irrelevant. If one cares about
performance (and knows more than the compiler, which is rare) and wants
to force the compiler to inline a function, it should use a compiler-
specific attribute, such as [[gnu::always_inline]]. inline is not
the right tool for that. Not even in C++.

So, this shouldn't be an issue either.

> - it could be a stand-alone function in
> the object code.

> But with "foo", you know the definition for that
> function, while with "bar" it could be either the inline definition or
> the external definition.

Again, both should be the same in my model of inline, since the TU that
defines the external definition uses the same source code from the
header than the rest of TUs will see.

Not an issue.

> With "foo", you can have local modifiable static data.

Local to what? In a header file?

> You can refer to
> other static data and functions in the file. With "bar", you cannot.

I would be very worried about a program whose design depends on static
storage variables in a header file. In C++, that may be useful. In C,
I've never seen such a need. However, if you really need it, you can
use 'static inline' for those very rare cases.

> (gcc and perhaps other compilers are far too lax on these rules - but
> let's stick to standard C and defined behaviours.)
> With "foo", you only need the one definition - and you can put it in a
> header or a C file, according to where you want to use it. With "bar",
> you /might/ need an external definition as well - or might not,
> depending on how it happens to be used.

No. You should use C99 inline in this way:

- A single definition in a header file, which will be used for _all_
   inline definitions _and_ the one external definition.

- A single extern inline prototype in a single TU, which will tell
   the compiler to emit the external definition.

So, with "bar", you also only write the function body once. You only
write an extra prototype.

> With "foo", if you actually need a non-inlined version of the function
> from multiple translation units,
> and you don't have a smart enough
> linker that can combine these, you'll waste a bit of space.

Exactly, and there's another reason to avoid it:

Pointers to the same function compare different in different TUs, as
they are different definitions; but that's not a normal thing I do, so
I care less about it.

> If that
> space is remotely close to significant, then you should not have been
> making the function "inline" in the first place.

> In real-world usage,
> the risk of extra space is irrelevant. (And I say that as someone who
> works on small microcontrollers for a living.)

It may be small, but since there are no advantages of static inline vs
inline with reasonable uses of inline, I prefer the smaller space, and
as a side effect, pointers compare equal.

The issues you've been describing of inline are really issues with the
old GNU inline, which is crap. C99 doesn't have those issues, unless
the programmer really writes stupid code (which is allowed). BTW, GCC
defaults to C99 inline unless you use -std=gnu89; but if you use
GNU C89, you have many more problems to worry about, of course.


Received on 2023-05-05 15:20:28