Cpp Basics - ashBabu/Utilities GitHub Wiki

Parallel C++

std::execution::par, works mainly for STL

#include <iostream>
#include <execution>
#include <vector>

int main()
{
   std::vector<int> v{1, 22, 54, 87, 56, 98};
   std::sort(std::execution::par, v.begin(), v.end());
   
   return 0;
}

check out std::async(), std::future

Thread building blocks (TBB)

  • sudo apt install libtbb-dev
  • Example
#include <iostream>
#include <vector>
#include <tbb/tbb.h>
#include <chrono>

int summarize(const std::vector<int>& vec) {
    int sum = 0;
    for (int i = 0; i < vec.size(); ++i) {
    sum += vec[i];
    }
    return sum;
}

int summarize_tbb(const std::vector<int>& vec) 
{
    // Parallel summation using TBB
    int sum = tbb::parallel_reduce(
        tbb::blocked_range<std::size_t>{0, vec.size()}, // Range of indices to process
        0, // Initial value for local sums
        [&vec] (const auto& r, int init)
        {
            for (auto i = r.begin(); i != r.end(); ++i)
            {
                init += vec[i];
            }
            return init;
        },
        std::plus<int>{}
    );

    // Another way
    /* 
    // Parallel summation using TBB
    tbb::parallel_reduce(
        tbb::blocked_range<size_t>(0, N),  // Range of indices to process
        0,  // Initial value for local sums
        [&](const tbb::blocked_range<size_t>& range, int local_sum) -> int {
            for (size_t i = range.begin(); i < range.end(); ++i) {
                local_sum += numbers[i];
            }
            return local_sum;
        },
        [](int x, int y) -> int {
            return x + y;  // Combine local sums into the final result
        });
    */

    return sum;
}


int main() {
    // Create a large vector with numbers from 1 to 1,000,000
    const size_t N = 1'000'000;
    std::vector<int> numbers(N);
    for (size_t i = 0; i < N; ++i) {
        numbers[i] = i + 1;
    }

    // Variable to store the result
    auto start = std::chrono::steady_clock::now();
    int sum_result = summarize(numbers);
    auto end = std::chrono::steady_clock::now();
    auto duration = end - start;
    std::cout<< "The sum is "<< sum_result<< " and the time taken for normal summation is "<< duration.count() <<"\n";


    start = std::chrono::steady_clock::now();
    int sum_tbb = summarize_tbb(numbers);
    end = std::chrono::steady_clock::now();
    duration = end - start;
    std::cout<< "The sum is "<< sum_result<< " and the time taken for tbb summation is "<< duration.count() <<"\n";

    return 0;
}
  • CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project(tbb)

# find dependencies
find_package(TBB REQUIRED)

include_directories(
    include
)

add_executable(tbb
    tbb.cpp
)

target_link_libraries(tbb PRIVATE TBB::tbb)
  • Result
The sum is 1784293664 and the time taken for normal summation is 5289687
The sum is 1784293664 and the time taken for tbb summation is 2713345

Lambda functions

  • Tutorial
  • Basic structure is [capture_clause](params){function_definition}
  • Example
#include <iostream>
#include <vector>
#include <algorithm>

