cpp-guide

Applies to: C++20, Systems Programming, Embedded, Game Engines, High-Performance Computing

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "cpp-guide" with this command: npx skills add ar4mirez/samuel/ar4mirez-samuel-cpp-guide

C/C++ Guide

Applies to: C++20, Systems Programming, Embedded, Game Engines, High-Performance Computing

Core Principles

  • RAII Everywhere: Every resource (memory, files, locks, sockets) is owned by an object whose destructor releases it

  • Zero Raw Ownership: Never use new /delete directly; use smart pointers and containers for all heap allocation

  • Value Semantics by Default: Pass and return by value; rely on move semantics and copy elision for performance

  • Compile-Time over Run-Time: Use constexpr , static_assert , concepts, and templates to catch errors at compile time

  • Warnings Are Errors: Build with -Wall -Wextra -Wpedantic -Werror ; every warning is a latent bug

Guardrails

Standard & Compiler

  • Use C++20 as the minimum standard (-std=c++20 / CMAKE_CXX_STANDARD 20 )

  • Compile with at least -Wall -Wextra -Wpedantic ; treat warnings as errors (-Werror )

  • Enable sanitizers in debug builds: -fsanitize=address,undefined

  • Use static_assert to verify assumptions about types, sizes, and alignments

  • Set CMAKE_EXPORT_COMPILE_COMMANDS ON for clang-tidy and IDE integration

  • Pin compiler minimum version in CI (e.g., GCC 12+, Clang 15+, MSVC 19.34+)

Code Style

  • Run clang-format before every commit (no exceptions)

  • Run clang-tidy with project checks before every commit

  • snake_case for functions, variables, namespaces, file names

  • PascalCase for types (classes, structs, enums, concepts, type aliases)

  • SCREAMING_SNAKE_CASE for macros and compile-time constants

  • Prefer enum class over plain enum (scoped, no implicit conversion)

  • Header include order: own header, project headers, third-party, standard library

  • Use #pragma once or traditional include guards consistently (pick one)

  • No using namespace std; in headers (allowed in .cpp function scope only)

Memory Management

  • Default to stack allocation; heap allocate only when lifetime or size requires it

  • std::unique_ptr for single ownership (default smart pointer)

  • std::shared_ptr only when ownership is genuinely shared; document why

  • Use std::make_unique and std::make_shared (exception-safe, single allocation)

  • Containers (std::vector , std::string , std::array ) manage their own memory

  • Use std::span for non-owning views into contiguous memory

  • Use std::string_view for non-owning string references (read-only, no allocation)

Error Handling

  • Use exceptions for truly exceptional conditions (constructor failure, precondition violations)

  • Use std::optional<T> for values that may be absent (replaces sentinel values)

  • Use std::variant<T, Error> or std::expected<T, E> (C++23) for result types

  • Mark functions noexcept when they do not throw (enables move optimization)

  • Always catch by const reference: catch (const std::exception& e)

  • Never catch ... without rethrowing or logging (swallowed errors are bugs)

Concurrency

  • Prefer std::jthread over std::thread (automatic join, stop token support)

  • Protect shared mutable state with std::mutex and std::scoped_lock

  • Use std::atomic for lock-free shared counters and flags

  • Never hold a lock while calling a callback or virtual function (deadlock risk)

  • All concurrent code must pass ThreadSanitizer (-fsanitize=thread )

  • Prefer std::latch , std::barrier , std::counting_semaphore (C++20) for coordination

Project Structure

myproject/ ├── CMakeLists.txt # Root: project(), options, add_subdirectory() ├── cmake/ # Shared CMake modules (warnings, sanitizers) ├── src/ │ ├── CMakeLists.txt # Library/executable targets │ ├── main.cpp # Entry point (thin: parse args, call run) │ ├── app.cpp / app.hpp │ └── domain/ │ └── user.cpp / user.hpp ├── include/myproject/ # Public headers (for libraries) ├── tests/ │ ├── CMakeLists.txt # GoogleTest / Catch2 targets │ └── test_user.cpp ├── benchmarks/ ├── .clang-format └── .clang-tidy

  • main.cpp should be thin: parse arguments, build config, call into library code

  • One class per header/source pair; test files mirror source structure

  • Use FetchContent or find_package for dependencies

