Skip to content

Commit d4fb352

Browse files
authored
C++ Rewrite (#443)
* C++ Rewrite Rewrite codebase from C to modern C++20 Complete modernization introducing type safety, better error handling, and a cleaner architecture. - **Language**: C → C++20 (requires GCC 10+, Clang 10+, MSVC 2019+) - **Structure**: Reorganized into lib/, cli/, tests/ - **Error handling**: Result<T> type with rich error information - **Device code**: 50-70% reduction via protocol templates - High-level C++ API (headsetcontrol.hpp) - C API for FFI bindings (headsetcontrol_c.h) - Shared library support (-DBUILD_SHARED_LIBRARY=ON) - Protocol templates: HIDPPDevice, SteelSeriesNovaDevice - Data-driven capability system - Test suite - HIDDevice base class with virtual methods per capability - Device registry singleton for device lookup - Capability descriptors as single source of truth - Feature handler registry (replaces switch statements) CLI interface unchanged - fully backwards compatible. - See docs/ADDING_A_DEVICE.md for adding devices - See docs/ADDING_A_CAPABILITY.md for adding features - See docs/LIBRARY_USAGE.md for library integration
1 parent abe3ac8 commit d4fb352

File tree

161 files changed

+23228
-8338
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

161 files changed

+23228
-8338
lines changed

.github/workflows/build-windows.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ jobs:
1616
msystem: MINGW64
1717
update: true
1818
install: git base-devel mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake mingw-w64-x86_64-hidapi make
19+
- name: Check compiler version
20+
run: |
21+
gcc --version
22+
g++ --version
1923
- name: Build
2024
run: |
2125
mkdir build
@@ -25,7 +29,7 @@ jobs:
2529
- name: Test
2630
run: |
2731
cd build
28-
make test
32+
ctest --output-on-failure
2933
- name: Upload artifact
3034
uses: actions/upload-artifact@v4
3135
with:

.github/workflows/build.yml

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,26 @@ jobs:
2525
fetch-depth: 0
2626
- uses: lukka/get-cmake@latest
2727

28+
- name: Install GCC 13 (Ubuntu)
29+
if: matrix.os == 'ubuntu-latest'
30+
run: |
31+
sudo apt-get update
32+
sudo apt-get install -y gcc-13 g++-13
33+
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 100
34+
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 100
35+
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-13 100
36+
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++-13 100
37+
2838
- name: Install Dependencies
2939
run: ${{ matrix.INSTALL_DEPS }}
3040

41+
- name: Check compiler version
42+
run: |
43+
echo "C compiler:"
44+
cc --version
45+
echo "C++ compiler:"
46+
c++ --version
47+
3148
- name: dir
3249
run: find $RUNNER_WORKSPACE
3350
shell: bash
@@ -40,7 +57,7 @@ jobs:
4057
buildDirectory: ${{ env.buildDir }}
4158

4259
- name: Run test
43-
run: cd ${{ env.buildDir }} && ctest
60+
run: cd ${{ env.buildDir }} && ctest --output-on-failure
4461

4562
- name: dir
4663
run: find $RUNNER_WORKSPACE

.gitignore

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
build/
2-
src/version.h
2+
build-msvc/
3+
lib/version.h
34

45
.idea/
6+
.vs/
7+
.claude/
58
cmake-build-debug/
69
.codechecker/
10+
.scannerwork/
711

812
.vscode/*
913
*.code-workspace
1014
*.cache/
1115

12-
.DS_Store
16+
.DS_Store
17+
18+
Testing/

CHANGELOG_CPP_REWRITE.md

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
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

Comments
 (0)