Why Are C++ Inline Functions in the Header

Is it wrong to place inline functions in C headers?

From n1570 (latest public C11 draft), §6.7.4:


  1. A function declared with an inline function specifier is an inline function. Making a
    function an inline function suggests that calls to the function be as fast as possible. The extent to which such suggestions are effective is implementation-defined.

The next section goes into details about linkage, but this passage above is basically all the C standard has to say about inline. Note how this gives the implementation every freedom, including to completely ignore inline.

Therefore, with just standard C, you could end up with multiple instances (one per translation unit) of a function that is called in the normal way. This is normally not what you want, as it combines two disadvantages (duplicated code and the overhead of a function call). So I'd argue the standard C inline is only ever useful for functions private to a single translation unit. Even then, you could assume a good optimizing compiler will automatically pick candidates for inlining, without an explicit inline.

If on the other hand your compiler provides a way to actually force inlining of a function (which according to your comments, the _inline specifier does for your compiler), having these functions in a header is safe. But be aware it is in no way portable.

As commented by cmaster, you can achieve kind of "manual inlining" with function-like macros instead for a portable solution.

Why are C++ inline functions in the header?

The definition of an inline function doesn't have to be in a header file but, because of the one definition rule (ODR) for inline functions, an identical definition for the function must exist in every translation unit that uses it.

The easiest way to achieve this is by putting the definition in a header file.

If you want to put the definition of a function in a single source file then you shouldn't declare it inline. A function not declared inline does not mean that the compiler cannot inline the function.

Whether you should declare a function inline or not is usually a choice that you should make based on which version of the one definition rules it makes most sense for you to follow; adding inline and then being restricted by the subsequent constraints makes little sense.

static inline functions in a header file

A static inline function is, in practice, likely (but not certain) to be inlined by some good optimizing compiler (e.g. by GCC when it is given -O2) at most of its call sites.

It is defined in a header file, because it then could be inlined at most call sites (perhaps all of them). If it was just declared (and simply "exported") the inlining is unlikely to happen (except if you compile and link with link-time optimizations, a.k.a. LTO, also, e.g. compile and link with gcc -flto -O2, and that increases a lot the build time).

In practice, the compiler needs to know the body of a function to be able to inline it. So a suitable place is to define it in some common header file (otherwise, it could be inlined only in the same translation unit defining it, unless you enable LTO), so that every translation unit would know the body of that inlinable function.

It is declared static to avoid multiple definitions (at link time) in case the compiler did not inline it (e.g. when you use its address).

In practice, in C99 or C11 code (except with LTO, which I rarely use), I would always put the short functions I want to be inlined as static inline definitions in common header files.

Be sure to understand how and when the C preprocessor works. Notice that you could in principle (but it would be very bad practice and disgusting style) avoid defining some static inline function in a common header file and instead copy and paste its identical definition in multiple .c files.
(However, that might make sense for generated .c files, e.g. if you design a compiler emitting C code).

FYI LTO is practically implemented by recent GCC compilers by embedding some internal compiler representation (some GIMPLE) inside object files, and redoing some "compilation" step - using the lto1 frontend - at "link" time. In practice, the entire program is almost compiled "twice".

(actually, I always wondered why the C standardization committee did not decide instead that all explicitly inline functions are static)

Why must an inline function be defined in a header if the compiler can just inline functions on its own accord

The reason we're forced to provide definitions of inline function in header files (or at least, in some form that is visible to the implementation when inlining a function in a given compilation unit) is requirements of the C++ standard.

However, the standard does not go out of its way to prevent implementations (e.g. the toolchain or parts of it, such as the preprocessor, compiler proper, linker, etc) from doing things a little smarter.

Some particular implementations do things a little smarter, so can actually inline functions even in circumstances where they are not visible to the compiler. For example, in a basic "compile all the source files then link" toolchain, a smart linker may realise that a function is small and only called a few times, and elect to (in effect) inline it, even if the points where inlining occurs were not visible to the compiler (e.g. because the statements that called the functions were in separate compilation units, the function itself is in another compilation unit) so the compiler would not do inlining.

The thing is, the standard does not prevent an implementation from doing that. It simply states the minimum set of requirements for behaviour of ALL implementations.

Essentially, the requirement that the compiler have visibility of a function to be inlined is the minimum requirement from the standard. If a program is written in that way (e.g. all functions to be inlined are defined in their header file) then the standard guarantees that it will work with every (standard compliant) implementation.

But what does this mean for our smarter tool-chain? The smarter tool-chain must produce correct results from a program that is well-formed - including one that defines inlined functions in every compilation unit which uses those functions. Our toolchain is permitted to do things smarter (e.g. peeking between compilation units) but, if code is written in a way that REQUIRES such smarter behaviour (e.g. that a compiler peek between compilation units) that code may be rejected by another toolchain.

In the end, every C++ implementation (the toolchain, standard library, etc) is required to comply with requirements of the C++ standard. The reverse is not true - one implementation may do things smarter than the standard requires, but that doesn't generate a requirement that some other implementation do things in a compatible way.

Technically, inlining is not limited to being a function of the compiler. It may happen in the compiler or the linker. It may also happen at run time - for example "Just In Time" technology can, in effect, restructure executable code after it has been run a few times in order to enhance subsequent performance [this typically occurs in a virtual machine environment, which permits the benefits of such techniques while avoiding problems associated with self-modifying executables].

inline function definition in header vs source file

You are using a portable strategy based on standard C99 (and more recent), which is entirely correct.

It is failing because you invoke gcc with its default -std setting, which for GCC 4.8.5 effectively tells it to use its legacy semantics and not standard C11 semantics. The default value for -std was gnu90 in version 4.8.5. In version 5, it was changed to gnu11, which implements C99/C11 inline semantics.

You're using default settings because your Makefile does not include $(CFLAGS) in the compile recipes; only in the final link recipe. (You can see that in the commands printed out by make).

While -std=gnu11 should work, it would be better to use -std=c11 instead. And consider upgrading to a more recent GCC version.

Can I put an inline function on a header file in c++?

It is already answered in C++ FAQ

How do you tell the compiler to make a non-member function inline?

When you declare an inline function, it looks just like a normal function:

void f(int i, char c);

But when you define an inline function, you prepend the function’s definition with the keyword inline, and you put the definition into a header file:

inline
void f(int i, char c)
{
// ...
}

Note: It’s imperative that the function’s definition (the part between the {...}) be placed in a header file, unless the function is used only in a single .cpp file. In particular, if you put the inline function’s definition into a .cpp file and you call it from some other .cpp file, you’ll get an “unresolved external” error from the linker.

Inline vs static inline in header file

The proper way to inline a function in C is as follows:

  • Place an inline function in the header
  • Create an implementation file that includes that header
  • Place an extern inline function in the implementation file.

example.h

inline int example(int val) {
return (val << 2) | 1;
}

example.c

#include "example.h"

extern inline int example(int val);

Can't you just declare a static inline in the header, without .c?

This would result in separate independent function definitions in each translation unit from which the header is included. In addition in increasing the size of compiled code unnecessarily, this would produce some unexpected behavior when you obtain a pointer to your inline functions: rather than producing the same address, the addresses of the inline function taken in different translation units would produce different values.

but if one guards the header file the re-defining can be avoided, can't it?

No, absolutely not. This has nothing to do with multiple inclusions of the same header. Each translation unit is compiled separately from other translation units, so when the compiler sees a static function, it has no choice but to create a private duplicate invisible from the outside of the translation unit.



Related Topics



Leave a reply



Submit