c++header-only

using `[[gnu::noinline]]` in header-only library


Functions in a header-only library should be declared as inline to prevent multiple definitions in the different translation units. That is, for example, I wrote a header-only library mylib.hpp:

void do_something(int) {}

And I used this header in two different cpp file:

// a.cpp
# include "mylib.hpp"
void func1() {do_something(1);}
// b.cpp
# include "mylib.hpp"
void func2() {do_something(2);}

Build them with g++ -o main a.cpp b.cpp, GCC will complain with "multiple definition of do_something(int)". To prevent this, define the function like static void do_something(int) {} to make it have a copy in each translation unit (that is, have two copies in the last output), or define the function like inline void do_something(int) {} to have exactly a single copy in the last output, which is what we want.

However, if I want to force do_something not to be inlined by the compiler (for example, I want to use backtrace library to dynamically find out which function called do_something), I should write it like:

[[gnu::noinline]] inline void do_something(int) {}

However, GCC complains:

Warning: inline declaration of ‘do_something(int)’ follows declaration with attribute ‘noinline’ [-Wattributes]

So, what is the proper way to do such things?


Solution

  • The 'proper' way to get rid of the warning is to split your code into a header (.h) and source (.cpp) file.

    // mylib.h
    [[gnu::noinline]]
    void do_something(int);
    
    // mylib.cpp
    void do_something(int)
    {
      // Implementation
    }
    
    // a.cpp
    #include "mylib.h"
    
    void func1()
    {
      do_something(1);
    }
    
    // b.cpp
    #include "mylib.h"
    
    void func2()
    {
      do_something(2);
    }
    

    Frankly, a library being 'header-only' is an overrated property. Splitting code into header and source files can improve compile times because the function body is parsed and compiled just once instead of being parsed and examined multiple times. (If you define a function in a header, the compiler may need to examine it multiple times to check that it hasn't changed between translation units.)

    If being 'header-only' is really that important to you the other options are:

    Another way that would work, but is really bad and should be avoided is:

    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Wattributes"
    [[gnu::noinline]]
    inline void do_something(int)
    {
      // Implementation
    }
    #pragma GCC diagnostic pop
    

    But this is a very hacky solution. Naturally it'll only work if GCC is the compiler and will break many other compilers. (Clang might be able to deal with it, but VC++ will choke on it.)


    Of course, you may not actually need to force the compiler to avoid inlining your function. Usually the compiler will make the right decision on its own.

    Personally the only times I've needed to use [[gnu::noinline]] are when a function used inline assembly that broke when the compiler inlined it and when measuring the overhead of a function call. (The latter is probably the intended use of the attribute.)