Coding rules

# Naming Conventions

- Classes/Structs: PascalCase (MotorController, SensorData)
- Functions/Methods: camelCase (readSensor, calculateSpeed)
- Variables: camelCase (sensorValue, maxSpeed)
- Constants: UPPER_SNAKE_CASE (MAX_BUFFER_SIZE, DEFAULT_TIMEOUT)
- Macros: UPPER_SNAKE_CASE (ENABLE_DEBUG, PIN_LED)
- Namespaces: lowercase (sensors, communication)
- Private members: prefix with m_ (m_temperature, m_isActive)
- Pointers: prefix with p or suffix with Ptr (pBuffer, dataPtr)
- Enums: PascalCase for type, UPPER_SNAKE_CASE for values
  enum class State { IDLE, RUNNING, ERROR };

# File Organization

- Headers: *.h* extension
- Source: *.cpp* or *.c* extension
- One class per file (except nested/helper classes)
- File naming: match class name (MotorController.h, MotorController.cpp)
- Header guards: use `#pragma once`

# Header Files

- Include order:
  - Corresponding header (in .cpp)
  - Project headers `#include "file.h"`
  - System headers `#include <file.h>`
- Forward declarations: use when possible to reduce dependencies
- Minimize includes: only include what you use
- Separate interface from implementation

# Code Formatting

- Indentation: 2 spaces (no tabs)
- Braces: Allman style (be consistent)
  // Allman
  if (condition)
  {
  doSomething();
  }
- Line length: 80-120 characters maximum
- Spaces: around operators (`a + b`, not `a+b`)
- Pointer/reference: attach to variable (`int *ptr`, not `int* ptr`)

# Memory Management

## PC

- Prefer RAII: Resource Acquisition Is Initialization
- Smart pointers: use `std::unique_ptr`, `std::shared_ptr` or `std::weak_ptr`
- Avoid raw new/delete: use smart pointers or containers
- No memory leaks: every allocation must have deallocation

## Microcontroller

- Minimize dynamic allocation: prefer stack or static allocation
- No new/delete in ISRs: Interrupt Service Routines
- Pre-allocate buffers: avoid runtime allocation
- Static memory pools: if dynamic allocation needed
- Check heap fragmentation: monitor free heap regularly

# Functions

- Single Responsibility: one function, one task
- Function length: maximum 50 lines (20-30 preferred)
- Parameters: maximum 4-5 parameters (use structs if more)
- Pass by reference: for non-primitive types (const Type&)
- Return values: prefer return over out-parameters
- Const correctness: mark const wherever applicable

# Classes

- Rule of Five: define or delete copy/move constructors and assignment
- Virtual destructors: for base classes with virtual methods
- Explicit constructors: use explicit for single-argument constructors
  class MyClass
  {
  MyClass() = default; // Default constructor
  virtual ~MyClass() = default; // Default destructor
  MyClass(const MyClass &other) = delete; // Copy constructor
  MyClass(MyClass &&other) noexcept = delete; // Move constructor
  MyClass &operator=(const MyClass &other) = delete; // Copy assignment
  MyClass &operator=(MyClass &&other) noexcept = delete; // Move assignment
  explicit MyClass(int val); // Constructor
  }

# Modern C++ Features (PC)

- Use auto: when type is obvious
- Range-based loops: `for (const auto& item : container)`
- nullptr: instead of NULL or 0
- constexpr: for compile-time constants
- Lambda functions: for short callbacks
- Move semantics: implement when appropriate
- std::array: instead of C-style arrays
- enum class: instead of plain enums
- Use C++ structured bingings
  std::map<std::string, int> ages = {{"Alice", 30}, {"Bob", 25}};
  // Before C++17
  for (const auto& pair : ages) {
  Serial.println(pair.first); // key
  Serial.println(pair.second); // value
  }
  // C++17: structured bindings
  for (const auto& [name, age] : ages) {
  Serial.println(name);
  Serial.println(age);
  }
  // With arrays
  int arr[][2] = {{1, 2}, {3, 4}, {5, 6}};
  for (auto& [a, b] : arr) {
  cout << a << " " << b << endl;
  }
  // With tuples
  std::vector<std::tuple<int, float, std::string>> data;
  for (const auto& [id, value, name] : data) {
  // Use id, value, name directly
  }
