Save 37% off PRO during our Black Friday Sale! »

Introduction to C++ Part II

Introduction to C++ Part II

F6baf93a0833a98bdc8184c214f4c468?s=128

Rohit Goswami

April 14, 2021
Tweet

Transcript

  1. INTRODUCTION TO C++ PART II ROHIT GOSWAMI MINSTP Created: 2021-04-13

    Tue 04:58 1
  2. 1 BRIEF INTRODUCTION 2 . 1

  3. 1.1 HELLO! Find me here: Who? Rohit Goswami MInstP Doctoral

    Researcher, University of Iceland, Faculty of Physical Sciences https://rgoswami.me 2 . 2
  4. 1.2 LOGISTICS All contents are Slides are in docs/pres hosted

    on GitHub Slides have shortned commit IDs These lead to speci c points in the git log Questions are welcome after / during the lecture have been set up for this GitHub Discussions 2 . 3
  5. 2 LOCAL PROJECT LAYOUTS 3 . 1

  6. 2.1 LANGUAGE AGNOSTIC BEGINNINGS Readme.{md,org} Motivation, rationale, license, installation instructions

    LICENSE Plain text, and preferably an open license is pretty handy for this license-generator .gitignore Lists les which do not need to be committed; typically generated les can be used to generate these gibo $ git init # Inside project $ gibo macOS Windows Xcode Emacs \ Vim Python C \ CMake TeX > .gitignore $ touch readme.md $ license-generator MIT \ author "Person" $ tree -L 2 . ├── LICENSE ├── docs │ └── pres └── readme.org 2 directories, 2 files sha256:9c29414 3 . 2
  7. 2.2 C++ PROJECT STRUCTURE src/ Should be split into libraries

    and executables include/ For headers, internal and external docs/ Documentation of all kinds, typically including markdown les CMakeLists.txt The project build system ci/ Scripts for continuous integration 1 Along each -I and INCLUDE #include<iostream> Starts in the same directory Reverse order upward -I and INCLUDE #include "path-spec" sha256:a5a0a9e 3 . 3
  8. 3 ITERATIVE IMPROVEMENTS - I 4 . 1

  9. Comments are imperative Segregation of build allows for easy cleanup

    rm -rf build 3.1 REFACTORING CMAKEFILE - I # Variables if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release endif() set(CMAKE_CXX_FLAGS "-Wall -Wextra") set(CMAKE_CXX_FLAGS_DEBUG "-g") set(CMAKE_CXX_FLAGS_RELEASE "-O3") $ # Should fail!!! $ cmake . sha256:8193ad9 cmake_minimum_required(VERSION 3.14 FA # ---- Project ---- project( PenningTrapSimulationII VERSION 1.0 LANGUAGES CXX ) # ---- Include guards ---- if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR) message( FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there." ) endif() $ # Works $ cmake -H. -Bbuild $ cmake build build $ cd build $ simulation.exe 4 . 2
  10. 3.2 HEADER ONLY INCLUDES These are meant for small classes

    With inlined de nitions #ifndef IOP_VEC_H #define IOP_VEC_H #endif IOP_VEC_H Prevents multiple inclusion Modularity means more unit-tests Better guarantees # Library set(headers "${CMAKE_CURRENT_SOURCE_DIR}/include set(sources "${CMAKE_CURRENT_SOURCE_DI # Build add_executable(simulation.exe ${source # Add to INCLUDE target_include_directories( simulation.exe PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_D ) We prefer <> to "" for includes #include <math_types/iop_vec.hpp> sha256:9ca7f3a 4 . 3
  11. 3.3 NAMESPACES Further improves modularity Fewer clashes Never use using

    namespace iopdat { data_types/iop_particle.hpp class Particle { public: Vector3 position; } } Same conceptual region, even across les namespace iopdat { data_types/iop_vec.hpp class Vector3 { } } set(headers "include/data_types/iop_vec.hpp" "include/data_types/iop_particle.hpp ) #include <data_types/iop_vec.hpp> #include <data_types/iop_particle.hpp> Regrouped the data_types math_types wasn’t primitive enough Refactoring early is normal With tests sha256:2f1b161 4 . 4
  12. Before After 3.4 COMPILED LIBRARIES class TimeStepper { private: std

    vector<PhysicsProcess *> physics_list; iopdat Particle p; double dt; public: TimeStepper(double time_step_size, double charge, double mass) : p(charge, mass) { dt = time_step_size; } }; class TimeStepper { private: std vector<PhysicsProcess *> physic iopdat Particle p; double dt; public: TimeStepper(double time_step_size, double charge, double mass); void AddProcess(PhysicsProcess* process); void Setup(iopdat Vector3 position, iopdat Vector3 Velocity) void Step(); void Print(); }; 4 . 5
  13. Header Only Compiled 3.4.1 WHY? Easier to install Can be

    dropped in and versioned Slow, especially installed They are expanded Compiled code is faster Adds complexity though Needs a build system Can easier to read More modular Easier to document $ tree -L . . ├── CMakeLists.txt ├── LICENSE ├── docs │ └── pres ├── include │ ├── data_types │ └── phys_procs.hpp ├── libsrc │ └── phys_procs.cpp ├── readme.org └── src ├── CMakeLists.txt └── main.cpp 6 directories, 7 files 4 . 6
  14. 3.4.2 HOW? - I CODE SEPARATION declared in .hpp /**

    * @brief Takes a step * @returns None. void Step(); The build system takes on most of the debt Compiles the code as a library “header-only” libraries can also be built Ensures it can be linked at runtime de ned in .cpp /** * @detail The algorithm is a direct * of standard mechanics void physproc TimeStepper Step() { iopdat Vector3 F(0, 0, 0); for (int i = 0; i < physics_list.size(); i ) { F += physics_list.at(i) Force(p); } iopdat Vector3 dv(dt * F.x / p.mass, dt * F.y / p.mass, dt * F.z / p.mass) p.velocity += dv; p.position += iopdat Vector3(p.velocity.x * dt, p.velocity.y * dt, p.velocity.z * dt) } 4 . 7
  15. 3.4.3 HOW? - II BUILDING AND LINKING For header-only libraries

    PUBLIC -> INTERFACE target: add_library(ioplib INTERFACE) main.cpp #include <phys_procs.hpp> sha256:e6d048e # Build order add_dependencies(simulation.exe ioplib # Libraries target_link_libraries(simulation.exe ioplib) # Dependencies add_library( ioplib SHARED "libsrc/phys_procs.cpp" ) # Still need the older headers target_include_directories( ioplib PUBLIC $<BUILD_INTERFACE: ${PROJECT_SOURCE_DIR}/include> ) 4 . 8
  16. 3.4.4 WHEN? Larger projects refactor often Modular components make it

    easy for new contributors Reuse of code is easier this way The speed bene ts can be considerable Speed here means compilation time boost is a classic example of a mostly compiled library Eigen3 is famously header-only Let there be libraries!! — Rohit Goswami (2021) 4 . 9
  17. 4 PACKAGE MANAGEMENT 5 . 1

  18. 4.1 CURRENT SCENARIO 2 Nix is the answer!! (not here)

    Python poetry, pipenv, pyenv C++ conan, vcpkg, cpm
  19. 5 . 2

  20. 4.2 CPM AND CMAKE mkdir -p cmake wget -O cmake/CPM.cmake

    "https github.com/cpm-cmake/\ CPM.cmake/releases/latest/download/get_cpm.cmake" # Can get slow otherwise, rebuilds constantly export CPM_SOURCE_CACHE=$HOME/.cache/CPM # Helpers include(cmake/CPM.cmake) CPMUsePackageLock(package-lock.cmake) # We like locks cmake -H. -Bbuild cmake build build target cpm-update-package-lock 5 . 3
  21. 5 TESTING AND CONTINUOUS INTEGRATION 6 . 1

  22. 5.1 TESTING FRAMEWORKS C++ has great testing frameworks Catch2, googletest,

    doctest, etc. Unit tests are the rst layer Ensure each function outputs as expected Integration tests are for work ows Ensure each series of tasks connect correctly # Catch2 CMakeLists.txt include(CTest) add_subdirectory(tests) enable_testing() # Externals ./tests/CMakeLists.txt include( /cmake/CPM.cmake) CPMAddPackage("gh:catchorg/Catch2@2.13 add_executable(particle_tests main.cpp vector_particle-test.cpp phys_processes-test.cpp ) # Link everything target_link_libraries(particle_tests ioplib Catch2) target_compile_features(particle_tests # Project Libraries include_directories( /src/include/data_types ${PROJECT_SOURCE_DIR}/src/include/) # ---- Run tests ---- add_test(NAME particleIOP-unit-tests COMMAND $<TARGET_FILE:particle_tests>) 6 . 2
  23. 5.2 WRITING TESTS #define CATCH_CONFIG_MAIN #define CATCH_CONFIG_RUNNER #include <catch2/catch.hpp> The

    macros must not be repeated Many more test scenarios d-SEAMS has examples O cial docs are great Try xing gravity as homework! Open a PR (pull request) when done #include <catch2/catch.hpp> #include <data_types/iop_particle.hpp> #include <data_types/iop_vec.hpp> #include <phys_procs.hpp> #include <iostream> TEST_CASE("Basic Vector class tests", iopdat Vector3 a{1, 2, 3}; REQUIRE(sizeof(a) 24); REQUIRE(sizeof(a) / sizeof(1) 6); REQUIRE(sizeof(a) / sizeof(1.0) 3); iopdat Vector3 b{4, 5, 6}; b += a; REQUIRE(b.x 5); REQUIRE(b.y 7); REQUIRE(b.z 9); std cout b.x; } sha256:d9ded32 6 . 3
  24. 5.3 CONTINUOUS INTEGRATION No one likes switching computers to test

    MacOS, Windows (WSL often), Many Linux distributions Some tests run for a long time Less attractive locally nixpkgs can take over a day! There are far too many options nowadays Wercker, Travis CI, Shippable, GitLab CI, Github Actions Mostly transient docker or nix based systems Setup can be annoying without nix 6 . 4
  25. 5.4 GITHUB ACTIONS local tests act allows name: CMake on:

    [push] env: BUILD_TYPE: Release jobs: build: runs-on: ${{ matrix.OS }} name: "${{ matrix.BUILD_TYPE }}" strategy: matrix: include: - BUILD_TYPE: Debug OS: ubuntu-latest - BUILD_TYPE: Debug OS: macos-latest - BUILD_TYPE: Debug ARCH: x86_64 OS: windows-latest # broken steps: - uses: actions/checkout@v2 - name: Create Build Environment run: cmake -E make_directory ${{git - name: Configure CMake shell: bash working-directory: ${{github.worksp run: cmake $GITHUB_WORKSPACE -DCMAK - name: Build working-directory: ${{github.worksp shell: bash run: cmake build . config $BUIL - name: Test working-directory: ${{github.worksp shell: bash run: ctest -C $BUILD_TYPE sha256:1293af3 6 . 5
  26. 6 EMBEDDING AND MIXED COMPILATION 7 . 1

  27. 6.1 WHY EMBED? 7 . 2

  28. 6.1.1 VISUALS?? File I/O is su cient simulate.exe > someFile.txt

    import numpy as np import matplotlib.pyplot as plt import matplotlib.animation as animation import pandas as pd df = pd.read_csv("someFile.txt", sep=" ") # Do stuff now Can be better for HPC systems Why embed / interpolate? 7 . 3
  29. 6.2 BETTER EXAMPLES 7 . 4

  30. 6.2.1 D-SEAMS Uses lua to expose a scripting engine Computation

    is still C++ Reduces compilation requirements Increases exibility [goswamiDSEAMSDeferredStructural2020] 7 . 5
  31. 6.2.2 EON EON uses a server client architecture Is meant

    for distributed use [henkelmanLongTimeScale2001]
  32. 7 . 6

  33. 7 PYBIND11 8 . 1

  34. 7.1 EMBEDDING PYTHON Example adapted from here pip install matplotlib

    CPMAddPackage( NAME pybind11 GITHUB_REPOSITORY pybind/pybind11 GIT_TAG v2.6.1 ) target_link_libraries(simulation.exe ioplib pybind11 embed) py scoped_interpreter #include <pybind11/embed.h> #include <pybind11/stl.h> namespace py = pybind11; int main() { std vector<double> signal(1024); for (size_t i = 0; i < signal.size(); i) signal[i] = std exp(i / -256.0) * std cos(2 * M_PI * 8 * i / 1024.0) py scoped_interpreter guard{}; using namespace py literals; Save the necessary local variab in a Python dict py dict locals = py dict{ "signal"_a = signal, }; Execute Python code, using the saved in `locals` py exec(R"( import matplotlib.pyplot as plt plt.plot(signal) plt.show() )", py globals(), locals); } sha256:5c21716 8 . 2
  35. 7.1.1 SAMPLE RUN

  36. 8 . 3

  37. 7.2 PROTON STEPPER Getters double physproc TimeStepper getX() { return

    p.position.x; } Accumulators std vector<double> x, y, z; for (int i = 0; i < 1E3; i ) { ProtonStepper.Step(); if (i % 7 0) { x.push_back(ProtonStepper.getX()); } } py scoped_interpreter guard{}; using namespace py literals; py dict locals = py dict{ "x"_a = x, }; py exec(R"( )", py globals(), locals); sha256:59555bc from mpl_toolkits.mplot3d import Axes3 import matplotlib.pyplot as plt fig = plt.figure() ax = fig.add_subplot(111, projection=' ax.scatter(x, y, z, c='r', marker='o') plt.show() 8 . 4
  38. 8 CONCLUSIONS 9 . 1

  39. 8.1 OMITTED TOPICS Documentation Possibly the most underrated skill in

    compuational work Package Management Managing OS’s HPC and Parallelism E cient data usage and algorithms Code Review Practices Scrum and teamwork Inter process communication Across networks and process, including serialization 9 . 2
  40. 8.2 FURTHER RESOURCES Community maintained, discusses features from C++11 onwards,

    spearheaded by Changkun Ou Maintained by Bjarne and Herb, great to get the pulse of the community Has a surprisingly good introduction and Package management at the system level in a reproducible manner Describes the present SOTA for documentation practices in the context of a large multi-language project Has a solid tutorial on tests and frameworks in general Modern C++ Tutorial C++ Core Guidelines Microsoft Visual Studio even some projects A Tutorial Introduction to Nix SymEngine and the Season of Docs CLion 9 . 3
  41. 9 THE END 10 . 1

  42. 9.1 BIBLIOGRAPHY Goswami, Goswami & Singh, D-SEAMS: Deferred Structural Elucidation

    Analysis for Molecular Simulations, Journal of Chemical Information and Modeling, 60(4), 2169-2177 . . Henkelman & Jónsson, Long Time Scale Kinetic Monte Carlo Simulations without Lattice Approximation and Prede ned Event Table, The Journal of Chemical Physics, 115(21), 9657-9666 . . . [goswamiDSEAMSDeferredStructural2020] doi [henkelmanLongTimeScale2001] link doi 10 . 2
  43. 9.2 THANKS! 10 . 3