int main()
{
    int d=7, e=5;
    std::vector<int> v{2, 5, 8, 25, 77};
   
    std::for_each(v.begin(), v.end(), [](int x){std::cout<<x<<"\n"});  // just prints out all the numbers in v

    std::for_each(v.begin(), v.end(), [d](int x){
           if (x%d == 0}
              std::cout<<x<<" is divisible by "<<d<<"\n";
           else
              std::cout<<x<<" is not divisible by "<<d<<"\n";
    );  // d is accessible but cannot be changed inside the function

    std::for_each(v.begin(), v.end(), [&d](int x){
           if (x%d == 0}
              std::cout<<x<<" is divisible by "<<d<<"\n";
           else
              std::cout<<x<<" is not divisible by "<<d<<"\n";
        d = 10;
    );  // d is accessible and can be changed inside the function
}
  • [&d](){} lets you change d inside function
  • [&d, &e](){} for access and change to both d and e (pass by reference)
  • [&d, e](){} for access and change to d and but cant change e
  • [&](){} for access and change to all variables inside the scope
  • [=](){} for access to all variables inside the scope

Preferred

  • Prefer \n over std::endl when outputting text to the console.

Initializations

Most Preferred: List initialization, int d {5}

In C++ 17, maybe_unused is allowed to suppress unused variable warning

int main()
{
    [[maybe_unused]] double pi { 3.14159 };
    [[maybe_unused]] double gravity { 9.8 };
    [[maybe_unused]] double phi { 1.61803 };

    std::cout << pi << '\n';
    std::cout << phi << '\n';

    // The compiler will no longer warn about gravity not being used

    return 0;
}

static and shared libraries

The difference is that a static library will generate a copy each time it is called, and the shared library has only one copy, which saves space. Create a file src/hello_ash_lib.cpp.

#include<iostream>

void print()
{
    std::cout<<"Hello_Ash  "<<std::endl;
}
  • In CMakeLists.txt,

    * add_library(hello_ash SHARED src/hello_ash_lib.cpp) will generate a `libhello_ash.so` shared library
    * add_library(hello_ash src/hello_ash_lib.cpp) will generate a `libhello_ash.a` static library
    
  • To make an executable with the above library, add the following in CMakeLists.txt,

     * add_executable(chp1 src/main.cpp)
     * target_link_libraries(chp1 hello_ash)
    

CMake Options

  • cmake .. -DCMAKE_INSTALL_PREFIX=/path/to/install/dir

Relative paths in C++

  • First get the path of the executable directory. A good tutorial is provided here which gives the path as /path/to/myBinaryFile
  • The following function strips the myBinaryFile part and gives the path to the directory /path/to/
#include <unistd.h>
#include <limits.h>
#include <string>

std::string getExePath()
{
    char result[ PATH_MAX ];
    ssize_t count = readlink( "/proc/self/exe", result, PATH_MAX );
    std::string path = std::string( result, (count > 0) ? count : 0 );
    std::string::size_type pos = path.find_last_of("\\/");
    return path.substr(0, pos);
}
  • Use INSTALL in CMakeLists.txt to add the path to yaml etc. For example,
install(TARGETS HAILaser_datafusion_app
        DESTINATION "${CMAKE_INSTALL_PREFIX}/bin/")

install(DIRECTORY "${CMAKE_SOURCE_DIR}/tests/config/"
        DESTINATION "${CMAKE_INSTALL_PREFIX}/bin/datafusion_app/yaml_configs/"
        FILES_MATCHING PATTERN "*.yaml"  # only the yamls
)
int main(int argc, char *argv[])
{
    std::string current_directory = getExePath();
    std::cout<<"THE CURRENT WORKING DIRECTORY is"<< current_directory << std::endl;
    std::string path_to_yaml = current_directory + "/datafusion_app/yaml_configs/HDF_RAW_V2.yaml";
    std::cout<<"PATH TO YAML IS: " << path_to_yaml<<std::endl;

}

Notes from A Tour of C++

  • const does runtime checking while constexpr does compile time checking
  • use error checks like std::assert(p!=nullptr) or std::static_assert(speed>max_speed, "cant go that fast")

Best resource on Modern CMake

Available here

1. Build explanation

cmake -S . -B build
cmake --build build
cmake --build build -t test

2. Write first CMakeLists.txt explanation

cmake_minimum_required(VERSION 3.15...3.21)

project(MyProject
  VERSION
    1.0
  DESCRIPTION
    "Very nice project"
  LANGUAGES
    CXX
)

add_executable(myexample simple.cpp)

install ccmake

  • sudo apt install cmake-curses-gui

Book references:

Tips on writing CMakeLists.txt

  • For custom packages which are built using the command sudo make install, the package name to be used in find_package(pkg_name REQUIRED) may not be obvious. It should generate a file under <install-path>/lib/cmake/<package-name> called something like <package-name>Config.cmake

  • cmake -S /path_to_src -B /path_to_build_dir # -S is source directory where the CMakeLists.txt is and -B path to the build folder

How to install C++ programs in linux

Extract the contents and change in the directory

./configure
make
sudo make install sudo ldconfig

OR

for example

git clone https://gitlab.com/libeigen/eigen.git
cd eigen
mkdir build
cd build
cmake ..

make install DESTDIR=/path/to/your/destination OR sudo make install

Update cmake

Binary
  • sudo apt-get install cmake # may not be the latest
Source
  • Download the latest .tar.gz version from here
  • cd $CMAKE_DOWNLOAD_PATH
  • ./configure
  • make
  • sudo make install
  • Might have to reboot

OR

  • wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | sudo tee /etc/apt/trusted.gpg.d/kitware.gpg >/dev/null
  • sudo apt-add-repository 'deb https://apt.kitware.com/ubuntu/ bionic main' # for ubuntu 18.04 sudo apt-add-repository 'deb https://apt.kitware.com/ubuntu/ focal main' # for ubuntu 20.04
  • sudo apt update
  • sudo apt install kitware-archive-keyring
  • sudo rm /etc/apt/trusted.gpg.d/kitware.gpg
⚠️ **GitHub.com Fallback** ⚠️