12 April, 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-get install g++
sudo apt-get install g++-10

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 = -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>

using std::cout;
using std::endl;

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

  return 0;
}

CMake

I will be using CMake.

sudo apt-get 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@

Difference to C# and Java

new has different meaning and is used to allocate memory in the heap. Use this with delete in mind

const is more widely used

virtual must be used explicitely

this

this->name = name;
return *this;

References must be used explicitely.

Topic

  • Explore more CMake features.
  • Operator declaration and implementation with and without class.
  • Casting: const_cast, dynamic_cast, reinterpret_cast, static_cast<int>(3.14)
  • Learn more about current STL
  • for_each, begin(), end(), move(), make_unique<>()

Variables

auto i = 1;

auto i{1};

auto i = {1}

auto x = 0b00110101;

Flow control

for (auto const& e : entries) { }

Enum

enum class AccountType { A, B, C };

Strings

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

Functions

inline void f() noexcept {

}

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

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

std::visit

Destructuring, std::tuple

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

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");

std::pair

Pointers

nullptr represents null std::unique_ptr std::shared_ptr

Use nullptr instead NULL

Rule of three

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

Templates

std::any std::optional

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>

using std::cout;
using std::endl;

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

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

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

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

  Point p4 = {2, 1};

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

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

Attributes

variant instead unions

std::variant

Files and Streams

  • Read/write files

Network I/O

Socktes, Web-Sockets

Threads

std::thread std::async

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 <algorithm>: all_of, none_of, any_of

#include <iostream>
#include <algorithm>
#include <vector>

using std::cout;
using std::vector;
using std::endl;

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

  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; });

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

  return 0;
}

std: atomic, variant, visit

std <numeric>: partical_sum

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)

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

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