19

I'm re-writing a small C math library of mine that will end up as a static library for the user and would like to benefit from inlining for my vector math interface.

I have the following:

[ mymath.h ]

...
...
extern float clampf( float v, float min, float max );
...
...

[ mymath.c ]

inline float clampf( float v, float min, float max )
{
    if( v < min ) v = min;
    if( v > max ) v = max;

   return v;
}

Since my library will be static and I'm only going to provide the .h (and the .lib) to the user, will the clampf function be inlined in their program when compiled?

Am I doing the right thing but declaring the function extern in the .h and inline in the .c?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
McBob
  • 1,011
  • 2
  • 13
  • 25

2 Answers2

21

You have it almost correct. You actually have it backwards; for inline functions you must put the inline definition in the header file and the extern declaration in the C file.

// mymath.h
inline float clampf( float v, float min, float max )
{
    if( v < min ) v = min;
    if( v > max ) v = max;

    return v;
}

// mymath.c
#include "mymath.h"
extern float clampf( float v, float min, float max );

You have to put the definition (full body) in the header file, this will allow any file which includes the header file to be able to use the inline definition if the compiler chooses to do so.

You have to put the extern declaration (prototype) in the source file to tell the compiler to emit an extern version of the function in the library. This provides one place in your library for the non-inline version, so the compiler can choose between inlining the function or using the common version.

Note that this may not work well with the MSVC compiler, which has very poor support in general for C (and has almost zero support for C99). For GCC, you will have to enable C99 support for old versions. Modern C compilers support this syntax by default.

Alternative:

You can change the header to have a static inline version,

// mymath.h
static inline float clampf(float v, float min, float max)
{
    ...
}

However, this doesn't provide a non-inline version of the function, so the compiler may be forced to create a copy of this function for each translation unit.

Notes:

  1. The C99 inlining rules are not exactly intuitive. The article "Inline functions in C" (mirror) describes them in detail. In particular, skip to the bottom and look at "Strategies for using inline functions". I prefer method #3, since GCC has been defaulting to the C99 method for a while now.

  2. Technically, you never need to put extern on a function declaration (or definition), since extern is the default. I put it there for emphasis.

Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
  • 1
    I am quite sure that your answer is wrong. If you define a function `inline`, and not `static inline`, the linking will fail if the function was used in multiple compilation units. Tested with GCC 4.6 and -std=c99. Declaring the function definition `extern inline` would work, but that is a GCC extension. – Kijewski Apr 24 '12 at 07:19
  • 3
    @kay: Read the entire answer. You have to add an `extern` declaration for the function in a translation unit somewhere (i.e., not in a header). That will cause the non-inline version to be emitted in that module. (And the header file should generally be included from that unit.) See the C99 standard, or read http://www.greenend.org.uk/rjk/tech/inline.html – Dietrich Epp Apr 24 '12 at 07:21
6

You should define your function as static inline in the .h file:

static inline float clampf( float v, float min, float max )
{
    if( v < min ) v = min;
    if( v > max ) v = max;

    return v;
}

The function must be absent in the .c file.

The compiler may decide not to inline the function but make it a proper function call. So every generated .o file may contain a copy of the function.

Kijewski
  • 25,517
  • 12
  • 101
  • 143
  • 2
    If I recall correctly, the linker will not coalesce different copies of a `static inline` function. So you will end up with duplicate code compared to the `inline` / `extern` version. (Bleeding edge linkers, I suspect, can eliminate the duplication, but they're still a little way from common use.) – Dietrich Epp Apr 24 '12 at 04:22
  • 3
    If the function is inline, then every call to the function results in the code being duplicated. This is by design? – Robert Martin Apr 24 '12 at 04:23
  • @DietrichEpp you are right. I (falsely) thought that this duplicate code elimination was the standard, yet. – Kijewski Apr 24 '12 at 04:32
  • 1
    @RobertMartin: Almost every compiler treats `inline` as a suggestion, and some compilers outright ignore the keyword. (The standard explicitly allows this.) Most compilers will ignore it when optimization is turned off. Using the `inline`/`extern` trick in C99 you can allow the compiler to use the inline version when it chooses, or the one common extern version when it chooses. You are basically providing the compiler with the maximum flexibility in emitting code. LTO makes the entire thing obsolete (Clang `-O4`, for example). – Dietrich Epp Apr 24 '12 at 04:49