5

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?

陈浩南
  • 633
  • 4
  • 12
  • 1
    The proper way is to have the function definition a cpp file. But since your way is not proper, why you don't just ignore (disable) the warning? – ixSci May 20 '22 at 16:10
  • @ixSci But if `do_something` is a function template, it may be not convenient to put it into a cpp file. – 陈浩南 May 20 '22 at 16:18
  • 1
    If it was a function template you would not have the `inline` keyword in the first place. – ixSci May 20 '22 at 16:19
  • Please post full code needed to reproduce the problem. Please post the exact full verbatim error messages from your compiler. `Warning: inline declaration of` is there no `file:line: first declared here ---->` and then `file:line: second time declared here --->`? You have two declarations of `do_something()` in your code, which is the reason of the error. – KamilCuk May 20 '22 at 18:14

3 Answers3

1

The inline keyword does not mean the code must be inlined or that it even will. inline says that there may be multiple (identical) versions of this functions and the linker will pick at most one representative.

The compiler can inline the function if it thinks it's worth it and inline can make it more likely to be inline. But why not let the compiler decide? It doesn't inline functions without reason.

But if you must have you tried static? A static function will not be exported to the outside of the translation unit. Every translation unit can have a static version of the same function without conflict. But be aware that that will be many copies of the same function and each has a unique address.

Goswin von Brederlow
  • 11,875
  • 2
  • 24
  • 42
0

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

Pharap
  • 3,826
  • 5
  • 37
  • 51
-1

if I want to force do_something not to be inlined by the compiler

There is no portable way to do that.

inline has a confusing name, it's not about inlining. You want just:

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

I want to use backtrace library to dynamically find out which function called do_something

There is artificial funciton attribute https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes .

what is the proper way to do such things?

The proper way of defining a function in a header is with a static keyword.

If you mean by not-inlining a function, you don't - compilers optimize code, functions get inlined. If you want a fuction to be available for debugging, you configure compiler not to inline the code (-fno-inline, if you want to force it).

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • 1
    As was mentioned in the question, `static` would cause duplicates of the symbol in each translation unit by marking them as having internal linkage. Although this _works_, this doesn't actually solve the problem practically for if the function is large, or used many times. – Human-Compiler May 20 '22 at 18:20
  • I do not understand. `this doesn't actually solve the problem` what problem is not solved? – KamilCuk May 20 '22 at 18:48