A sandbox environment for C++ experimentation:
- Run code with
scratch/main.cpporscratch/fast.cpp - Test code with
scratch/tests.cppusing GoogleTest - Benchmark code with
scratch/benchmark.cppusing GoogleBenchmark - View assembly with
scratch/assembly.cpp
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 # FedoraLibrary dependencies (GoogleTest, GoogleBenchmark) are managed with Conan
$ uv tool install conan
$ conan profile detectInstall 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 # DebugInstall 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 installEdit scratch/main.cpp file
$ cmake --preset debug # Configure
$ cmake --build --preset debug # Build
$ ./build/debug/main # Run
$ lldb ./build/debug/main # DebugAlternatively, for faster prototyping without any checks, use the
scratch/fast.cpp file
$ cmake --preset debug # Configure
$ cmake --build --preset debug # Build
$ ./build/debug/fast # RunEdit scratch/tests.cpp file
$ cmake --preset debug # Configure
$ cmake --build --preset debug # Build
$ ctest --preset debug # Run
$ lldb ./build/debug/tests # DebugFor listing and running specific tests
$ ctest --preset debug -N # List tests
$ ctest --preset debug -R '<regex>' # Run matching testsExample 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));
}Edit scratch/benchmark.cpp file
$ cmake --build --preset release # Build
$ ./build/release/benchmark # Run
$ lldb ./build/release/benchmark # DebugExample 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 520319518Edit 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 # DebugExample
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
retSuggestions or improvements? Raise a pull request!