rturrado / the_modern_cpp_challenge

My solutions to the 100 problems of The Modern C++ Challenge, a book by Marius Bancila (published by Packt).

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

main CI unixlike-builds CI codecov

The Modern C++ Challenge

My solutions to the 100 problems of The Modern C++ Challenge, a book by Marius Bancila (published by Packt).

These exercises were initially implemented as a project within a bigger Visual Studio solution, Catching up with modern C++, as a way of practicing modern C++ (11/14/17/20). However, anyone downloading that project would need to:

  • Make sure they have installed all the libraries used in the code.
  • And update the Visual Studio project properties containing absolute paths to those libraries (e.g., C/C++ > General > Additional Include Directories, and Linker > Input > Additional Dependencies).

The goal behind The Modern C++ Challenge project is twofold. On one hand, let users download, build, and execute the code with a minimal effort (ideally without needing to install any library). On the personal side, get some hands-on experience with CMake and other tools of the C++ ecosystem. I've also taken the chance to improve the original code with:

  • Tests (google tests).
  • Benchmarks (google benchmarks).
  • Static analysis (CLion's Inspect Code).
  • Memory error detection (address sanitizer).
  • Continuous integration (GitHub actions).
  • Code coverage (gcov).
  • Compiler caching (sccache).

Branch status

  • The main branch only builds on Windows/msvc.
  • The unixlike-builds branch only builds on Unix-like systems.
  • main release v2.0.0 and unixlike-builds release v1.0.0 provide the same functionality.

Since the unixlike-builds branch uses gcc, which doesn't have support yet for all the C++20 features (especially the <format> library), a few problems are implemented differently in that branch. Basically, the date and time problems use Howard Hinnant's <date> library instead of the <format> library. This should be the only difference between main release v2.0.0 unixlike-builds release v1.0.0.

Once gcc will have support for all the C++20 features, the main branch should be suitable to be used in Unix-like systems, and the unixlike-builds branch should be dropped.

The instructions below refer to Windows/msvc.

Installation

Requirements

  • Boost libraries: this project has been tested with version 1.78.0.
  • Microsoft Visual C++ (MSVC) compiler toolset.
  • CMake: required minimum version is 3.22.
  • git.
  • sccache: only if you are going to use a *ccache or *github build preset.

Most of the required software can be installed from the Visual Studio 2022 Installer.

  • Microsoft Visual C++ (MSVC) compiler toolset: Workloads tab, Desktop development with C++ item.
  • CMake: Individual components tab, Compilers, build tools, and runtimes section, C++ CMake tools for Windows item.
  • git: Individual components tab, Code tools section, Git for Windows item.

You can download the current version of the Boost libraries from https://www.boost.org/users/download/.

  • Notice it's not necessary to build them, since the code only uses headers.
  • Create a BOOST_ROOT environment variable, and set it to Boost libraries's root directory (e.g. C:\libraries\boost_1_78_0).

Should you want to make use of sccache (https://github.com/mozilla/sccache), you could install it via scoop.

From a PowerShell terminal, as non-administrator:

PS C:\> Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
PS C:\> irm get.scoop.sh | iex
PS C:\> scoop install sccache

Clone

From a cmd:

C:\projects> git config core.symlinks true
C:\projects> git clone --recurse-submodules https://github.com/rturrado/the_modern_cpp_challenge

Config

There are several options to run CMake from Visual Studio.

  • CMake should start automatically when choosing a Configuration and a Build Preset (e.g. msvc Debug (tests) and Build windows-msvc-debug (tests)) in the tool bar.
  • CMake can be started manually from the Configure Cache option in the Project menu.
  • Finally, CMake can also be started manually from a Developer Command Prompt (Tools menu, Command Line option):
C:\projects\the_modern_cpp_challenge> cmake --preset windows-msvc-debug-tests

Build

From Visual Studio, once CMake finishes, type CTRL+B or build from Build > Build All menu.

Or, from the command line:

C:\projects\the_modern_cpp_challenge> cmake --build --preset windows-msvc-debug-tests

Build presets

The following build presets are available (the configuration presets have the same name):

  • Debug:
    • windows-msvc-debug-tests: tests enabled.
    • windows-msvc-debug-github: tests and ccache enabled. This is the Debug preset used in GitHub Actions.
  • Release:
    • windows-msvc-release-benchmarks: benchmarks enabled.
    • windows-msvc-release-tests: tests enabled.
    • windows-msvc-release-github: benchmarks and ccache enabled. This is the Release preset used in GitHub Actions.

Output binaries

All successful builds will generate:

  • the_modern_c++_challenge.exe: the main binary, a console application that interacts with the user to execute the different problems from the book.
  • the_modern_c++_challenge-<MAJOR_VERSION>_<MINOR_VERSION>.lib: a static library, used, for example, by the test and benchmark binaries.

Builds with the option -DTHE_MODERN_C++_CHALLENGE_BUILD_TESTS=ON (debug build presets) will also generate:

  • the_modern_c++_challenge_test.exe: a console application to test the code.

Builds with the option -DTHE_MODERN_C++_CHALLENGE_BUILD_BENCHMARKS=ON (release-benchmarks and release-github build presets) will also generate:

  • the_modern_c++_challenge_benchmark.exe: a console application to benchmark the code.

Run

From Visual Studio: the main binary receives a res folder as an argument (a resource directory used by some of the problems). The way to set this parameter from within Visual Studio is via the .vs\launch.vs.json, which is not in the repository since it is a user file. The example below shows a possible launch.vs.json file. Notice args only contains the path of the res folder. Notice also that you should provide one configuration entry per output binary (not for the benchmark binary).

{
  "version": "0.2.1",
  "defaults": {},
    "configurations": [
        {
            "type": "default",
            "project": "CMakeLists.txt",
            "projectTarget": "the_modern_c++_challenge.exe (src\\Debug\\the_modern_c++_challenge.exe)",
            "name": "the_modern_c++_challenge.exe (src\\Debug\\the_modern_c++_challenge.exe)",
            "args": [
                "C:\\projects\\the_modern_c++_challenge\\res"
            ]
        }
    ]
}

Choose the_modern_c++_challenge.exe from Select Startup Item, and click the green "play" button.

Or, from the command line:

C:\projects\the_modern_cpp_challenge> .\out\build\windows-msvc-debug-tests\src\Debug\the_modern_c++_challenge.exe res

Tests

Build with:

C:\projects\the_modern_cpp_challenge> cmake --preset windows-msvc-debug-tests
C:\projects\the_modern_cpp_challenge> cmake --build --preset windows-msvc-debug-tests

You can run the test executable directly:

C:\projects\the_modern_cpp_challenge> .\out\build\windows-msvc-debug-tests\test\Debug\the_modern_c++_challenge_test.exe res

Or execute the tests via ctest:

C:\projects\the_modern_cpp_challenge\out\build\windows-msvc-debug-tests> ctest -C Debug --output-on-failure

Alternatively, if you want a less verbose ouptut:

C:\projects\the_modern_cpp_challenge> .\out\build\windows-msvc-debug-tests\test\Debug\the_modern_c++_challenge_test.exe res --gtest_brief=1

Or:

C:\projects\the_modern_cpp_challenge\out\build\windows-msvc-debug-tests> ctest -C Debug --output-on-failure --progress

Benchmarks

Build with:

C:\projects\the_modern_cpp_challenge> cmake --preset windows-msvc-release-benchmarks
C:\projects\the_modern_cpp_challenge> cmake --build --preset windows-msvc-release-benchmarks

And run with:

C:\projects\the_modern_cpp_challenge> .\out\build\windows-msvc-release-benchmarks\benchmark\Release\the_modern_c++_challenge_benchmark.exe

Dependencies

The Boost libraries have to be downloaded by the user before building the project:

  • boost: Boost libraries.

The following libraries are used in this project, but are automatically downloaded and made available via CMake's FetchContent:

  • asio: standalone version of the Boost Asio library, used here for networking.
  • benchmark: Google benchmarking.
  • fmt: formatting library with more features than C++ iostreams.
  • googletest: GoogleTest and GoogleMock.
  • nlohmann/json: JSON support.
  • range-v3: range library with many more features than what have been added to C++20.
  • rtc: some utilities I use in different C++ projects.
  • SQLite3: CMake support for the SQLite amalgamation.
  • SQLite Modern CPP: modern C++ wrapper around SQLite C API.

The same happens with the list below. I'm showing them apart because they are forks of my own. You can check their README files to see how I adapted the original libraries to use them in this project.

  • Crypto++ CMake: C++ library of cryptographic schemes.
  • curlcpp: C++ wrapper for curl.
  • FreeImage: open source library project for developers who would like to support popular graphics image formats like PNG, BMP, JPEG, TIFF and others.
  • PDF-Writer: creation, parsing and manipulation of PDF files and streams.
  • perceptualdiff: program that compares two images using a perceptually based image metric. Used in the tests to compare PNG files. It makes use of the FreeImage library.
  • PNGwriter: C+ wrapper for libpng.
  • pugixml: XML processing.
  • ZipLib: ZIP archives support.

Finally, this group of libraries is automatically managed via vcpkg:

  • cURL: command line tool and library for transferring data with URLs.
  • FreeType: software font engine.
  • libpng: official PNG reference library.
  • zlib: general purpose data compression library.

Notes

  • Windows builds with address sanitizer enabled have started to crash since updating to MSVC 14.33.31629.

  • res\sample_folder\sample_subfolder is a symbolic link to the res\sample_subfolder directory. If you experience failures in some tests that access sample_subfolder, it may happen that the symbolic link wasn't correctly created after a git operation. In that case, you may need to tell git to enable the use of symbolic links, and then update your local checkout. You can do that by running the commands below:

    C:\projects\the_modern_cpp_challenge> git config core.symlinks true
    C:\projects\the_modern_cpp_challenge> git reset --hard

About

My solutions to the 100 problems of The Modern C++ Challenge, a book by Marius Bancila (published by Packt).

License:MIT License


Languages

Language:C++ 96.3%Language:CMake 3.2%Language:C 0.5%