Claude Code Plugins

Community-maintained marketplace

Feedback

Modern C++ programming patterns and idioms

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name cpp
description Modern C++ programming patterns and idioms
domain programming-languages
version 1.0.0
tags cpp, c++, stl, raii, templates, memory
triggers [object Object]

C++

Overview

Modern C++ (C++11 and beyond) patterns including RAII, smart pointers, templates, and STL.


Modern C++ Fundamentals

Smart Pointers

#include <memory>
#include <iostream>

// unique_ptr - exclusive ownership
class Resource {
public:
    Resource() { std::cout << "Resource acquired\n"; }
    ~Resource() { std::cout << "Resource released\n"; }
    void use() { std::cout << "Resource used\n"; }
};

void unique_ptr_example() {
    // Create unique_ptr
    auto ptr = std::make_unique<Resource>();
    ptr->use();

    // Transfer ownership
    auto ptr2 = std::move(ptr);
    // ptr is now nullptr

    // Array support
    auto arr = std::make_unique<int[]>(10);
}

// shared_ptr - shared ownership
void shared_ptr_example() {
    auto shared1 = std::make_shared<Resource>();
    {
        auto shared2 = shared1; // Reference count = 2
        shared2->use();
    } // shared2 destroyed, count = 1

    std::cout << "Use count: " << shared1.use_count() << "\n";
} // shared1 destroyed, resource released

// weak_ptr - non-owning reference
class Node {
public:
    std::shared_ptr<Node> next;
    std::weak_ptr<Node> prev; // Avoid circular reference

    ~Node() { std::cout << "Node destroyed\n"; }
};

void weak_ptr_example() {
    auto node1 = std::make_shared<Node>();
    auto node2 = std::make_shared<Node>();

    node1->next = node2;
    node2->prev = node1; // weak_ptr, no ownership

    if (auto locked = node2->prev.lock()) {
        // Use locked (shared_ptr)
    }
}

RAII Pattern

#include <fstream>
#include <mutex>

// File wrapper with RAII
class File {
    std::fstream file_;

public:
    explicit File(const std::string& filename)
        : file_(filename, std::ios::in | std::ios::out) {
        if (!file_.is_open()) {
            throw std::runtime_error("Failed to open file");
        }
    }

    ~File() {
        if (file_.is_open()) {
            file_.close();
        }
    }

    // Delete copy operations
    File(const File&) = delete;
    File& operator=(const File&) = delete;

    // Allow move operations
    File(File&& other) noexcept : file_(std::move(other.file_)) {}
    File& operator=(File&& other) noexcept {
        file_ = std::move(other.file_);
        return *this;
    }

    void write(const std::string& data) {
        file_ << data;
    }
};

// Lock guard (RAII for mutex)
class ThreadSafeCounter {
    mutable std::mutex mutex_;
    int count_ = 0;

public:
    void increment() {
        std::lock_guard<std::mutex> lock(mutex_);
        ++count_;
    }

    int get() const {
        std::lock_guard<std::mutex> lock(mutex_);
        return count_;
    }
};

// Scoped cleanup
template<typename F>
class ScopeGuard {
    F cleanup_;
    bool active_ = true;

public:
    explicit ScopeGuard(F cleanup) : cleanup_(std::move(cleanup)) {}

    ~ScopeGuard() {
        if (active_) cleanup_();
    }

    void dismiss() { active_ = false; }

    ScopeGuard(const ScopeGuard&) = delete;
    ScopeGuard& operator=(const ScopeGuard&) = delete;
};

// Usage
void example() {
    auto resource = acquireResource();
    ScopeGuard guard([&]() { releaseResource(resource); });

    // Do work...

    guard.dismiss(); // Don't cleanup if successful
}

Move Semantics

#include <vector>
#include <string>
#include <utility>

class Buffer {
    std::unique_ptr<char[]> data_;
    size_t size_;

public:
    // Constructor
    explicit Buffer(size_t size) : data_(new char[size]), size_(size) {}

    // Copy constructor
    Buffer(const Buffer& other) : data_(new char[other.size_]), size_(other.size_) {
        std::copy(other.data_.get(), other.data_.get() + size_, data_.get());
    }

    // Move constructor
    Buffer(Buffer&& other) noexcept
        : data_(std::move(other.data_)), size_(other.size_) {
        other.size_ = 0;
    }

