c++
type introspection
Introspection is examining type information at runtime, reflection goes deeper and can also manipulate values, properties and functions at runtime.
C does not have reflection possibilities so this is about introspection, and in particular inspecting the types of runtime variables.
The main source for this came from : visit
Very basic you can get a code for any type with this command :
| typeid | |
|---|---|
It prints
| signature | |
|---|---|
My guess after some playing around : it must be short for (F)unction returning (i)nt with parameters (i)nt and (P)ointer to (P)ointer to (c)har. The E is a mystery, it ios not external linkage, since static changes nothing. Maybe it is an end marker !?!
Use c++filt to decode these
These are mangled names, see : visit You need the -t (type) parameter for these :
Note that without the E it just returns FiiPPc, so it might be an end marker after all ??
Note that const qualifiers will be gone in this method !!
Here is the description of the typeid operator. : visit which does not mention the codes so it must be a compiler specific operator since it suggests it prints the complete types.
However the discussion above lead to a very handy implementation that prints a readable typename like :
| readable typename | |
|---|---|
The complete implementation is this header file :
It starts with getting constexpr and noexcept right for various compilers. We could just fill that in for gcc if we know them to come to a shorter version. Of course running it through the preprocessor works fine :
| preprocess | |
|---|---|
It reveals that all macros should be filled in g++.
| macros | |
|---|---|
Also the way to print the current function in g++ is PRETTY_FUNCTION
This yields this output :
The expression type_name
| decltype | |
|---|---|
So between the <> of the type_name template appears the type of the main function. Though the
If we step into type_name<>() you will see the type of main filled in :
| gdb run | |
|---|---|
Here is where
So we have a string here with the name we want at position 46. Assigning it to a static_string makes it more portable i guess because the original answer did not us it but parsed the string up to the '=' character.
see : visit
bitsets
C++ has the bitset type for expressing bits like bitfields in C :
| bitfields | |
|---|---|
Note that sizeof(myset) is 8 BYTES, don't be fooled by thinking it is 8 bits. This is just the minimum size for a bitset (long int). You will see that std::bitset<64> is still 8 bits, 65 will take 16 :
Output is :
| output | |
|---|---|
65 bits just do not fit in 8 bytes, so it needs two longs (16 bytes) and at the next wrap (129) it will become 24 bytes. So this does present a better solution for bit arrays, since you would have to name each field in a bitfield struct differently :
| really 8 bits but named separately | |
|---|---|
At the minimal advantage that this struct will be 1 bytes in size.
A bitset is only larger in granularity (8 bytes).
Besides that bitset offers some convenience functions like :
- test()
- set()
- reset()
- flip()
static member functions
If you get this error :
error: cannot declare member function to have static linkage
It means that you have also added static to the definition of the member function.
| static member function | |
|---|---|
Strange choice, but just memorize that.
compilation error
It usually start with :
| error | |
|---|---|
And it is on one of these lines in basic_ios.h
This usually means you have still included a c header like
compiler speedup
First of all, use make -j8 which makes make parallellize the make for 8 processors.
Or do make -j100 to make it use any processor available. To avoid having to alter .vimrc and to spare typing i made it an alias in.bashrc
| alias for parallel make | |
|---|---|
Main reason not to use c++ was the tremendous time it takes to compile. Before my transition to c++ the backend library took 16 seconds to compile all versions :all: release debug test tags with clang++, and 14 seconds with g++
Afterwards it took 1m:08s, which is more than 6 times slower. One thing to try to speed it up was the use of precompiled headers. Start by looking at what a header files loads during compilation. Watch you make output and copy just one line, run it and time it :
Now look at what files it processes, with the -H flag :
| list headers | |
|---|---|
And many many more. Now you see it starts with backend.h which sucks in all the others, also backend is in a writable directory so try just compiling that file instead of the source file :
It just created a binary version of all that, you can first look at what header files now remain with the previous command, because it will take the h.ghc file before it takes the .h file (so make a makefile entry for that !!) The output is quit different :
| output | |
|---|---|
It suggest include guards for certain files as well.
Also the timings are much better :
I'll take that, it has halved. The total compile time now takes 54 seconds. If you examine the test/test.cpp compilation you will see that /usr/local/catch.hpp also drags in a lot of stuff but if you :
Also doing it as root does not work, so what if we put catch inside test/test.h. That does work, and it cuts down the time again. :
You can try out all lines that seem to take a longer time and find out if they are a target for precompilation.
The precompiled headers with different flags are not compatible like debug and release !!
It does seem to matter where you include the pch, the first line always works.
In fact the best strategy for speed compiles is to put all headers in one headers file and include that as the first line.
We are now down to (but for debug only !!!).
Sadly i can't try a c run anymore for debug only
gch rejection
catch invalidates all .gch files you make !, i switched to google tests just for that reason.
You can see with the -H flag what headers are pulled in and also which are rejected :
Acceptation is marked with a !
Rejection is marked with an x :
and that will go on for a while.
.gch files are only valid for compilation with the 'exact' same flags.
Exact is relative, some flags are compatible, but certainly -g and -o are not. You can easily make a .gch file for each of them by creating a directory .gch file instead of a file ! All files in that directory are then examined and the first one that works is used. The files can be any name you choose !!
const objects
When making a global object at least try to make it const, so nobody can mess with it. You can instantiate the object as const by declaring :
However you will now get these errors if you do this on an unprepared class:
passing 'const Osrm' as 'this' argument discards qualifiers [-fpermissive]
The function in question was :
What the error is saying is that you call myInstance.nearest(myxy) as :
| calling myInstance.nearest(myxy) roughly does this | |
|---|---|
This roughly what a member call does, patching the object itself into 'this' as first parameter. Now you are assigning a const (myInstance) to a non-const (this) and that's not allowed. If you now declare the method as const :
to read the declaration in plain text always start at the name and then first go right for specifiers and () or [] then leftwards.
You could now read the function as :
| roughly means | |
|---|---|
It does NOT also make the xy coords_t const !!. The const just says the function will not change the instance, and this it's variables. xy is just a local variable !!
In this way we can use the osrm pointer with having to pass it down in every function without any function accidentally changing it's state.
const linkage
const variables have internal linkage by default !!
As opposed to non-const variable which have external linkage by default.
For an explanation, one anonymous comment put it very clear :
: This is because they are intended to be used like this:
| usage | |
|---|---|
constant namespace
However, it also means that how i wanted to use it was to have a program-wide constant namespace that is the same for every source file.
| const namespace | |
|---|---|
With internal linkage it means that the constants is just included in each file as if it was typed there. And you can give both another value, also you have to initialize both since it is a static const.
I you use extern, you will have to initialize one of them ! And it does not seem to matter which, the header or the .cpp file. Initializing 0 or more than one will give an error. Also the error is :
multiple definition of 'x'
Which suggests that the same mechanism for non-consts is used, and that the one with the initializer is regarded as the definition, and all the others as declarations. This also means the right place to initialize is the constant.cpp file :
| constant.cpp | |
|---|---|
With const expressions the one with the initializer is the definition. In non-const the one without 'extern' is the definition.
initialization
coercion
Implicit type conversion in assignments and expressions can lead to weird results :
| does NOT print -5 | |
|---|---|
This does not print -5, but 4294967291. This has to do with the rules of coercion
Evaluating arithmetic expressions
When evaluating expressions, the compiler breaks each expression down into individual sub expressions. The arithmetic operators require their operands to be of the same type. To ensure this, the compiler uses the following rules:
If an operand is an integer that is narrower than an int, it undergoes integral promotion (as described above) to int or unsigned int. If the operands still do not match, then the compiler finds the highest priority operand and implicitly converts the other operand to match. The priority of operands is as follows:
- long double (highest)
- double
- float
- unsigned long long
- long long
- unsigned long
- long
- unsigned int
- int (lowest)
So in this case:
5u - 10 == with types : unsigned int - signed int == the highest priority wins, so 10 get's promoted unsigned int - unsigned int 5u - 10u = -5u == -5 unsigned wraps around to 4294967291
be careful with mixing signed and unsigned integers.
If in doubt you can use typeinfo to see what the type is. It might be worth noting that g++ uses these codes for the types :
- signed int : i
- unsigned int : j
| typeid | |
|---|---|
So both operands 5u and 10 fit into an unsigned, but the answer does not.
implicit blocks
When using an if statement with 1 line, it is actually replace with an implicit block statement. So :
| implicit blocks | |
|---|---|
However, you would expect that to be the same for statements inside a switch case, but it is not :
| not equivalent | |
|---|---|
It is a subtle difference, but for some scope rules.
| {} needed or not | |
|---|---|
Also you can define variables inside the switch {} block, but you cannot initialize them.
| no initialization within switch {} block | |
|---|---|
The error you get when you initialize int x=11; before case labels is :
So this makes sense if a switch directly does a goto (jump) the label 11 it never reaches the initialization step. You will get similar errors when you goto or continue in a similar situation.
functions and arrays
Note that this does not generate an error :
| no error ! | |
|---|---|
This will print:
| output | |
|---|---|
What happens is that this is the stack frame of main at the time of calling func
The stack frame for func will be
| stackframe | |
|---|---|
At return it will be altered to :
| at return | |
|---|---|
nullptr
C define NULL as a macro for 0, but there is a better way using nullptr.
nullptr is also 0 but it has the correct type for the pointer you use it for. For instance this snippet :
double* A = nullptr; char* B = nullptr;
cout << typeid(A).name() << 'n'; cout << typeid(B).name() << 'n';
Will print
And if you feed those to c++filt :
Neat, whenever you encounter an old NULL, just replace it with nullptr.
Then there is also a std::nullptr_t version and that is type that is only capable of holding a nullptr. I cannot see the use for it however.
references
non-const references can (and must) only be initialized with non-const l values.
But !!, const references can also be initialized with const l values but also r values !!
The last one is a bit weird because what address is it referencing. Look at this code :
| explanation | |
|---|---|
We print the addresses of the references and the distance to the first variable x. This prints :
So the address of ref1 is the same as that of x, that is what to expect. But ref2 is definitely pointing to another variable on the stack, 1 after x, so i presume there is a temporary variable constructed for this on the stack frame. Normally r-values have 'expression scope' which means the 5 is destroyed as soon as int x = 5 terminated (x is now 5, but the =5 to create it has gone). The = 3 however get's an extended life span that matches that of it's reference.
pointers
x->y is syntactic sugar for (*x).y, so it dereferences the pointer to struct for you.
for (each) loops
It is NOT foreach it is just for with a different syntax.
You cannot get the index of a for loop since it works on constructs like linked lists which don't have an index.
Use a counter or a normal loop if you need the index !
To also alter the elements we need to do it by reference :
Don't forget e is the element, NOT the index. That's why i used e instead of i this time.
for (each) does NOT work with pointers to arrays, because the size has to be known for the loop.
constexpr
constexpr is a const expression that must be initialized at compile time, while const can be deferred until runtime. This makes constexpr a better candidate for optimization.
Don't use const/constexpr to be a good boy, it also promotes optimizations, especially constexpr
Always use constexpr unless the compiler complains.
| constexpr | |
|---|---|
default parameters
If you use default parameters beware that you can only declare it once, so you have to choose between the forward declaration OR the function definition:
| default parameters | |
|---|---|
I always did it in the function, but it might be better in the header file because that is what potential users see first. This advantage disappears when you don't offer the code as an interface library.
In any case i would add the other one as a comment as well
| comment default values | |
|---|---|
default parameters do not count in function overloading !
A clarifying example :
| example | |
|---|---|
default parameters do not work with function pointers !!, the defaults are replaced at compile time if absent. You can't do that with dynamic function pointers.
So you will need to supply all parameters yourself !
| works | |
|---|---|
const classes
Note that if you make classes const, some public functions might also become const.
Though getValue only reads it still get's the violates const error. It changes when you make that function const in the class itself.
| const function returning int | |
|---|---|
Now this will compile. Note that this 'trick' will not work in setValue() because that tries to alter a (now) const member which remains a const violation.
Make all readonly methods const, so they can remain to be used if you make a const object instance.
Constructors can not be const since they need to initialize their members, the language disallows constructors to be const.
This means the 'const Something something(4)' will just work and init to 4
It is possible to overload a function with it's const version. The const one will then be used for it's const instances.
So this works :
| works | |
|---|---|
Note that it would not make a difference if it was not returning a reference, because it would just return a separate copy of the member. But constness is considered part of the signature so these functions are different.
virtual functions
When using pointers to member functions pointers to base classes are assignable to pointers of derived classes. Not the other way around, because 'a dog is an animal' but 'an animal is not necessarily a dog'
This will print :
| output | |
|---|---|
As you see the assigned pointer prints the value of C, it's body is a C. But an A should now nothing about B or C classes so it prints its own i_am() function.
Making a method virtual enables the search to bubble down to the most derived candidate it can find. That means if you add 'virtual' to the i_am() function of A you get :
| call | |
|---|---|
So it bubbled down to the i_am() of class B, while still having the 'body' of C.Adding virtual to the i_am() of B, makes it go all the way down :
| output | |
|---|---|
Of course for this to work the methods should have identical prototypes.
Note that virtual functions are resolved at runtime, and thus ..slower !!
templates
function templates are filled in by the compiler according to the calls in the program.
| templates | |
|---|---|
This program will generate 2 functions because function is called with two different types.
| nm output | |
|---|---|
There is a slight difference in the signature (d vs i). But the compiler deduces how many of these are needed.
Not only typenames are eligible for parameterizing, but you then have to give more information :
| num parameters | |
|---|---|
These 2 will also generate different signatures, and also the number is reflected in there. So all combinations of all calls generates one function.
| nm output | |
|---|---|
I would probably add the
| use | |
|---|---|
cpplint
cpplint is a program that checks for visit. Since it is an automated tool and i have no real style preferences i adhere to cpplint, and use astyle to format (most of) it. Especially these rules cannot be formatted with astyle :
- at least two space between
- Extra space before last semicolon;
- Line ends in whitespace.
- Should have a space between // and comment
Besides that just create a filed called ~/.astylerc with this content :
And to indent a file run this inside vim :
| astyle | |
|---|---|
Zap compiler
One of the most annoying things about c++ programming is the slow compilation. Zap can really make an end to this.
Speeding up process :
Let's start with the normal compilation of the backend without any aid. We only compile debug + test, which is the most common compilation when developing. Also i will do these runs with and without parallel make :
method precompiled -j 1 -j 8
gcc no 2m18.307s 0m29.374s zapcc 1st run no 0m23.991s 0m13.715s zapcc 1st yes 0m23.246s 0m13.100s zapcc 2nd run no 0m16.368s 0m4.918s
For some reason precompiled headers don't have any effect anymore ?!?! Investigate that some time, in the mean time 4 seconds is VERY good, and also we don't have to keep track of the precompiled headers anymore, you don't have to regenerate them when changing something.
install zap compiler
The source comes from : visit
Sadly the compilation of the zap compiler itself is not so fast :
| compile the zap compiler itself | |
|---|---|
The whole idea of zap is to cache as much as possible. So a second compile will much faster that the first. Also a make clean does not mean really recompile everything. You need to kill the zapserver for that :