Skip to content

Repository files navigation

C++ Playground

A sandbox environment for C++ experimentation:

  • Run code with scratch/main.cpp or scratch/fast.cpp
  • Test code with scratch/tests.cpp using GoogleTest
  • Benchmark code with scratch/benchmark.cpp using GoogleBenchmark
  • View assembly with scratch/assembly.cpp

Setup

Required toolchain: a C++26 compiler clang++ (or g++) and cmake.

Optional: clang-tidy (linter), ccache (compilation cache) and mold (fast linker), all auto-detected when installed; ninja (build generator); clang-format and cmake-format (formatters); lldb (or gdb) for debugging; and llvm-cxxfilt (or c++filt) to demangle the assembly output.

$ sudo pacman -S clang llvm lldb cmake ninja ccache mold                                  # Arch
$ sudo apt install clang clang-tidy clang-format lldb llvm cmake ninja-build ccache mold  # Debian/Ubuntu
$ sudo dnf install clang clang-tools-extra lldb llvm cmake ninja-build ccache mold        # Fedora

Library dependencies (GoogleTest, GoogleBenchmark) are managed with Conan

$ uv tool install conan
$ conan profile detect

Install dependencies and configure

$ conan install . -of build/release -pr conan/profile --build=missing --lockfile=conan.lock  # Release
$ conan install . -of build/debug -pr conan/profile --build=missing --lockfile=conan.lock -s build_type=Debug  # Debug

Git Hooks

Install pre-commit once, then install the repository hooks once per clone. clang-format and cmake-format check formatting on push.

$ uv tool install pre-commit
$ pre-commit install

Run

Edit scratch/main.cpp file

$ cmake --preset debug  # Configure
$ cmake --build --preset debug  # Build
$ ./build/debug/main  # Run
$ lldb ./build/debug/main  # Debug

Alternatively, for faster prototyping without any checks, use the scratch/fast.cpp file

$ cmake --preset debug  # Configure
$ cmake --build --preset debug  # Build
$ ./build/debug/fast  # Run

Test

Edit scratch/tests.cpp file

$ cmake --preset debug  # Configure
$ cmake --build --preset debug  # Build
$ ctest --preset debug  # Run
$ lldb ./build/debug/tests  # Debug

For listing and running specific tests

$ ctest --preset debug -N  # List tests
$ ctest --preset debug -R '<regex>'  # Run matching tests

Example solution for Leetcode 1 problem twoSum

#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include <unordered_map>
#include <vector>

class Solution {
 public:
  [[nodiscard]]
  static auto twoSum(const std::vector<int>& nums, int target) noexcept
      -> std::vector<int> {
    auto mp = std::unordered_map<int, int>{};  // (nums[i], i)
    for (auto i = 0ZU, n = nums.size(); i < n; ++i) {
      auto complement = target - nums[i];
      if (mp.contains(complement)) {
        return {static_cast<int>(i), mp[complement]};
      }
      mp[nums[i]] = static_cast<int>(i);
    }
    return {0, 0};
  }
};

TEST(SolutionTest, Test1) {
  std::vector<int> nums{2, 7, 11, 15};
  EXPECT_THAT(Solution::twoSum(nums, 9), testing::UnorderedElementsAre(0, 1));
}

TEST(SolutionTest, Test2) {
  std::vector<int> nums{3, 2, 4};
  EXPECT_THAT(Solution::twoSum(nums, 6), testing::UnorderedElementsAre(1, 2));
}

TEST(SolutionTest, Test3) {
  std::vector<int> nums{3, 3};
  EXPECT_THAT(Solution::twoSum(nums, 6), testing::UnorderedElementsAre(0, 1));
}

Benchmark

Edit scratch/benchmark.cpp file

$ cmake --build --preset release  # Build
$ ./build/release/benchmark  # Run
$ lldb ./build/release/benchmark  # Debug

Example from https://quick-bench.com/

#include <benchmark/benchmark.h>

static void StringCreation(benchmark::State& state) {
  for (auto _ : state) {
    auto foo = std::string{"hello"};
    benchmark::DoNotOptimize(foo);
  }
}
BENCHMARK(StringCreation);

static void StringCopy(benchmark::State& state) {
  auto foo = std::string{"hello"};
  for (auto _ : state) {
    auto bar = std::string{foo};
    benchmark::DoNotOptimize(bar);
  }
}
BENCHMARK(StringCopy);

That generates the following output

Running build/benchmark
Run on (14 X 4400 MHz CPU s)
CPU Caches:
  L1 Data 48 KiB (x7)
  L1 Instruction 64 KiB (x7)
  L2 Unified 2048 KiB (x7)
  L3 Unified 12288 KiB (x1)
Load Average: 0.45, 0.42, 0.63

---------------------------------------------------------
Benchmark               Time             CPU   Iterations
---------------------------------------------------------
StringCreation      0.922 ns        0.921 ns    765882382
StringCopy           1.35 ns         1.35 ns    520319518

Assembly

Edit scratch/assembly.cpp file (use the release preset instead, depending on the assembly you want to see)

$ cmake --preset debug  # Configure
$ cmake --build --preset debug  # Build
$ ./build/debug/assembly  # Run
$ lldb ./build/debug/assembly  # Debug

Example

auto add(int a, int b) -> int {
  return a + b;
}

auto main() -> int {
  auto result  = add(3, 5);
}

Generates build/debug/assembly_demangled.s

add(int, int):                               # @add(int, int)
# %bb.0:
	push	rbp
	mov	rbp, rsp
	mov	dword ptr [rbp - 4], edi
	mov	dword ptr [rbp - 8], esi
	mov	eax, dword ptr [rbp - 4]
	add	eax, dword ptr [rbp - 8]
	pop	rbp
	ret

main:                                   # @main
# %bb.0:
	push	rbp
	mov	rbp, rsp
	sub	rsp, 16
	mov	edi, 3
	mov	esi, 5
	call	add(int, int)
	mov	dword ptr [rbp - 4], eax
	xor	eax, eax
	add	rsp, 16
	pop	rbp
	ret

Contributions

Suggestions or improvements? Raise a pull request!

About

A sandbox environment for C++ experimentation

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages