last update: 2024-10-06 at 12:00:11 CEST |
Notes C/C++ Development
Generic C++
// g++ -o test generic_function.cpp #include <iostream> #include <iomanip> #include <vector> auto greater = [](int& a, int param){ return a > param; }; auto smaller = [](int& a, int param){ return a < param; }; auto add = [](double& a, const double b, const double c){ a = a + b; return a > c; }; template<class Container, typename Lambda, typename... Args> auto generic(Container& container, const Lambda& lambda, Args ...args) { Container result; for(auto& item : container){ if(lambda(item, std::forward<Args>(args)...) ) result.push_back(item); } return result; } template<class Container> static void print(const Container& c){ for (const auto& i : c) std::cout << std::fixed << std::setprecision(2) << i << ' '; std::cout << "\n"; } int main(void){ std::vector<int> ints = {1, 2, 3, 4, 5, 6, 7}; print(generic(ints, greater, 3)); print(generic(ints, smaller, 3)); std::vector<double> floats = {1.0, 2.0, 3.0, 4.0}; print(generic(floats, add, 1.5, 3.0)); return 0; }
Speed Challenge - Stack vs Heap
class A { public: A(); ~A(); char c[1000000]; }; A::A(){} A::~A(){}
/*compile: g++ heap_allocate.cpp -o heap-allocate*/ #include <memory> #include "A.h" #define LOOPS 100000000 A* get_from_heap() { return new A(); } void heap_allocate() { for(int i=0; i<LOOPS; i++){ std::auto_ptr<A> a(get_from_heap()); } } int main() { heap_allocate(); }
Note
|
you can also use unique_ptr cause auto_ptr is marked as deprecated. then you have to compile with the -std=c++11 flag. But for some reason the execution time of heap-allocate will doubls (0m13.057s) |
/*compile: g++ stack_allocate.cpp -o stack-allocate*/ #include "A.h" #define LOOPS 100000000 A get_from_stack() { return A(); } void stack_allocate() { for(int i=0; i<LOOPS; i++){ A a=get_from_stack(); } } int main() { stack_allocate(); }
g++ --version g++ (Debian 4.7.2-5) 4.7.2
Result
time ./stack-allocate real 0m0.873s user 0m0.864s sys 0m0.004s
time ./heap-allocate real 0m5.322s user 0m5.316s sys 0m0.000s
Why is stack allocation so fast?
-
due to the stacks memory layout memory management requires less book keeping
-
addresses for allocation/freeing arise automatically (First In Last Out)
-
only one pointer (the stack pointer) has to be moved to point to a new address
-
-
stack allocation is hot, the memory is more likely to be in cache
-
heap allocation may first search for a free and big enough memory hole
-
the kernel has to be asked. It may split/merge/reuse/frees memory
-
Inheritance
#include <iostream> using namespace std; class Z { public: char x; Z(){x='Z'; cout << "Z()" << endl;} }; class B: public Z { public: B(){x='B'; cout << "B()" << endl;} }; class A: public Z { public: A(){x='A'; cout << "A()" << endl;} A(B b){x='A'; cout << "A(b)" << endl;} };
/*compile: g++ polymorphism.cpp -o polymorphism -Wall*/ #include "classes.h" using namespace std; Z getZasA() { return A(); } Z getZasB() { return B(); } A getAasB() { return B(); } int main() { cout << "a.x = " << A().x << endl; cout << "b.x = " << B().x << endl; cout << "getZasA().x = " << getZasA().x << endl; cout << "getZasB().x = " << getZasB().x << endl; cout << "getAasB().x = " << getAasB().x << endl; }
./polymorphism Z() A() a.x = A Z() B() b.x = B Z() A() getZasA().x = A Z() B() getZasB().x = B Z() B() Z() A(b) getAasB().x = A
Conversion
Convert String to Double using Boost
double val; std::str = "3.1416"; try{ val = boost::lexical_cast<double>(str); } catch (boost::bad_lexical_cast const&){ val = 0; }
Wrong Integer Comparison
This small programm shows the problem of comparing a signed integer against an unsigned one. A good compiler like g++ fortunately warns about this if using the switch for example the compiler switch -Wall. These Warnings should be taken serious. Here is why:
warning: comparison between signed and unsigned integer expressions
#include <stdio.h> #include <stdint.h> #include <inttypes.h> #include <iostream> using namespace std; int main(void) { uint64_t u = 5; int64_t s = -1; if(s < u){ cout << "s:"<<s<<" is smaller than u:"<<u<<endl; return 0; } cout << "s:"<<s<<" is greater than u:"<<u<<endl; cout << "FOOOOOP!"<<endl; return 1; }
The output of the programm is:
s:-1 is greater than u:5 FOOOOOP!
The reason why this happens is that the compiler does convert the singed value s to an unsigned value. Therefore s=18446744073709551615. In such a simple program this might look obvios but in a more complex scenario sometimes the compiler gives important hints. Another example:
#include <stdio.h> #include <stdint.h> #include <inttypes.h> #include <iostream> using namespace std; int main(void) { uint64_t u = 5; int64_t s = -1; if(s-u > 0){ cout << s-u <<" YEEEE!"<<endl; return 0; } cout << s-u <<endl; }
The output of the programm is:
18446744073709551610 YEEEE!
Namespaces
Use a Friend Class from a Different Namespace
Suppose we have class A in namespace foo. If we want class B (which is in the global namespace ::) to access private members of A we have to make class B a friend of A somewhere in class A we must define:
friend class B;
But the problem is that both classes reside in different namespaces. The compiler will complain with the very helpful message (g++)
The standats says:
Here is an example of how to access the private variable x inside the class A from function f() which is in class B. The correct way to declare the friendship is friend class ::B; Class B must be accessed via the :: operator to access the global namespace.
#include <iostream> #include "B.h" int main() { B b; std::cout << b.f() << std::endl; return 0; }
class B; namespace foo{ class A{ friend class ::B; private: int x; }; }
#include "A.h" class B { public: foo::A a; int f() { return a.x; } };
Compile this with the command e.g.
g++ -Wall -pedantic main.cpp -o friends
string Manipulation
The c++ programming language often offers multiple ways for the same task.
How to Declare a Static Const String as Class Member
In Java we can easily write:
public class A { static final String woo = "This is why"; }
in c++ we can only initialize static const member variables like int i=10; within our class (integral types). Otherwise we will get a compiler error like: invalid in-class initialization of static data member of non-integral type 'std::string'. → The correct way is to declare the member variable inside the class, but initialize it outside of the class. For example:
Header file A.h
class A { private: static const char woo[]; }
Source file A.cpp
const char A::woo[] = "This is why";
Note
|
I would suggest to always prefer a const char* over a const std::string for string constants unless there are good reasons against it. Because if you write const std::string boo = "Hi!" the constructor of boo will be called just before the execution of main() (a std::string requires dynamic initialization) and additionally the constructor will create a copy of boo on the heap. All this is avoided if using a const char*. |
Get String Lenght equivalent for a Char Array
If we declare std::string A we can use the A.lenght() mehtod to get the number of characters of the string. If we declare char B[10] we can use the mehtod int strlen(char *) to get the lenght.
std::string A = "test" char B[] = "test" A.lenght() strlen(B)
in both cases the length is 4
Search for Memory Leaks
Valgrind is a good tool for identifying memory leaks
Valgrind
valgrind --leak-check=full --show-reachable=yes -v
Pmap
Constantly print an overview of allocated pages by a pid or a group of them. The tool pmap reveals the memory allocated by malloc or mmap (anon region)
while true; do for pid in $(pidof luxconsole); do echo $pid; pmap $pid | grep anon ;done;sleep 1;done
Enable Core Dumps
Set the core file size to unlimited. Note: ulimit -a shows the current user limits.
ulimit -S -c unlimited > /dev/null 2>1
Debugging
gdb - The GNU Debugger
How to print a STL vector:
print *(vector._M_impl._M_start)@vector.size()
This only print N elements of the vector:
print *(vector._M_impl._M_start)@N
Print a backtrace of all running threads:
threads apply all backtrace
Note
|
this is the same but shorter: thr a a bt |
set the MALLOC_CHECK_ environment variable to 2, this will cause glibc to use an error tolerant version of malloc, which will cause your program to abort at the point where the double free is done.
You can set this from gdb by using the set environment MALLOC_CHECK_ 2 command before running your program; the program should abort, with the free() call visible in the backtrace.
GCC Compiler Errors
A collection of compiler errors and how to fix them
error: ISO C++ forbids declaration of 'int64_t' with no type
#include <stdint>
error: 'cout' was not declared in this scope
#include <iostream> using namespace std;
In file included from B.h:1:0, from main.cpp:2: A.h: In member function ‘int B::f()’: A.h:7:7: error: ‘int foo::A::x’ is private In file included from main.cpp:2:0: B.h:8:12: error: within this context
see here