- User C++ Attributes
  // [[nodiscard]] - Warn if return value ignored (C++17)
  [[nodiscard]] int calculateSum(int a, int b);
  calculateSum(3, 4); // Warning: result unused
  // [[maybe_unused]] - Suppress unused warnings
  void debug([[maybe_unused]] int value);
  // [[fallthrough]] - Intentional switch fallthrough (C++17)
  switch (value) {
  case 1:
  doSomething();
  [[fallthrough]];
  case 2:
  doSomethingElse();
  break;
  }
  // [[deprecated]] - Mark as deprecated
  [[deprecated("Use newFunction() instead")]]
  void oldFunction();
  // [[likely]] / [[unlikely]] - Branch prediction hints (C++20)
  if (condition) [[likely]] {
  // Most common path
  } else {
  // Rare path
  }

# Embedded / Microcontroller Specific

- Avoid exceptions: often disabled on MCUs (use error codes)
- Avoid RTTI: disable Runtime Type Information (-fno-rtti)
- Minimize STL: use selectively (some implementations are heavy)
- Volatile keyword: for hardware registers and ISR-shared variables
- ISR constraints:
  - Keep ISRs short (< 10 microseconds if possible)
  - No blocking operations
  - Avoid dynamic memory allocation
  - Minimize shared variable access
- Optimization pragmas: use carefully
- Inline functions: for time-critical code
- Bit manipulation: use for register operations
  PORTB |= (1 << PB5); // Set bit
  PORTB &= ~(1 << PB5); // Clear bit

# Constants and Macros

- Prefer const/constexpr over define
- Macros: use UPPERCASE and parentheses
  #define MAX(a, b) ((a) > (b) ? (a) : (b))
- Configuration constants: group in header
  namespace Config
  {
  constexpr uint32_t BAUD_RATE = 115200;
  constexpr uint8_t BUFFER_SIZE = 64;
  }

# Error Handling

## PC

- Exceptions: for exceptional conditions
- Error codes: for expected failures
- RAII: for resource cleanup

## Microcontroller

- Return codes: primary error handling method
- Error enums: define clear error types
  enum class ErrorCode { OK, TIMEOUT, INVALID_PARAM, HW_ERROR };
- Assert macros: for development/debugging
- Watchdog timer: implement for fault recovery

# Comments and Documentation

- Doxygen format: for API documentation
- Self-documenting code: prefer clear names over comments
- TODO/FIXME: mark incomplete or problematic code
- Explain "why": not "what" (code shows what)
- Update comments: keep in sync with code

# Code Quality

- No magic numbers: use named constants
  constexpr int MAX_RETRIES = 3;
  if (retries > MAX_RETRIES)
  {
  /* ... */
  }
- Avoid global variables: use singletons or dependency injection
- Initialize variables: always initialize at declaration
- Avoid deep nesting: maximum 3-4 levels
- Guard clauses: early return for error conditions
  if (!isValid)
  return ERROR_INVALID;
  // main logic here

# Concurrency (where applicable)

- Atomic operations: for shared variables
- Mutexes: protect critical sections (PC)
- Disable interrupts: for critical sections (MCU)
- Volatile: for interrupt-shared variables
- Lock-free algorithms: when possible on MCU

# Performance

- Avoid floating-point: on MCUs without FPU
- Fixed-point arithmetic: for fractional values on MCU
- Lookup tables: for complex calculations
- Inline critical functions: use inline keyword
- Profile before optimizing: measure, don't guess
- Compiler optimization flags: -O2, -O3 (test thoroughly)

# Testing

- Unit tests: for all public interfaces
- Mock hardware: for embedded testing
- Test on target: always test on actual hardware
- Boundary conditions: test edge cases
- Code coverage: aim for >80%

# Version Control

- Meaningful commits: descriptive commit messages
- Feature branches: for new features
- Code review: before merging
- Avoid generated files: in repository (.gitignore)

# Build System

- CMake: as soon as possible
- Compiler warnings: enable all (-Wall -Wextra -Wpedantic)
- Treat warnings as errors: -Werror
- Debug/Release configs: separate configurations

# —

# Lexicon

RTTI (Run-time type information): run-time type information is a feature that exposes information about an object's data type at runtime. RTTI can be used to do safe typecasts using the `dynamic_cast<>` operator, and to manipulate type information at runtime using the `typeid` operator and `std::type_info` class.

RAII (Resource acquisition is initialization): Resource acquisition is initialization is a programming idiom used in several object-oriented, statically typed programming languages to describe a particular language behavior. Resource acquisition (allocation) is done during object creation by the constructor, while resource deallocation (release) is done during object destruction by the destructor. In other words, resource acquisition must succeed for initialization to succeed. The resource is guaranteed to be held between the end of initialization and the starts of finalization, and only as long as the object is active. Thus, if there are no object leaks, there are no resource leaks.