I'm compiling the next simple example:
#include <iostream>
struct PP
{
inline void wInline();
}
inline void PP::wInline()
{
std::cout << "hola" << endl;
}
int main()
{
PP pp;
pp.wInline();
return 0;
}
in this way:
g++ -O0 -finline-functions -finline-functions-called-once
-finline-small-functions -Wall -Wextra -pedantic -std=c++11 -Winline
main.cpp
with gcc 4.8.2 and wInline is not being inlined.
Are the -finline-functions, -finline-functions-called-once and
-finline-small-functions flags enough?
Why am I having no warnings even with the -Winline flag enabled?
The inline keyword is a hint to the optimizer. Since you are compiling with -O0, no optimization is done.
The gcc documentation says this about -Winline:
Warn if a function that is declared as inline cannot be inlined.
There are rules for what functions can be declared inline as well as heuristics that gcc uses to decide if it should inline a function declared as inline.
You can read more about the inline keyword here.
(Edited to answer second question.)
Related
In the following code, I create a Builder template, and provide a default implementation to return nothing. I then specialize the template with int, to return a value 37.
When I compile with -O0, the code prints 37, which is the expected result. But when I compile using -O3, the code prints 0.
The platform is Ubuntu 20.04, with GCC 9.3.0
Can anyone helps me understand the behavior?
builder.h
class Builder {
public:
template<typename C>
static C build() {
return 0;
}
};
builder.cc
#include "builder.h"
template<>
int Builder::build<int>() {
return 37;
}
main.cc
#include "builder.h"
#include <iostream>
int main() {
std::cout << Builder::build<int>() << '\n';
}
makefile
CXX_FLAG = -O0 -g
all:
g++ $(CXX_FLAG) builder.cc -c -o builder.o
g++ $(CXX_FLAG) main.cc builder.o -o main
clean:
rm *.o
rm main
You should add a forward declaration for build<int>() to builder.h, like so:
template<>
int Builder::build<int>();
Otherwise, while compiling main.cc, the compiler sees only the generic template, and is allowed to inline an instance of the generic build() function. If you add the forward declaration, the compiler knows you provided a specialization elsewhere, and will not inline it.
With -O3, the compiler tries to inline, with -O0 it will not inline anything, hence the difference.
Your code actually violates the "One Definition Rule": it will create two definitions for Builder::build<int>(), but they are not the same. The standard says the result is undefined, but no diagnostics are required. That is a bit unfortunate in this case, as it would have been helpful if a warning or error message was produced.
I got the following warning when compiling a C++ file :
variables.cpp:10:8: warning: extended initializer lists only available with -std=c++11 or -std=gnu++11
int c{2} ;
This is the file :
#include <iostream>
#include <string>
#include <cstdlib>
using namespace std ;
int main()
{
int a = 0 ;
int b(1) ;
int c{2} ;
string myString = "I am a string !" ;
cout << a+b+c << endl ;
cout << myString << endl ;
return EXIT_SUCCESS ;
}
And this is the command line :
g++ -std=c++0x -Wall -Wextra -Winit-self -Wold-style-cast -Woverloaded-virtual -Wuninitialized -Wmissing-declarations -Winit-self -ansi -pedantic variables.cpp -o variables
I am using g++ 7.4.0 on Ubuntu 18.04.1
I do not want to ignore the warning but to solve it,
Thank you
PS : I tried to change -std=c++0x to -std=c++11 but it did not change anything
Remove -ansi in your command, which is equivalent to -std=c++98 and would overwrite your previous flag -std=c++11. According to C-Dialect-Options,
In C mode, this is equivalent to -std=c90. In C++ mode, it is equivalent to -std=c++98.
Replace -std=c++0x with -std=c++11.
Note that if your compiler supports it, it is recommended to use the lastest c++ standard which is -std=c++17. Using newer c++ standard usually makes your code shorter, more readable and more performant.
You have 2 problem in compilation command line:
The first one is the -ansi in compilation command that implicitly set the standard to the c++98. In you case the -ansi option generate conflict with -std=c++11.
The second one is the -std=c++0x, you have to replace it with -std=c++11.
Here the simple code for compile-time repetition of a lambda. I compiled for AVR with -Os and -O2. With -Os the lambda isn't inlined but with -O2. The g++ manual says that -Os is same as -O2 but disables some optimizations which increase the code size. I wonder if I can tweak the g++ options to inline such simple lambdas.
#include <cstdint>
#include <utility>
volatile uint8_t x;
namespace detail {
template<auto... II, typename F>
void repeat_impl(std::index_sequence<II...>, F&& f) {
( ((void)II, f()) , ...);
}
}
template<auto N, typename F>
void repeat(F&& f) {
detail::repeat_impl(std::make_index_sequence<N>{}, static_cast<F&&>(f));
}
int main() {
repeat<10>([](){
x/=2;
});
}
These three seem to be related with inline, turned on at -O2 but not -Os:
-finline-small-functions
-findirect-inlining
-fpartial-inlining
You could add one by one to -Os. Although inline may happen in an early phase of compile & optimization, there might be possibility that a function becomes feasible for inline after another optimization preceding inline is applied.
I do not think always_inline attribute is in C++ standard. (not sure, though) If it is not, it depends on the compiler. Gcc seems to support the attribute. Adding the attribute to the function would result in the function inlined.
I found an example where the output is different depending on the optimization settings (-O3 vs none), yet GCC 4.8.2 produces no warnings, even with the -std=c++11 -Wall -pedantic options.
In this particular case, I'm assuming that "forgetting" the commented line in header.h is a mistake, and with -O3, c<int>::get() gets inlined.
However, is there any way to protect yourself against these kinds of mistakes -- a compiler or linker option perhaps?
header.h:
#ifndef HEADER_H
#define HEADER_H
template<class T>
struct c
{
int get() { return 0; }
};
// template<> int c<int>::get();
#endif
imp.cpp:
#include "header.h"
template<>
int c<int>::get()
{
return 1;
}
main.cpp:
#include <iostream>
#include "header.h"
int main()
{
c<int> i;
std::cout << i.get() << '\n'; // prints 0 with -O3, and 1 without
}
build:
c++ -std=c++11 -pedantic -Wall -O3 -c imp.cpp
c++ -std=c++11 -pedantic -Wall -O3 -c main.cpp
c++ -std=c++11 -pedantic -Wall -O3 imp.o main.o
What you get if you have the line in your header-file, is a declaration of an explicit specialization for that member-function.
Thus, main.cpp is assured of a definition in some other compilation-unit, and things work.
If you leave it out, you have a violation of the ODR:
That specific instantiation of your class-template is different in the compilation-units. Resulting in "ill-formed, no diagnostic required", so anything goes.
And there is (currently?) no compiler-option to force gcc to diagnose it.
The true mistake here is the way you're laying out your source files and building them. When c<int>::get() is used, its definition should be available in order to instantiate the template. To fix this, header.h should #include "imp.cpp" rather than the other way around. You may want to rename imp.cpp to imp.inl or something else.
When you define templates which are used outside of a single .cpp file, those definitions should be visible to anyone who includes their header.
As an aside: I don't think there's any way to make the compiler or linker warn you about what you've done here. But if you structure your project as I've described, this problem won't happen, because the forward declaration will be unnecessary.
I've been reading again Scott Meyers' Effective C++ and more specifically Item 30 about inlining.
So I wrote the following, trying to induce that optimization with gcc 4.6.3
// test.h
class test {
public:
inline int max(int i) { return i > 5 ? 1 : -1; }
int foo(int);
private:
int d;
};
// test.cpp
int test::foo(int i) { return max(i); }
// main.cpp
#include "test.h"
int main(int argc, const char *argv[]) {
test t;
return t.foo(argc);
}
and produced the relevant assembly using alternatively the following:
g++ -S -I. test.cpp main.cpp
g++ -finline-functions -S -I. test.cpp main.cpp
Both commands produced the same assembly as far as the inline method is concerned;
I can see both the max() method body (also having a cmpl statement and the relevant jumps) and its call from foo().
Am I missing something terribly obvious? I can't say that I combed through the gcc man page, but couldn't find anything relevant standing out.
So, I just increased the optimization level to -O3 which has the inline optimizations on by default, according to:
g++ -c -Q -O3 --help=optimizers | grep inline
-finline-functions [enabled]
-finline-functions-called-once [enabled]
-finline-small-functions [enabled]
unfortunately, this optimized (as expected) the above code fragment almost out of existence.
max() is no longer there (at least as an explicitly tagged assembly block) and foo() has been reduced to:
_ZN4test3fooEi:
.LFB7:
.cfi_startproc
rep
ret
.cfi_endproc
which I cannot clearly understand at the moment (and is out of research scope).
Ideally, what I would like to see, would have been the assembly code for max() inside the foo() block.
Is there a way (either through cmd-line options or using a different (non-trivial?) code fragment) to produce such an output?
The compiler is entirely free to inline functiones even if you don't ask it to - both when you use inline keyword or not, or whether you use -finline-functions or not (although probably not if you use -fnoinline-functions - that would be contrary to what you asked for, and although the C++ standard doesn't say so, the flag becomes pretty pointless if it doesn't do something like what it says).
Next, the compiler is also not always certain that your function won't be used "somewhere else", so it will produce an out-of-line copy of most inline functions, unless it's entirely clear that it "can not possibly be called from somewhere else [for example the class is declared such that it can't be reached elsewhere].
And if you don't use the result of a function, and the function doesn't have side-effects (e.g. writing to a global variable, performing I/O or calling a function the compiler "doesn't know what it does"), then the compiler will eliminate that code as "dead" - because you don't really want unnecessary code, do you? Adding a return in front of max(i) in your foo function should help.