This document describes the changes in the C++ modernization branch.
Complete rewrite of HeadsetControl from C to modern C++20, introducing:
- Type-safe error handling with
Result<T> - RAII resource management
- Protocol abstraction templates
- High-level library API
- Comprehensive test suite
- Minimum C++ standard: Now requires C++20 (GCC 10+, Clang 10+, MSVC 2019+)
- Project structure: Code reorganized into
lib/,cli/,tests/ - Header extensions: Changed from
.hto.hppfor C++ headers
New headsetcontrol.hpp provides a simple, HID-abstracted interface:
#include <headsetcontrol.hpp>
auto headsets = headsetcontrol::discover();
for (auto& headset : headsets) {
if (headset.supports(CAP_BATTERY_STATUS)) {
auto battery = headset.getBattery();
if (battery) {
std::cout << battery->level_percent << "%\n";
}
}
}New headsetcontrol_c.h provides a pure C interface for bindings:
hsc_headset_t* headsets;
int count = hsc_discover(&headsets);
// ... use headsets ...
hsc_free_headsets(headsets, count);All device methods now return Result<T> with rich error information:
auto result = device->getBattery(handle);
if (result) {
// Success: access result->level_percent, result->status, etc.
} else {
// Error: result.error().code, result.error().message
}Reusable protocol implementations reduce device code by 60-80%:
HIDPPDevice<T>- Logitech HID++ protocolSteelSeriesDevice<T>- SteelSeries protocol familyCorsairDevice<T>- Corsair iCUE protocol
New structured result types with extended information:
BatteryResult- level, status, voltage, time estimatesSidetoneResult- level with device range infoChatmixResult- level with game/chat percentagesEqualizerInfo- bands, range, step size
New capability descriptor and handler registry system:
CapabilityDescriptor- Single source of truth for all capability metadata (CLI flags, descriptions, value ranges)FeatureHandlerRegistry- Dispatch table replacing giant switch statements- Automatic parameter validation from descriptors
- Auto-generated help text value hints from descriptors
// All capability metadata in one place
inline constexpr std::array<CapabilityDescriptor, NUM_CAPABILITIES> CAPABILITY_DESCRIPTORS = {{
{CAP_SIDETONE, CAPABILITYTYPE_ACTION, "sidetone", "-s", "Set sidetone level", 0, 128, "<0-128>"},
// ...
}};
// Feature execution via registry
auto result = FeatureHandlerRegistry::instance().execute(cap, device, handle, param);BUILD_SHARED_LIBRARYCMake option for creating dynamic library- Windows DLL export macros (
HSC_API) - Versioned shared library with SOVERSION
- Unit tests for utility functions (67 tests)
- Mock device tests
- Integration tests
- Test device with all capabilities
HeadsetControl/
├── lib/ # Core library
│ ├── devices/ # Device implementations
│ │ ├── protocols/ # Protocol templates
│ │ └── *.hpp # Device classes
│ ├── output/ # Serialization
│ ├── headsetcontrol.hpp # High-level C++ API
│ ├── headsetcontrol_c.h # C API
│ ├── result_types.hpp # Result<T> and error types
│ └── device.hpp # Capability enums
├── cli/ # Command-line interface
│ ├── main.cpp
│ └── argument_parser.hpp
├── tests/ # Test suite
└── docs/ # Documentation
Old (C):
// 200+ lines per device
struct device corsair_void_init() {
struct device dev;
dev.idVendor = 0x1b1c;
dev.idProductsSupported = product_ids;
dev.send_sidetone = &corsair_void_send_sidetone;
// ... many more function pointers ...
return dev;
}New (C++):
// 50-100 lines per device
class CorsairVoid : public HIDDevice {
public:
uint16_t getVendorId() const override { return 0x1b1c; }
std::vector<uint16_t> getProductIds() const override { return {0x0a14, ...}; }
Result<SidetoneResult> setSidetone(hid_device* h, uint8_t level) override {
// Implementation with proper error handling
}
};std::formatfor string formattingstd::spanfor buffer viewsstd::optionalfor nullable valuesstd::string_viewfor zero-copy stringsstd::chronofor time handling[[nodiscard]]for error checking- Designated initializers for structs
- Concepts for type constraints
- CTAD (Class Template Argument Deduction)
src/*.c→lib/*.cppsrc/*.h→lib/*.hppsrc/devices/*.c→lib/devices/*.hppsrc/main.c→cli/main.cpp
lib/headsetcontrol.hpp- High-level C++ APIlib/headsetcontrol.cpp- API implementationlib/headsetcontrol_c.h- C API headerlib/headsetcontrol_c.cpp- C API implementationlib/result_types.hpp- Result typelib/capability_descriptors.hpp- Capability metadata (single source of truth)lib/feature_handlers.hpp- Feature handler registrylib/feature_utils.hpp- Feature helper functionslib/string_utils.hpp- String utilitieslib/devices/device_utils.hpp- Device utilitieslib/devices/hid_device.hpp- Base device classlib/devices/hid_interface.hpp- HID abstractionlib/devices/protocols/*.hpp- Protocol templateslib/output/serializers.hpp- Output serializationlib/output/output_data.hpp- Output data modelscli/argument_parser.hpp- CLI argument parsingtests/*.cpp- Test filesdocs/ADDING_A_DEVICE.md- Device development guidedocs/LIBRARY_USAGE.md- Library integration guide
- All
src/devices/*.cfiles (replaced by.hpp) src/device_registry.c(replaced bylib/device_registry.cpp)src/output.c(replaced bylib/output/)
New documentation added:
docs/ADDING_A_DEVICE.md- How to add new device supportdocs/LIBRARY_USAGE.md- Using HeadsetControl as a library (C++, C, Python, Rust examples)- Updated
CLAUDE.md- Developer guidance
- CMake structure updated for
lib/,cli/,tests/layout - Library target:
headsetcontrol_lib - CLI target:
headsetcontrol - Test target:
headsetcontrol_tests - Install targets for library and headers
No changes - CLI interface remains the same.
- Create
lib/devices/vendor_model.hpp - Inherit from
HIDDeviceor protocol template - Implement virtual methods
- Register in
lib/device_registry.cpp
See docs/ADDING_A_DEVICE.md for details.
New high-level API available:
- C++:
#include <headsetcontrol.hpp> - C:
#include <headsetcontrol_c.h>
See docs/LIBRARY_USAGE.md for integration guide.