|
| 1 | +# Changelog: C++ Modernization |
| 2 | + |
| 3 | +This document describes the changes in the C++ modernization branch. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +Complete rewrite of HeadsetControl from C to modern C++20, introducing: |
| 8 | +- Type-safe error handling with `Result<T>` |
| 9 | +- RAII resource management |
| 10 | +- Protocol abstraction templates |
| 11 | +- High-level library API |
| 12 | +- Comprehensive test suite |
| 13 | + |
| 14 | +## Breaking Changes |
| 15 | + |
| 16 | +- **Minimum C++ standard**: Now requires C++20 (GCC 10+, Clang 10+, MSVC 2019+) |
| 17 | +- **Project structure**: Code reorganized into `lib/`, `cli/`, `tests/` |
| 18 | +- **Header extensions**: Changed from `.h` to `.hpp` for C++ headers |
| 19 | + |
| 20 | +## New Features |
| 21 | + |
| 22 | +### High-Level Library API |
| 23 | + |
| 24 | +New `headsetcontrol.hpp` provides a simple, HID-abstracted interface: |
| 25 | + |
| 26 | +```cpp |
| 27 | +#include <headsetcontrol.hpp> |
| 28 | + |
| 29 | +auto headsets = headsetcontrol::discover(); |
| 30 | +for (auto& headset : headsets) { |
| 31 | + if (headset.supports(CAP_BATTERY_STATUS)) { |
| 32 | + auto battery = headset.getBattery(); |
| 33 | + if (battery) { |
| 34 | + std::cout << battery->level_percent << "%\n"; |
| 35 | + } |
| 36 | + } |
| 37 | +} |
| 38 | +``` |
| 39 | + |
| 40 | +### C API for FFI |
| 41 | + |
| 42 | +New `headsetcontrol_c.h` provides a pure C interface for bindings: |
| 43 | + |
| 44 | +```c |
| 45 | +hsc_headset_t* headsets; |
| 46 | +int count = hsc_discover(&headsets); |
| 47 | +// ... use headsets ... |
| 48 | +hsc_free_headsets(headsets, count); |
| 49 | +``` |
| 50 | +
|
| 51 | +### Result<T> Error Handling |
| 52 | +
|
| 53 | +All device methods now return `Result<T>` with rich error information: |
| 54 | +
|
| 55 | +```cpp |
| 56 | +auto result = device->getBattery(handle); |
| 57 | +if (result) { |
| 58 | + // Success: access result->level_percent, result->status, etc. |
| 59 | +} else { |
| 60 | + // Error: result.error().code, result.error().message |
| 61 | +} |
| 62 | +``` |
| 63 | + |
| 64 | +### Protocol Templates |
| 65 | + |
| 66 | +Reusable protocol implementations reduce device code by 60-80%: |
| 67 | + |
| 68 | +- `HIDPPDevice<T>` - Logitech HID++ protocol |
| 69 | +- `SteelSeriesDevice<T>` - SteelSeries protocol family |
| 70 | +- `CorsairDevice<T>` - Corsair iCUE protocol |
| 71 | + |
| 72 | +### Rich Result Types |
| 73 | + |
| 74 | +New structured result types with extended information: |
| 75 | + |
| 76 | +- `BatteryResult` - level, status, voltage, time estimates |
| 77 | +- `SidetoneResult` - level with device range info |
| 78 | +- `ChatmixResult` - level with game/chat percentages |
| 79 | +- `EqualizerInfo` - bands, range, step size |
| 80 | + |
| 81 | +### Data-Driven Feature System |
| 82 | + |
| 83 | +New capability descriptor and handler registry system: |
| 84 | + |
| 85 | +- `CapabilityDescriptor` - Single source of truth for all capability metadata (CLI flags, descriptions, value ranges) |
| 86 | +- `FeatureHandlerRegistry` - Dispatch table replacing giant switch statements |
| 87 | +- Automatic parameter validation from descriptors |
| 88 | +- Auto-generated help text value hints from descriptors |
| 89 | + |
| 90 | +```cpp |
| 91 | +// All capability metadata in one place |
| 92 | +inline constexpr std::array<CapabilityDescriptor, NUM_CAPABILITIES> CAPABILITY_DESCRIPTORS = {{ |
| 93 | + {CAP_SIDETONE, CAPABILITYTYPE_ACTION, "sidetone", "-s", "Set sidetone level", 0, 128, "<0-128>"}, |
| 94 | + // ... |
| 95 | +}}; |
| 96 | + |
| 97 | +// Feature execution via registry |
| 98 | +auto result = FeatureHandlerRegistry::instance().execute(cap, device, handle, param); |
| 99 | +``` |
| 100 | + |
| 101 | +### Shared Library Support |
| 102 | + |
| 103 | +- `BUILD_SHARED_LIBRARY` CMake option for creating dynamic library |
| 104 | +- Windows DLL export macros (`HSC_API`) |
| 105 | +- Versioned shared library with SOVERSION |
| 106 | + |
| 107 | +### Comprehensive Test Suite |
| 108 | + |
| 109 | +- Unit tests for utility functions (67 tests) |
| 110 | +- Mock device tests |
| 111 | +- Integration tests |
| 112 | +- Test device with all capabilities |
| 113 | + |
| 114 | +## Architecture Changes |
| 115 | + |
| 116 | +### Project Structure |
| 117 | + |
| 118 | +``` |
| 119 | +HeadsetControl/ |
| 120 | +├── lib/ # Core library |
| 121 | +│ ├── devices/ # Device implementations |
| 122 | +│ │ ├── protocols/ # Protocol templates |
| 123 | +│ │ └── *.hpp # Device classes |
| 124 | +│ ├── output/ # Serialization |
| 125 | +│ ├── headsetcontrol.hpp # High-level C++ API |
| 126 | +│ ├── headsetcontrol_c.h # C API |
| 127 | +│ ├── result_types.hpp # Result<T> and error types |
| 128 | +│ └── device.hpp # Capability enums |
| 129 | +├── cli/ # Command-line interface |
| 130 | +│ ├── main.cpp |
| 131 | +│ └── argument_parser.hpp |
| 132 | +├── tests/ # Test suite |
| 133 | +└── docs/ # Documentation |
| 134 | +``` |
| 135 | + |
| 136 | +### Device Implementation Pattern |
| 137 | + |
| 138 | +Old (C): |
| 139 | +```c |
| 140 | +// 200+ lines per device |
| 141 | +struct device corsair_void_init() { |
| 142 | + struct device dev; |
| 143 | + dev.idVendor = 0x1b1c; |
| 144 | + dev.idProductsSupported = product_ids; |
| 145 | + dev.send_sidetone = &corsair_void_send_sidetone; |
| 146 | + // ... many more function pointers ... |
| 147 | + return dev; |
| 148 | +} |
| 149 | +``` |
| 150 | + |
| 151 | +New (C++): |
| 152 | +```cpp |
| 153 | +// 50-100 lines per device |
| 154 | +class CorsairVoid : public HIDDevice { |
| 155 | +public: |
| 156 | + uint16_t getVendorId() const override { return 0x1b1c; } |
| 157 | + std::vector<uint16_t> getProductIds() const override { return {0x0a14, ...}; } |
| 158 | + |
| 159 | + Result<SidetoneResult> setSidetone(hid_device* h, uint8_t level) override { |
| 160 | + // Implementation with proper error handling |
| 161 | + } |
| 162 | +}; |
| 163 | +``` |
| 164 | + |
| 165 | +### Modern C++ Features Used |
| 166 | + |
| 167 | +- `std::format` for string formatting |
| 168 | +- `std::span` for buffer views |
| 169 | +- `std::optional` for nullable values |
| 170 | +- `std::string_view` for zero-copy strings |
| 171 | +- `std::chrono` for time handling |
| 172 | +- `[[nodiscard]]` for error checking |
| 173 | +- Designated initializers for structs |
| 174 | +- Concepts for type constraints |
| 175 | +- CTAD (Class Template Argument Deduction) |
| 176 | + |
| 177 | +## File Changes Summary |
| 178 | + |
| 179 | +### Renamed/Moved |
| 180 | +- `src/*.c` → `lib/*.cpp` |
| 181 | +- `src/*.h` → `lib/*.hpp` |
| 182 | +- `src/devices/*.c` → `lib/devices/*.hpp` |
| 183 | +- `src/main.c` → `cli/main.cpp` |
| 184 | + |
| 185 | +### New Files |
| 186 | +- `lib/headsetcontrol.hpp` - High-level C++ API |
| 187 | +- `lib/headsetcontrol.cpp` - API implementation |
| 188 | +- `lib/headsetcontrol_c.h` - C API header |
| 189 | +- `lib/headsetcontrol_c.cpp` - C API implementation |
| 190 | +- `lib/result_types.hpp` - Result<T> type |
| 191 | +- `lib/capability_descriptors.hpp` - Capability metadata (single source of truth) |
| 192 | +- `lib/feature_handlers.hpp` - Feature handler registry |
| 193 | +- `lib/feature_utils.hpp` - Feature helper functions |
| 194 | +- `lib/string_utils.hpp` - String utilities |
| 195 | +- `lib/devices/device_utils.hpp` - Device utilities |
| 196 | +- `lib/devices/hid_device.hpp` - Base device class |
| 197 | +- `lib/devices/hid_interface.hpp` - HID abstraction |
| 198 | +- `lib/devices/protocols/*.hpp` - Protocol templates |
| 199 | +- `lib/output/serializers.hpp` - Output serialization |
| 200 | +- `lib/output/output_data.hpp` - Output data models |
| 201 | +- `cli/argument_parser.hpp` - CLI argument parsing |
| 202 | +- `tests/*.cpp` - Test files |
| 203 | +- `docs/ADDING_A_DEVICE.md` - Device development guide |
| 204 | +- `docs/LIBRARY_USAGE.md` - Library integration guide |
| 205 | + |
| 206 | +### Deleted |
| 207 | +- All `src/devices/*.c` files (replaced by `.hpp`) |
| 208 | +- `src/device_registry.c` (replaced by `lib/device_registry.cpp`) |
| 209 | +- `src/output.c` (replaced by `lib/output/`) |
| 210 | + |
| 211 | +## Documentation |
| 212 | + |
| 213 | +New documentation added: |
| 214 | +- `docs/ADDING_A_DEVICE.md` - How to add new device support |
| 215 | +- `docs/LIBRARY_USAGE.md` - Using HeadsetControl as a library (C++, C, Python, Rust examples) |
| 216 | +- Updated `CLAUDE.md` - Developer guidance |
| 217 | + |
| 218 | +## Build System |
| 219 | + |
| 220 | +- CMake structure updated for `lib/`, `cli/`, `tests/` layout |
| 221 | +- Library target: `headsetcontrol_lib` |
| 222 | +- CLI target: `headsetcontrol` |
| 223 | +- Test target: `headsetcontrol_tests` |
| 224 | +- Install targets for library and headers |
| 225 | + |
| 226 | +## Migration Notes |
| 227 | + |
| 228 | +### For Users |
| 229 | +No changes - CLI interface remains the same. |
| 230 | + |
| 231 | +### For Developers Adding Devices |
| 232 | +1. Create `lib/devices/vendor_model.hpp` |
| 233 | +2. Inherit from `HIDDevice` or protocol template |
| 234 | +3. Implement virtual methods |
| 235 | +4. Register in `lib/device_registry.cpp` |
| 236 | + |
| 237 | +See `docs/ADDING_A_DEVICE.md` for details. |
| 238 | + |
| 239 | +### For Library Users |
| 240 | +New high-level API available: |
| 241 | +- C++: `#include <headsetcontrol.hpp>` |
| 242 | +- C: `#include <headsetcontrol_c.h>` |
| 243 | + |
| 244 | +See `docs/LIBRARY_USAGE.md` for integration guide. |
0 commit comments