Search code examples
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:

    • Using static. This will silence the warning, but runs the risk of producing multiple copies of the same function - one per translation unit. A link-time optimiser might be able to eliminate the duplicates, but it might not be a good idea to assume that will be the case.
    • Making the function a template makes the function implicitly inline whilst still respecting the [[gnu::noinline]], but then you end up with a dummy template parameter and force people to specify that parameter when calling the function.
    • You could make the function a static class member, which will make it implicitly inline without upsetting the attribute, and only requiring a bit of extra typing from the user (Class::do_something(1)).

    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.)