14 May, 2021

Programming in C++

I started learning C++ with Turbo C++ and left programming in C++ with Microsoft Visual C++ MFC. It has been over 20 years since and C++ has evolved so much.

Very good books delivered with the Turbo C++ compiler
Install tools:
sudo apt install g++
sudo apt install g++-10
sudo apt install make

g++ --version shows the current version, which is 9.3.0 and g++-10 --version shows 10.2.0

Compile:

cc hello.cpp -o hello
#include "stdio.h"

int main() {
  printf("Hello World!\n");
}

Using GNU make, here is the Makefile:

CC = g++-10

CFLAGS = -g -std=c++20
OBJ := $(patsubst %.cpp,%.o,$(wildcard *.cpp))

main: $(OBJ)
    $(CC) -o main $(OBJ) $(CFLAGS)

%.o: %.cpp
    $(CC) -c -o $@ $< $(CFLAGS)

clean:
    rm -f *.o main

The main.cpp file:

#include <iostream>

int main() {
  std::cout << "Hello World!" << std::endl;

  return 0;
}

Include header-file only once like this:

#ifndef USER_H
#define USER_H
class User {};
#endif

CMake

I will be using CMake.

sudo apt install cmake

Here is the CMakeList.txt:

cmake_minimum_required(VERSION 3.10)

project(Program VERSION 1.0)

configure_file(ProgramConfig.h.in ProgramConfig.h)

target_include_directories(Program PUBLIC "${PROJECT_BINARY_DIR}")

set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -std=c++2a")
set (source_dir "${PROJECT_SOURCE_DIR}/src/")

file (GLOB source_files "${source_dir}/*.cpp") 

add_executable (Program ${source_files})

ProgramConfig.h.in

#define Program_VERSION_MAJOR @Program_VERSION_MAJOR@
#define Program_VERSION_MINOR @Program_VERSION_MINOR@

(todo: Explore more CMake features)

Debug with GDB

Use the compiler flag -g to add debug infos to the executable.

A debug session could go like this:

  1. Start with gdb main --tui where main is the executable.
  2. Set break-point b 34
  3. Step over to next line n
  4. Step into code/next line s
  5. Show variable p data
  6. End q

Attach to a running process:

gdb executable --pid=process-id --tui
Commands
b line-numberBreak at line line-number
nStep over to next line
sStep into next line
cContinue running
lPrint source code
p variable-namePrint contents of variable
dDelete breakpoint
qExit
rRun

C++ Language

Here are my notes about the current C++ language.

Variables

Use auto to declare variables whenever possible. It helps to avoid unintentional conversion, improves readability and is easier to maintain.

auto i = 1;

C++14 digital separators auto i = 1'000'000'000;

auto i{1};

auto i = {1}