CMakeLists.txt Essentials

cmake_minimum_required(VERSION 3.20) project(myproject VERSION 0.1.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

add_compile_options(-Wall -Wextra -Wpedantic -Werror)

if(CMAKE_BUILD_TYPE STREQUAL "Debug") add_compile_options(-fsanitize=address,undefined -fno-omit-frame-pointer) add_link_options(-fsanitize=address,undefined) endif()

add_subdirectory(src) add_subdirectory(tests)

Key Patterns

RAII Resource Wrapper

class FileHandle { public: explicit FileHandle(const std::filesystem::path& path) : handle_(std::fopen(path.c_str(), "r")) { if (!handle_) { throw std::runtime_error("Failed to open: " + path.string()); } } ~FileHandle() { if (handle_) std::fclose(handle_); }

FileHandle(const FileHandle&#x26;) = delete;
FileHandle&#x26; operator=(const FileHandle&#x26;) = delete;
FileHandle(FileHandle&#x26;&#x26; other) noexcept : handle_(std::exchange(other.handle_, nullptr)) {}
FileHandle&#x26; operator=(FileHandle&#x26;&#x26; other) noexcept {
    if (this != &#x26;other) { if (handle_) std::fclose(handle_); handle_ = std::exchange(other.handle_, nullptr); }
    return *this;
}

FILE* get() const noexcept { return handle_; }

private: FILE* handle_; };

Smart Pointers

// unique_ptr: default choice, single ownership auto user = std::make_unique<User>("alice", "alice@example.com"); process_user(*user); // pass by reference, not pointer

// shared_ptr: only when ownership is genuinely shared auto config = std::make_shared<Config>(load_config("app.toml")); auto worker1 = std::jthread([config] { serve(config); }); auto worker2 = std::jthread([config] { monitor(config); });

// weak_ptr: break reference cycles, optional observation class TreeNode { std::vector<std::shared_ptr<TreeNode>> children_; std::weak_ptr<TreeNode> parent_; // non-owning back-reference };

Move Semantics: Rule of Zero / Five

// Rule of Zero: prefer this. Let members manage resources. struct UserRecord { std::string name; std::string email; std::vector<std::string> roles; // No special member functions needed -- std::string and std::vector handle everything. };

// Rule of Five: only when managing a raw resource manually. // See references/patterns.md for full RAII wrapper examples. // Key rules: move constructors/assignment MUST be noexcept; // use std::exchange to leave moved-from objects in a valid state.

std::optional and std::variant

// std::optional: replaces sentinel values (-1, nullptr, "") std::optional<User> find_user(std::string_view email) { auto it = users_.find(std::string(email)); if (it == users_.end()) return std::nullopt; return it->second; }

if (auto user = find_user("alice@example.com")) { std::println("Found: {}", user->name); }

// std::variant: type-safe tagged union using ParseResult = std::variant<int, double, std::string>;

std::visit(overloaded{ [](int v) { std::println("int: {}", v); }, [](double v) { std::println("double: {}", v); }, [](const std::string& v) { std::println("string: {}", v); }, }, result);

Concepts (C++20)

template<typename T> concept Hashable = requires(T a) { { std::hash<T>{}(a) } -> std::convertible_to<std::size_t>; };

template<Hashable Key, typename Value> class Cache { public: void put(const Key& key, Value value) { store_[key] = std::move(value); } std::optional<Value> get(const Key& key) const { auto it = store_.find(key); return it != store_.end() ? std::optional(it->second) : std::nullopt; } private: std::unordered_map<Key, Value> store_; };

constexpr Computation

constexpr int factorial(int n) { if (n <= 1) return 1; return n * factorial(n - 1); } static_assert(factorial(5) == 120);

consteval auto make_lookup_table() { std::array<int, 256> table{}; for (int i = 0; i < 256; ++i) table[i] = (i * i) % 256; return table; } constexpr auto kLookup = make_lookup_table();

Ranges (C++20)

auto active_user_emails(const std::vector<User>& users) { return users | std::views::filter([](const User& u) { return u.is_active; }) | std::views::transform(&User::email); }

auto top_scores = scores | std::views::filter([](int s) { return s > 0; }) | std::views::transform([](int s) { return s * 100 / max_score; }) | std::views::take(10);

Structured Bindings

for (const auto& [key, value] : config_map) { std::println("{} = {}", key, value); }

auto [min_it, max_it] = std::minmax_element(data.begin(), data.end());

auto [ok, msg, code] = process_request(req); if (!ok) { log_error(msg, code); }

Testing

GoogleTest

class UserServiceTest : public ::testing::Test { protected: void SetUp() override { db_ = std::make_unique<InMemoryDatabase>(); service_ = std::make_unique<UserService>(*db_); } std::unique_ptr<InMemoryDatabase> db_; std::unique_ptr<UserService> service_; };

TEST_F(UserServiceTest, CreateUserSucceeds) { auto user = service_->create_user("alice", "alice@example.com"); EXPECT_EQ(user.name, "alice"); EXPECT_EQ(user.email, "alice@example.com"); EXPECT_FALSE(user.id.empty()); }

TEST_F(UserServiceTest, DuplicateEmailThrows) { service_->create_user("alice", "alice@example.com"); EXPECT_THROW(service_->create_user("bob", "alice@example.com"), DuplicateEmailError); }

Catch2

TEST_CASE("Parser handles valid input", "[parser]") { Parser parser; SECTION("integer literals") { auto result = parser.parse("42"); REQUIRE(result.has_value()); CHECK(std::get<int>(*result) == 42); } SECTION("empty input returns nullopt") { CHECK_FALSE(parser.parse("").has_value()); } }

Testing Standards

  • Test names describe behavior: TEST(OrderService, CancelledOrderCannotBeShipped)

  • Use TEST_F with fixtures for shared setup/teardown

  • EXPECT_* for non-fatal assertions; ASSERT_* only when continuation is meaningless

  • Coverage target: >80% for libraries, >60% for applications

  • Mock external dependencies with interfaces and dependency injection

  • Run tests under AddressSanitizer and UndefinedBehaviorSanitizer in CI

Tooling

Essential Commands

cmake -B build -DCMAKE_BUILD_TYPE=Debug # Configure cmake --build build -j$(nproc) # Build (parallel) cmake --build build --target test # Run tests (CTest) clang-format -i src//*.cpp src//.hpp # Format clang-tidy src/.cpp -- -std=c++20 # Static analysis gcovr --root . --html -o coverage.html # Coverage report

clang-format Configuration

.clang-format

BasedOnStyle: Google IndentWidth: 4 ColumnLimit: 100 PointerAlignment: Left SortIncludes: CaseInsensitive

clang-tidy Configuration

.clang-tidy

Checks: > -, bugprone-, cert-, cppcoreguidelines-, misc-, modernize-, performance-, readability-, -modernize-use-trailing-return-type WarningsAsErrors: '' HeaderFilterRegex: 'src/.'

Sanitizers Reference

Sanitizer Flag Catches

AddressSanitizer -fsanitize=address

Buffer overflow, use-after-free, leaks

UndefinedBehaviorSanitizer -fsanitize=undefined

Signed overflow, null deref, alignment

ThreadSanitizer -fsanitize=thread

Data races, deadlocks

MemorySanitizer -fsanitize=memory

Uninitialized reads (Clang only)

  • ASan + UBSan run together; TSan and MSan must run separately

  • Always use -fno-omit-frame-pointer with sanitizers

References

For detailed patterns and examples, see:

  • references/patterns.md -- RAII wrappers, smart pointer patterns, thread-safe singleton

External References

  • C++ Core Guidelines

  • cppreference.com

  • Compiler Explorer (Godbolt)

  • GoogleTest Documentation

  • Catch2 Documentation

  • clang-tidy Checks

  • CMake Best Practices

  • Abseil C++ Tips of the Week

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

code-review

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

python-guide

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

typescript-guide

No summary provided by upstream source.

Repository SourceNeeds Review