1---2name: c-conventions-23description: C++ coding conventions and best practices for modern C++ development. Use when writing, reviewing, or refactoring C++ code to ensure consistency with project standards, const-correctness, RAII principles, and C++23 features.4---56# C++ Coding Conventions78## General Principles910- Follow modern C++ best practices (C++23 standard preferred, C++17 minimum)11- Use RAII principles for resource management12- Prefer smart pointers (`std::unique_ptr`, `std::shared_ptr`) over raw pointers13- Apply const-correctness throughout the codebase14- Write self-documenting code with clear naming and structure15- Keep functions focused and modular16- Leverage the type system for compile-time safety17- Ensure platform portability (Linux, macOS, Windows)1819## C++ Standard and Compatibility2021- Use **C++23 standard** when possible for latest features22- Maintain **C++17 minimum** for broader compiler support23- Use standard library features over custom implementations24- Avoid compiler-specific extensions unless necessary25- Test on multiple compilers (GCC, Clang, MSVC)26- Use feature test macros for conditional compilation27- Handle platform differences through standard mechanisms2829## Const Correctness3031- All input parameters should be `const` when not modified32- Member functions that don't modify state should be `const`33- Use `const` references for complex types in parameters34- Apply `const` to return values when appropriate35- Examples:36 - ✅ Correct: `void SetTitle(const std::string& title);`37 - ✅ Correct: `std::string GetTitle() const;`38 - ✅ Correct: `const Data& GetData() const;`39 - ❌ Incorrect: `void SetTitle(std::string title);` (unnecessary copy)4041## Comparison Conventions4243- **Always place constants on the left side of comparisons** (constant-left style)44- Use explicit `nullptr` comparisons instead of implicit boolean conversion45- This prevents accidental assignment when `=` is used instead of `==`46- Examples:47 - ✅ Correct: `if (nullptr == ptr)`, `if (0 == value)`, `if (true == condition)`48 - ❌ Incorrect: `if (!ptr)`, `if (ptr == nullptr)`, `if (value == 0)`49- Apply to all comparisons including pointer checks, numeric values, and booleans50- Benefits: Compiler error if `=` is mistakenly used instead of `==`5152## RAII and Resource Management5354- Use RAII for all resource management (memory, files, locks, etc.)55- Prefer smart pointers over raw pointers:56 - `std::unique_ptr` for exclusive ownership57 - `std::shared_ptr` for shared ownership58 - `std::weak_ptr` to break circular dependencies59- Use standard containers instead of manual memory management60- Examples:61 ```cpp62 // Good: RAII with smart pointers63 auto data = std::make_unique<Data>();64 auto shared = std::make_shared<SharedObject>();6566 // Good: RAII with containers67 std::vector<int> numbers;68 std::string text;6970 // Avoid: Raw pointers requiring manual cleanup71 Data* data = new Data(); // Must remember to delete72 ```73- Let destructors handle cleanup automatically7475## Classes and Destructors7677- All destructors should be virtual (even when deleted)78- All abstract/interface classes should have a protected virtual destructor79- Use the Rule of Zero when possible (let compiler generate special members)80- When implementing special members, follow the Rule of Five81- Declare move constructor and move assignment operator when beneficial8283## File Organization8485- **Header files (.h)**: Class declarations, inline functions, templates86- **Implementation files (.cpp)**: Method implementations, non-template code87- Each class should have a separate header and implementation file88- Filename must match the class name exactly (e.g., `Driver` class → `Driver.h` and `Driver.cpp`)89- Header files go in `include/` directory90- Implementation files go in `src/` directory91- Exceptions:92 - Template classes may have implementation in header if needed93 - Tightly coupled class hierarchies (like AST nodes) may share files9495## Implementation Separation9697- Method implementations should be in .cpp files, not inline in headers98- Reduces recompilation of dependencies when implementation changes99- Only these may remain inline in headers:100 - Constructors (if trivial)101 - Destructors (if trivial)102 - One-line getters/setters for performance103 - Template functions (required)104- Prefer out-of-line implementations for better compilation times105106## Header File Structure107108- Include guard or `#pragma once` at top109- Includes (system headers first, then project headers)110- Forward declarations (to minimize includes)111- Type definitions and aliases112- Class declarations113- Inline function definitions114- Example:115 ```cpp116 #ifndef __MYPROJECT_CLASS_H_INCL__117 #define __MYPROJECT_CLASS_H_INCL__118119 #include <string>120 #include <vector>121122 #include "BaseClass.h"123124 // Forward declarations125 class Helper;126127 class MyClass : public BaseClass128 {129 public:130 // ... class definition131 };132133 #endif // __MYPROJECT_CLASS_H_INCL__134 ```135136## Implementation File Organization137138- Include corresponding header first139- Include system headers140- Include project headers141- Anonymous namespace for file-local helpers142- Class member function implementations143- Example:144 ```cpp145 #include "MyClass.h"146147 #include <algorithm>148 #include <iostream>149150 #include "Helper.h"151152 namespace153 {154 // File-local helper functions155 void LocalHelper()156 {157 // ...158 }159 }160161 // Class member implementations162 MyClass::MyClass()163 {164 // ...165 }166 ```167168## Class Structure and Scope Order169170- Always declare scopes in the order: `public`, `protected`, `private`171- This makes the public interface immediately visible when reading class definitions172- Group related members together within each section173- Example:174 ```cpp175 class MyClass176 {177 public:178 // Constructors and destructor179 MyClass();180 virtual ~MyClass();181182 // Public interface183 void PublicMethod();184 int GetValue() const;185186 protected:187 // Protected interface for derived classes188 virtual void ProtectedMethod();189190 private:191 // Private implementation details192 void PrivateHelper();193 int privateData_;194 std::string privateName_;195 };196 ```197198## Naming Conventions199200- **Types** (classes, structs, enums, typedefs): Upper PascalCase (e.g., `Episode`, `MediaType`)201- **Functions/methods**: Upper PascalCase (e.g., `GetTitle`, `SetDuration`, `ParseInput`)202- **Variables and function parameters**: camelCase (e.g., `bufferSize`, `episodeCount`)203- **Member variables**: camelCase with underscore postfix (e.g., `dataSize_`, `title_`)204- **Constants**: UPPER_SNAKE_CASE (e.g., `MAX_EPISODE_LENGTH`, `DEFAULT_TIMEOUT`)205- **Namespaces**: lowercase (e.g., `myproject`, `utils`)206- **Template parameters**: Single uppercase letter or PascalCase (e.g., `T`, `ValueType`)207- Remove redundant prefixes from class names (e.g., use `Model` instead of `P3Model`)208209## Include Guards210211- Use format `__PROJECT_CLASS_NAME_H_INCL__` where CLASS_NAME matches the class212- Must start with project-specific prefix to identify namespace213- Single word class: `Driver` → `__MYPROJECT_DRIVER_H_INCL__`214- Multi-word class: `TestTools` → `__MYPROJECT_TEST_TOOLS_H_INCL__`215- Insert underscore between each word in PascalCase class names216- Example:217 ```cpp218 #ifndef __MYPROJECT_DRIVER_H_INCL__219 #define __MYPROJECT_DRIVER_H_INCL__220221 // Class declaration222223 #endif // __MYPROJECT_DRIVER_H_INCL__224 ```225226## Alignment Pragmas227228- All header files must use 8-byte alignment for types using `#pragma pack`229- Include alignment pragmas at the top and restore at the bottom230- Use cross-compiler compatible pragmas for MSVC, GCC, and Clang:231 ```cpp232 // At top of header (after include guard, before includes)233 #pragma pack(push, 8)234235 // ... class declarations ...236237 // At bottom of header (before closing include guard)238 #pragma pack(pop)239 ```240241## Namespaces242243- Use namespaces to organize code logically244- Avoid `using` directives in headers (e.g., `using namespace std;`)245- Use `using` declarations sparingly in implementation files246- Prefer explicit namespace qualification for clarity247- Use nested namespaces for hierarchical organization248- Examples:249 ```cpp250 namespace myproject251 {252 namespace utils253 {254 class Helper { };255 }256257 class MainClass { };258 }259260 // C++17 nested namespace syntax261 namespace myproject::utils262 {263 class Helper { };264 }265 ```266267## Function and Method Design268269- Keep functions short and focused on single responsibility270- Use early returns to reduce nesting depth271- Pass by const reference for complex types, by value for primitives272- Use trailing return types when it improves clarity (e.g., with `auto`)273- For intentionally unused parameters, use `[[maybe_unused]]` attribute or comment274- Examples:275 ```cpp276 // Good: Clear parameter passing277 void ProcessData(const std::vector<int>& data, int threshold);278279 // Good: Trailing return type with auto280 auto GetValue() -> std::optional<int>;281282 // Good: Unused parameter handling283 void Handler([[maybe_unused]] int eventType)284 {285 // Implementation doesn't use eventType286 }287 ```288289## Type Definitions and Aliases290291- Use `using` instead of `typedef` for type aliases292- Create meaningful aliases for complex types293- Document the purpose of type aliases294- Examples:295 ```cpp296 // Good: Clear type aliases297 using UserId = uint64_t;298 using ErrorCallback = std::function<void(const std::string&)>;299 using DataMap = std::unordered_map<std::string, std::shared_ptr<Data>>;300301 // Avoid: Obscure typedef302 typedef unsigned long long int ull;303 ```304305## Enums306307- Prefer `enum class` over `enum` for type safety308- Use explicit underlying types when needed309- Prefix enum values with enum name for clarity (only if not using `enum class`)310- Examples:311 ```cpp312 // Best: enum class (scoped and type-safe)313 enum class Color : uint8_t314 {315 Red,316 Green,317 Blue318 };319320 // Usage: Color::Red321322 // Acceptable: Traditional enum with prefix323 enum MediaType324 {325 MEDIA_TYPE_AUDIO,326 MEDIA_TYPE_VIDEO,327 MEDIA_TYPE_SUBTITLE328 };329 ```330331## Error Handling332333- Use exceptions for exceptional conditions334- Use `std::optional` for values that may not exist335- Use `std::expected` (C++23) or similar for expected errors336- Never throw from destructors337- Document exceptions in function comments338- Examples:339 ```cpp340 // Good: Optional for nullable values341 std::optional<User> FindUser(const std::string& name);342343 // Good: Exception for errors344 void LoadFile(const std::string& path)345 {346 if (path.empty())347 {348 throw std::invalid_argument("Path cannot be empty");349 }350 // ... load file351 }352353 // Good: Error handling with optional354 auto user = FindUser("john");355 if (user.has_value())356 {357 ProcessUser(user.value());358 }359 ```360361## Memory Management362363- Prefer stack allocation over heap allocation when possible364- Use smart pointers for heap-allocated objects365- Use `std::make_unique` and `std::make_shared` for construction366- Avoid naked `new` and `delete`367- Use containers for collections of objects368- Examples:369 ```cpp370 // Good: Smart pointers371 auto data = std::make_unique<Data>();372 auto shared = std::make_shared<Config>();373374 // Good: Stack allocation375 Data localData;376 std::array<int, 10> numbers;377378 // Good: Containers379 std::vector<std::unique_ptr<Item>> items;380 ```381382## Comments and Documentation383384- Use `//` for all comments (single-line and multi-line)385- Document public APIs with Doxygen-style comments in header files386- Use traditional Doxygen syntax:387 - `///` for Doxygen comments388 - `\brief` for brief descriptions389 - `\param` for parameters390 - `\return` for return values391- Implementation files should use inline `//` comments for logic explanation392- Comment the "why" not the "what"393- Examples:394 ```cpp395 /// \brief Sets the episode title396 /// \param title The new title for the episode397 void SetTitle(const std::string& title);398399 // Implementation comment explaining reasoning400 // Use binary search because data is sorted401 auto it = std::lower_bound(data.begin(), data.end(), target);402 ```403404## Code Formatting405406- Use consistent indentation (4 spaces preferred)407- Braces: Opening brace on next line for functions and blocks408- Line length: Keep under 120 characters when practical409- Use `.clang-format` configuration for automatic formatting410- Example:411 ```cpp412 // Function: opening brace on next line413 void MyClass::ProcessData(const std::vector<int>& data)414 {415 // Control structure: opening brace on next line416 if (nullptr == data_)417 {418 Initialize();419 }420421 for (const auto& item : data)422 {423 ProcessItem(item);424 }425 }426 ```427428## Modern C++ Features429430- Use `auto` for type deduction when type is obvious from context431- Use range-based for loops instead of iterators when possible432- Use structured bindings (C++17) for multiple return values433- Use `std::string_view` for non-owning string references434- Use `constexpr` for compile-time constants435- Examples:436 ```cpp437 // Good: auto for obvious types438 auto config = std::make_unique<Config>();439 auto it = container.find(key);440441 // Good: Range-based for442 for (const auto& item : items)443 {444 ProcessItem(item);445 }446447 // Good: Structured bindings448 auto [success, value] = TryParse(input);449450 // Good: string_view451 void ProcessName(std::string_view name);452453 // Good: constexpr454 constexpr int MAX_SIZE = 1024;455 ```456457## Templates458459- Keep template code in headers (required by C++ standard)460- Use concepts (C++20) to constrain template parameters461- Provide clear error messages for template failures462- Document template parameters and requirements463- Examples:464 ```cpp465 // C++20 concepts466 template<typename T>467 concept Drawable = requires(T obj)468 {469 obj.Draw();470 };471472 template<Drawable T>473 void Render(const T& object)474 {475 object.Draw();476 }477478 // Traditional template with static_assert479 template<typename T>480 class Container481 {482 static_assert(std::is_default_constructible_v<T>,483 "T must be default constructible");484 };485 ```486487## Lambda Expressions488489- Use lambdas for short, local operations490- Capture by reference `[&]` for local scope, by value `[=]` when needed491- Be explicit with captures when clarity is important492- Use `mutable` when lambda needs to modify captured values493- Examples:494 ```cpp495 // Good: Short algorithm496 std::sort(items.begin(), items.end(),497 [](const Item& a, const Item& b)498 {499 return a.priority > b.priority;500 });501502 // Good: Explicit captures503 int threshold = 10;504 auto filter = [threshold](int value)505 {506 return value > threshold;507 };508509 // Good: Mutable lambda510 int counter = 0;511 auto increment = [counter]() mutable512 {513 return ++counter;514 };515 ```516517## Standard Library Usage518519- Prefer standard library over custom implementations520- Use algorithms from `<algorithm>` header521- Use standard containers (`vector`, `map`, `set`, etc.)522- Use `<string>` for string handling523- Use `<filesystem>` (C++17) for file operations524- Examples:525 ```cpp526 // Good: Standard algorithms527 std::sort(data.begin(), data.end());528 auto it = std::find_if(items.begin(), items.end(), predicate);529530 // Good: Standard containers531 std::vector<int> numbers;532 std::unordered_map<std::string, Data> cache;533534 // Good: Filesystem operations535 std::filesystem::path filePath = "/path/to/file";536 if (std::filesystem::exists(filePath))537 {538 // Process file539 }540 ```541542## Const and Constexpr543544- Use `const` for runtime constants545- Use `constexpr` for compile-time constants546- Use `consteval` (C++20) to force compile-time evaluation547- Mark functions `constexpr` when possible for compile-time optimization548- Examples:549 ```cpp550 // Runtime constant551 const int bufferSize = GetBufferSize();552553 // Compile-time constant554 constexpr int MAX_USERS = 100;555556 // Constexpr function557 constexpr int Square(int x)558 {559 return x * x;560 }561562 // C++20 consteval (must be compile-time)563 consteval int Factorial(int n)564 {565 return (n <= 1) ? 1 : n * Factorial(n - 1);566 }567 ```568569## Platform Portability570571- Use standard C++ features when possible572- Handle platform differences through preprocessor or runtime checks573- Test on multiple platforms (Linux, macOS, Windows)574- Use standard integer types from `<cstdint>`575- Examples:576 ```cpp577 #ifdef _WIN32578 // Windows-specific code579 #include <windows.h>580 #else581 // POSIX code582 #include <unistd.h>583 #endif584585 // Use standard fixed-size types586 uint32_t value32;587 int64_t offset;588 ```589590## Compiler Warnings591592- Build with strict warnings enabled:593 - GCC/Clang: `-Wall -Wextra -Wpedantic`594 - MSVC: `/W4`595- Treat warnings as errors in development builds596- Fix all warnings - don't suppress them unless absolutely necessary597- Document any warning suppressions with reasoning598599## Testing Strategy600601- Write unit tests for all public APIs602- Test edge cases: null pointers, empty containers, boundary values603- Use test frameworks (Google Test, Catch2, etc.)604- Mock dependencies for isolated testing605- Test on all target platforms606- Examples:607 ```cpp608 TEST(MyClassTest, ConstructorInitializesCorrectly)609 {610 MyClass obj;611 EXPECT_EQ(0, obj.GetValue());612 }613614 TEST(MyClassTest, SetValueUpdatesCorrectly)615 {616 MyClass obj;617 obj.SetValue(42);618 EXPECT_EQ(42, obj.GetValue());619 }620 ```621622## Documentation623624- Document all public APIs in header files625- Include purpose, parameters, return values, and exceptions626- Use Doxygen format for API documentation627- **CRITICAL: Always verify documentation against actual implementation**628- README must show real API patterns, not fictional functions629- Use actual class names and member names from header files630- Integration examples must use real function signatures631- Keep documentation synchronized with code changes632- Example:633 ```cpp634 /// \brief Creates a new user account635 /// \param username The unique username for the account636 /// \param email The user's email address637 /// \return A unique pointer to the created User object638 /// \throws std::invalid_argument if username is empty639 std::unique_ptr<User> CreateUser(640 const std::string& username,641 const std::string& email);642 ```643644## Documentation Tools645646- Use Doxygen for API documentation generation647- Use Graphviz DOT for class diagrams and dependency diagrams648- Use `@dot...@enddot` blocks for custom graphs649- Keep diagrams clean and focused on domain relationships650- Treat standard types (String, etc.) as primitives in diagrams651652## Code Review Checklist653654- [ ] All public APIs have Doxygen documentation655- [ ] Const correctness applied throughout656- [ ] Constant-left comparisons used consistently657- [ ] Smart pointers used instead of raw pointers658- [ ] RAII principles applied for resource management659- [ ] Rule of Zero or Rule of Five followed correctly660- [ ] No memory leaks (verified with valgrind or similar)661- [ ] Code compiles without warnings on all platforms662- [ ] Unit tests pass663- [ ] Include guards or pragma once used correctly664- [ ] Namespaces used appropriately665- [ ] Modern C++ features used where beneficial666- [ ] Code formatted according to project standards667668## Build System (CMake)669670- Use CMake 3.20+ for modern features671- Support multiple platforms (Linux, macOS, Windows)672- Support multiple compilers (GCC, Clang, MSVC)673- Generate both shared and static libraries674- Use CMake targets and properties675- Example CMakeLists.txt structure:676 ```cmake677 cmake_minimum_required(VERSION 3.20)678 project(MyProject VERSION 1.0.0 LANGUAGES CXX)679680 set(CMAKE_CXX_STANDARD 23)681 set(CMAKE_CXX_STANDARD_REQUIRED ON)682683 # Library target684 add_library(mylib685 src/MyClass.cpp686 src/Helper.cpp687 )688689 target_include_directories(mylib690 PUBLIC include691 PRIVATE src692 )693694 # Executable target695 add_executable(myapp696 src/main.cpp697 )698699 target_link_libraries(myapp PRIVATE mylib)700 ```