`auto s = "This is a std::string"s;

C++14 binary digitals auto x = 0b00110101;

Constexpr template with variables:

template <typename T>
constexpr T pi = T{3.14159};

template <>
constexpr int pi<int>{3};

int main() {
  std::cout << pi<double> << std::endl;
  std::cout << pi<int> << std::endl;
  return 0;
}

Flow control

for (auto i : array) { }

for (auto& n : array) { n += 2; }

for (auto const& e : entries) { }

C++17 declare variables in if- and switch-block

if (const auto x = get_x(); x < 10) {
}

switch (const auto x = get_x(); ...) {
}

C++17 if constexpr is evaluated by the compiler

if constexpr (x == 1) {
} else {
}

Enum

enum class AccountType { A, B, C };

Strings

std::string, std::wstring, std::string_view

find_if

Raw strings:

const char* str = R"";

Regex

std::regex

#include <regex>

int main() {
  std::string s = "This is the text we apply a regular expression on.\n"
    "\"Start this in a new line in double-quotes.\"";
  std::regex a_regex("Quote", std::regex_constants::icase);

  if (std::regex_search(s, a_regex)) {
    std::cout << "Found!" << std::endl;
  }

  return 0;
}

Functions

inline void f() noexcept {
}

auto g(double x) {
  return x * x;
}

[[deprecated]]
void h() {
}

[[deprecated("Use f instead.")]]
void g() {
}

Use braced bracket initialization to call f().

struct Point {
  int x;
  int y;
}

void f(Point p) {
}

int main() {
  f({1, 2});
}

noexcept is used for compile-time optimization. There is a noexcept() operator that can give the info at compile-time whether an expression can throw an exception.

  • Use inline for small functions (containing 1-2 statements)

  • Use snake-case for function names.

  • Place { not in separate line (Ignore the exception for { for functions in NL.17 of the C++ Core Guidelines, no separate line required)

    A function can have unnamed parameters. This is to indicate that the parameter is not used but keep the interface the same. Either because the caller cannot be changed or because to implement a virtual function of a base class.

Classes

class and struct are basically the same with public access as default for struct and private for class. A struct class cannot be used in template parameter typename.

A class can extend from a struct and the other way round too.

struct A {
  int a{1};
};

class B: protected A {
public:
  int b{2};
};

struct C: B {
  int c{3};
};

Constructor explicit default deleted

Member functions virtual

  • mutable in member variable declaration
  • nested class declaration
  • typdef class forward declaration in surrounding class

std::get std::get_if

Use Pascal-case for class-names.

Friend

#include <iostream>

class B;

class A {
  friend class B;
  std::string sentence{"I'm A."};

public:
  void say_hello(A& a, B& b) {
    std::cout << "A:" << std:: endl;
    std::cout << a.sentence << std::endl;
    // A is not friend of B
    // std::cout << b.sentence << std::endl;
  }
};

class B {
  std::string sentence{"I'm B."};

public:
  void say_hello(A& a, B& b) {
    std::cout << "B:" << std::endl;
    std::cout << a.sentence << std::endl;
    std::cout << b.sentence << std::endl;
  }

  friend void say_hello_friend(A&, B& b) {
    // this function is not friend of A but has full access to B and is not member of B
    // std::cout << a.sentence << std::endl;
    std::cout << "Friend function of B:" << std::endl;
    std::cout << b.sentence << std::endl;
  }
};

int main() {
  const A a;
  const B b;
  a.say_hello(a, b);
  b.say_hello(a, b);
  // say_hello_friend is not mbember of B
  // b.say_hello_friend(a, b);
  say_hello_friend(a, b);

  return 0;
}

RAII - Resource Acquisition is Initialization

Ref-Qualifier

Member functions and operator overloading can have a lvalue or a rvalue ref-qualifier. Mixing with non-ref-qualifier is not allowed.

struct A {
  void f();
  void g() &;  // lvalue ref-qualifier
  void g() &&; // rvalue ref-qualifier
  void h();    // error, mixing not allowed
  void h() &;  // error, mixing not allowed
  void i() &;
  void i() const &;
  A* operator &() &;          // only lvalue allowed
  A& operator =(A const&) &; // only lvalue allowed
};

int main() {
  A* pA = &A(); // does not compile
  A() = A();    // does not compile
}

Use ref-qualifier to differentiate between move and copy.

class B {
  std::vector<char> data;
public:
  std::vector<char> const& get_data() const & {
    return data;
  }

  std::vector<char>&& get_data() && {
    return std::move(data);
  }
};

int main() {
  B b;
  std::vector<char> i = b.get_data();     // copy
  std::vector<char> j = (B()).get_data(); // move
}

CTAD Class Template Argument Deduction

Const

StackOverflow

First const can be placed either side of type:

const int * const == int const * const

Placing it after is more consistent but it is against the C++ Core Guidelines. The guidelines is inconsistent with the exception to the rule.

int const three = 3; - Local variable

for (auto const& e : v) - Local loop variable

void f(Quote const& q); - Function parameter

int g() const; - Member function does not change member variables.

volatile indicate to the compiler not to do some type of optimization.

mutable specifier for non static class member. It makes a const class member mutable. Used in context with std::mutex.

Constexpr

With constexpr the compiler can evaluate and optimize at compile-time.

constexpr int f(int x) {}

constexpr int y = f(1);

Lambdas

Lambdas: [](){} vs function objects

[] - Contains capture of variables. Default is by value.

[a, &b] - a by value and b by reference.

`[c = 12] - Init capture.

Capture a non-copyable variable by moving:

std::unique_ptr<A> a;
auto lambda = [b{std::move(a)}]()( *b = };
int main() {
  std::vector<int> v{4, 1, 4, 1, 3, 6, 3, 7, 2, 3, 6, 4};

  for_each(begin(v), end(v), [](int i) { std::cout << i << ";"; });
}

`std::visit`

### Destructuring, std::tuple

Since C++17 destructuring assignment on declaring variables is available. Similar to JavaScript but only for declaring variables.

```cpp
std::tuple<int, double, std::string> split(std::string const& line) {
  // do the split
  return std::make_tuple(3, 3.1415, "pi");
}

...

auto [id, value, name] = split("3; 3.1415; pi");
auto second = std::get<1>(split("3; 3.1415; pi"));
auto third = std::get<std::string>(split("3; 3.1415; pi"));

int a = 0;
double b;
std::string c;

std::tie(std::ignore, b, c) = split("...");

std::pair

std::make_tuple - Create a tuple.

std::get - Get value out of a tuple.

std::tie - Create a tuple of lvalue references.

std::ignore - Placeholder for std::tie

Fold Expression

C++17 Using the ... to indicate a fold expression.

Pointers

nullptr represents null

std::unique_ptr

std::shared_ptr

std::make_unique

Use nullptr instead NULL

Rule of three

  • If one of copy constructor, destructor, copy assignment operator is defined, then define all three.

decltype

// todo add decltype code

Move Semantics

Back to Basics: Move Semantics p1 Back to Basics: Move Semantics p2

Templates

std::any std::optional

SFINAE - Substitution Failure Is Not An Error (What's that?)

Exceptions

throw std::runtime_error("some resource could not be allocated");
try {
} catch (std::bad_variant_access const& e) {
  std::cout << e.what() << std::endl;
}

Operator Overloading

#include <iostream>

struct Point {
  int x;
  int y;

  /*
  constexpr bool operator==(const Point& rhs) const noexcept { return (x == rhs.x) && (y == rhs.y); }
  */
  constexpr bool operator==(const Point&) const noexcept = default;
  constexpr auto operator<=>(const Point&) const noexcept = default;
};

int main() {
  Point p1 = {1, 1};
  Point p2 = {1, 2};
  Point p3 = {1, 1};

  auto eq1 = p1 == p2;
  auto eq2 = !(p1 != p2);
  auto eq3 = !(p1 != p3);

  std::cout << "eq1 " << eq1 << std::endl;
  std::cout << "eq2 " << eq2 << std::endl;
  std::cout << "eq3 " << eq3 << std::endl;

  auto threeway = (p1 <=> p2) < 0; // less
  auto threeway2 = (p2 <=> p1) > 0; // greater
  auto threeway3 = (p1 <=> p3) == 0; // equal

  std::cout << "threeway " << threeway << std::endl;
  std::cout << "threeway2 " << threeway2 << std::endl;
  std::cout << "threeway3 " << threeway3 << std::endl;

  Point p4 = {2, 1};

  auto threeway4 = (p2 <=> p4) == 0; // equal
  auto threeway5 = (p2 <=> p4) <  0; // equal

  std::cout << "threeway4 " << threeway4 << std::endl; // 0
  std::cout << "threeway5 " << threeway5 << std::endl; // 1
  return 0;
}

Casting

const_cast

dynamic_cast

reinterpret_cast

static_cast<int>(3.14)

Attributes

variant instead unions

std::variant

Files and Streams

  • Read/write files

Network I/O

Socktes, Web-Sockets

Threads

std::thread std::async std::promise std::future

Avoid static, use thread_local instead.

Avoid global data.

Protect static variables with static std::mutex.

Coroutines

Constraints and Concepts

C++20: concept requires

STL Standard Library

std::array

Functions
swapSwaps the contents of two arrays.

std::vector

Functions
capacity-
clear-
insert-
pop_back-
push_back-
reverse-
shrink_to_fit-
size-

std <algorithm>: all_of, none_of, any_of for_each, copy, copy_if

Operations with Iterators
std::distanceReturns the steps used to get from first past the last.
std::inplace_mergeMerges two sorted ranges into one sorted range.
std::partitionSeparates a list of elements by a predicate so that true elements comes before false elements.
std::sortSort a list of elements.
std::uniqueMake a list of elements unique by deleting elements except the first of the same.
std::count_if
#include <iostream>
#include <algorithm>
#include <vector>

int main() {
  std::vector<int> v{4, 1, 4, 1, 3, 6, 3, 7, 2, 3, 1, 4};

  for_each(begin(v), end(v), [](auto e){ std::cout << e << " "; });
  copy(begin(v), end(v), std::ostream_iterator<int>(std::cout, " "));

  auto all = all_of(begin(v), end(v), [](auto e){ return e == 4; });
  auto any = any_of(begin(v), end(v), [](auto e){ return e == 4; });
  auto none = none_of(begin(v), end(v), [](auto e){ return e == 4; });

  std::cout << std::endl << "all_of any_of none_of" << std::endl;
  std::cout << all << "      " << any << "     " << none << std::endl;

  return 0;
}

std: atomic

variant, visit

std <numeric>: partical_sum

std::min - Function returns min of two or min of begin and end iterator. An optional comarator can be supplied.

std::max - Function returns max of two or max of begin and end iterator. An optional comparator can be supplied.

std::max_element - (What is the difference to std::max?)

std::stable_sort - Sorts elements where the present order of equal elements are preserved.

std::sqrt - Square-root function.

std::numeric_limits

std::binary_function

std::swap - Swap two values.

std::queue

std::random_device

std::ranlux48 - 48-bit RANLUX generator

std::istringstream std::ostringstream

std::isalpha std::isdigit

WebAssembly

Database Access

Efficiency and Performance

Compare generated output in assembly language:

g++ -S main.cpp
g++ -masm=intel -S main.cpp

or

objdump -M intel -S --disassemble main.o > main.s

std::chrono performance measurements

Branch prediction (fewer branches, deterministic)

  • Do as much as possible at compile time
    • Pre-populated data-structure
    • Rearrange statements and blocks
  • Trust private code and do less validation
  • Watch out for virtual methods and replace with template (CRTP Curiously recurring template pattern) (memory vs runtime)
  • C++20 Use [[likely]] and [[unlikely]] to help the compiler
  • Data dependent, reorder data. Test the example on StackOverflow

Cash misses

  • Warmup criticall vip-code-path with skip-finally flag

  • No system calls

  • No memory allocation after warmup phase

  • No context switching (todo)

Web with C++

Crow - C++ micro web framework (comparable to Node.js + Express?) https://github.com/ipkn/crow

HttpLib - C++ header-only HTTP/s server/client library https://github.com/yhirose/cpp-httplib

Pistache - REST framework https://github.com/pistacheio/pistache

Restbed - REST framework https://github.com/Corvusoft/restbed

Restinio - C++ header-only HTTP/s server framework https://github.com/Stiffstream/restinio

Microsoft C++ REST SDK - (retired) https://github.com/microsoft/cpprestsdk

JSON

RapidJSON https://rapidjson.org/

Standard C++ https://isocpp.org

C++ Reference https://en.cppreference.com/

C++ Core Guidelines http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines

Google C++ Style Guide https://google.github.io/styleguide/cppguide.html

Compiler Explorer https://godbolt.org

CPP Insights https://cppinsights.io/

Microsoft C++ Language Documentation https://docs.microsoft.com/en-us/cpp/cpp/

Cross-platform mobile development with C++

Boost C++ Libraries https://www.boost.org/

Boost.DI - Boost Dependency Injection https://github.com/boost-ext/di

LLVM Compiler https://llvm.org/

Node.js C++ addons https://nodejs.org/api/addons.html#addons_c_addons

Microsoft Guidelines Support Library https://github.com/Microsoft/GSL

C++ Draft http://eel.is/c++draft/

WG21 redirect service https://wg21.link/

Bloaty McBlatface - A size profiler for binaries https://github.com/google/bloaty

Blogs

Fluent {C++} - Jonathan Boccara https://www.fluentcpp.com/

Modernes C++ - Rainer Grimm https://www.grimm-jaud.de/index.php/blog

Exlpore

RmlUi - The HTML/CSS User Interface Library Evolved https://github.com/mikke89/RmlUi

Not Enough Standards - Platform-independent utilities https://github.com/Alairion/not-enough-standards