    // Copy assignment
    Buffer& operator=(const Buffer& other) {
        if (this != &other) {
            data_.reset(new char[other.size_]);
            size_ = other.size_;
            std::copy(other.data_.get(), other.data_.get() + size_, data_.get());
        }
        return *this;
    }

    // Move assignment
    Buffer& operator=(Buffer&& other) noexcept {
        if (this != &other) {
            data_ = std::move(other.data_);
            size_ = other.size_;
            other.size_ = 0;
        }
        return *this;
    }

    size_t size() const { return size_; }
};

// Perfect forwarding
template<typename T, typename... Args>
std::unique_ptr<T> make_unique_custom(Args&&... args) {
    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

Templates

Function Templates

#include <type_traits>
#include <concepts>

// Basic template
template<typename T>
T max(T a, T b) {
    return (a > b) ? a : b;
}

// Template specialization
template<>
const char* max<const char*>(const char* a, const char* b) {
    return (strcmp(a, b) > 0) ? a : b;
}

// SFINAE (Substitution Failure Is Not An Error)
template<typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
double_value(T value) {
    return value * 2;
}

// C++20 Concepts
template<typename T>
concept Numeric = std::is_arithmetic_v<T>;

template<Numeric T>
T add(T a, T b) {
    return a + b;
}

// Requires clause
template<typename T>
requires std::is_default_constructible_v<T>
T create_default() {
    return T{};
}

// Variadic templates
template<typename... Args>
void print(Args... args) {
    (std::cout << ... << args) << "\n";
}

// Fold expressions
template<typename... Args>
auto sum(Args... args) {
    return (args + ...);
}

Class Templates

// Generic container
template<typename T, size_t N>
class Array {
    T data_[N];

public:
    constexpr size_t size() const { return N; }

    T& operator[](size_t index) {
        if (index >= N) throw std::out_of_range("Index out of range");
        return data_[index];
    }

    const T& operator[](size_t index) const {
        if (index >= N) throw std::out_of_range("Index out of range");
        return data_[index];
    }

    T* begin() { return data_; }
    T* end() { return data_ + N; }
    const T* begin() const { return data_; }
    const T* end() const { return data_ + N; }
};

// Template with default arguments
template<typename T, typename Allocator = std::allocator<T>>
class Vector {
    // ...
};

// Partial specialization
template<typename T>
class Container<T*> {
    // Specialization for pointer types
};

// CRTP (Curiously Recurring Template Pattern)
template<typename Derived>
class Counter {
    static inline int count_ = 0;

public:
    Counter() { ++count_; }
    ~Counter() { --count_; }

    static int count() { return count_; }
};

class Widget : public Counter<Widget> {
    // Widget inherits counting behavior
};

STL Containers and Algorithms

#include <vector>
#include <map>
#include <unordered_map>
#include <set>
#include <algorithm>
#include <numeric>

void container_examples() {
    // vector
    std::vector<int> vec{1, 2, 3, 4, 5};
    vec.push_back(6);
    vec.emplace_back(7); // Construct in place

    // map
    std::map<std::string, int> ordered_map;
    ordered_map["one"] = 1;
    ordered_map.insert({"two", 2});
    ordered_map.try_emplace("three", 3);

    // unordered_map
    std::unordered_map<std::string, int> hash_map;
    hash_map["one"] = 1;

    // set
    std::set<int> ordered_set{3, 1, 4, 1, 5};
    auto [iter, inserted] = ordered_set.insert(9);
}

void algorithm_examples() {
    std::vector<int> vec{5, 2, 8, 1, 9, 3};

    // Sort
    std::sort(vec.begin(), vec.end());
    std::sort(vec.begin(), vec.end(), std::greater<int>());

    // Find
    auto it = std::find(vec.begin(), vec.end(), 8);
    auto it2 = std::find_if(vec.begin(), vec.end(), [](int n) { return n > 5; });

    // Transform
    std::vector<int> doubled(vec.size());
    std::transform(vec.begin(), vec.end(), doubled.begin(), [](int n) { return n * 2; });

    // Accumulate
    int sum = std::accumulate(vec.begin(), vec.end(), 0);

    // Remove-erase idiom
    vec.erase(std::remove_if(vec.begin(), vec.end(), [](int n) { return n < 3; }), vec.end());

    // C++20 ranges (simplified)
    // auto result = vec | std::views::filter([](int n) { return n > 3; })
    //                   | std::views::transform([](int n) { return n * 2; });
}

Concurrency

#include <thread>
#include <future>
#include <mutex>
#include <condition_variable>
#include <atomic>

// Basic threading
void thread_example() {
    std::thread t([]() {
        std::cout << "Hello from thread\n";
    });
    t.join();
}

// async/future
std::future<int> async_example() {
    return std::async(std::launch::async, []() {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        return 42;
    });
}

// promise/future
void promise_example() {
    std::promise<int> promise;
    std::future<int> future = promise.get_future();

    std::thread producer([&promise]() {
        promise.set_value(42);
    });

    int result = future.get();
    producer.join();
}

// Thread-safe queue
template<typename T>
class ThreadSafeQueue {
    std::queue<T> queue_;
    mutable std::mutex mutex_;
    std::condition_variable cond_;

public:
    void push(T value) {
        std::lock_guard<std::mutex> lock(mutex_);
        queue_.push(std::move(value));
        cond_.notify_one();
    }

    T pop() {
        std::unique_lock<std::mutex> lock(mutex_);
        cond_.wait(lock, [this]() { return !queue_.empty(); });
        T value = std::move(queue_.front());
        queue_.pop();
        return value;
    }

    bool try_pop(T& value) {
        std::lock_guard<std::mutex> lock(mutex_);
        if (queue_.empty()) return false;
        value = std::move(queue_.front());
        queue_.pop();
        return true;
    }
};

// Atomic operations
class AtomicCounter {
    std::atomic<int> count_{0};

public:
    void increment() { count_.fetch_add(1, std::memory_order_relaxed); }
    int get() const { return count_.load(std::memory_order_relaxed); }
};

Lambda Expressions

#include <functional>

void lambda_examples() {
    // Basic lambda
    auto add = [](int a, int b) { return a + b; };

    // Capture by value
    int x = 10;
    auto by_value = [x]() { return x; };

    // Capture by reference
    auto by_ref = [&x]() { x++; };

    // Capture all by value
    auto all_value = [=]() { return x; };

    // Capture all by reference
    auto all_ref = [&]() { x++; };

    // Mutable lambda (modify captured values)
    auto mutable_lambda = [x]() mutable { return ++x; };

    // Generic lambda (C++14)
    auto generic = [](auto a, auto b) { return a + b; };

    // Init capture (C++14)
    auto ptr = std::make_unique<int>(42);
    auto capture_move = [p = std::move(ptr)]() { return *p; };

    // Template lambda (C++20)
    auto template_lambda = []<typename T>(std::vector<T>& vec) {
        return vec.size();
    };

    // Constexpr lambda (C++17)
    constexpr auto square = [](int n) constexpr { return n * n; };
    static_assert(square(5) == 25);
}

// Storing lambdas
class EventHandler {
    std::function<void(int)> handler_;

public:
    void set_handler(std::function<void(int)> handler) {
        handler_ = std::move(handler);
    }

    void trigger(int value) {
        if (handler_) handler_(value);
    }
};

Error Handling

#include <stdexcept>
#include <optional>
#include <variant>
#include <expected> // C++23

// Custom exception
class DatabaseError : public std::runtime_error {
    int error_code_;

public:
    DatabaseError(const std::string& message, int code)
        : std::runtime_error(message), error_code_(code) {}

    int error_code() const { return error_code_; }
};

// std::optional for nullable values
std::optional<int> find_value(const std::string& key) {
    if (key == "answer") return 42;
    return std::nullopt;
}

void optional_usage() {
    auto result = find_value("answer");

    if (result) {
        std::cout << "Found: " << *result << "\n";
    }

    int value = result.value_or(0);
}

// std::variant for type-safe union
using Result = std::variant<int, std::string>;

Result compute(bool success) {
    if (success) return 42;
    return std::string("error");
}

void variant_usage() {
    Result r = compute(true);

    std::visit([](auto&& arg) {
        using T = std::decay_t<decltype(arg)>;
        if constexpr (std::is_same_v<T, int>) {
            std::cout << "Success: " << arg << "\n";
        } else {
            std::cout << "Error: " << arg << "\n";
        }
    }, r);
}

// std::expected (C++23)
// std::expected<int, std::string> divide(int a, int b) {
//     if (b == 0) return std::unexpected("Division by zero");
//     return a / b;
// }

Related Skills

  • [[system-design]] - Systems programming
  • [[performance-optimization]] - Low-level optimization
  • [[desktop-apps]] - Native applications