Sound Byte Libs 29c5ff3
C++ firmware library for audio applications on 32-bit ARM Cortex-M processors
Loading...
Searching...
No Matches
method_detection.hpp
Go to the documentation of this file.
1/**
2 * @file method_detection.hpp
3 * @brief Method and interface detection for driver validation
4 * @ingroup validation
5 *
6 * Uses C++17 SFINAE (Substitution Failure Is Not An Error) to detect what methods
7 * drivers provide at compile-time. This enables comprehensive driver contract
8 * validation with clear error messages.
9 *
10 * ## SFINAE Pattern
11 *
12 * All detection uses the consistent std::void_t pattern:
13 * ```cpp
14 * template<typename T, typename = void>
15 * struct has_method : std::false_type {};
16 *
17 * template<typename T>
18 * struct has_method<T, std::void_t<decltype(T::method(...))>>
19 * : std::true_type {};
20 * ```
21 *
22 * @see gpio_requirements.hpp
23 */
24
25#ifndef SBL_VALIDATION_METHOD_DETECTION_HPP_
26#define SBL_VALIDATION_METHOD_DETECTION_HPP_
27
28#include <type_traits>
29#include <cstdint>
30#include <cstddef>
31
32// Include canonical HAL types
36#include <sbl/hal/adc/types.hpp>
39
40namespace sbl {
41namespace validation {
42
43/**
44 * @brief GPIO Driver Method Detection (Handle-First API)
45 *
46 * Detects presence of required methods in GPIO driver implementations.
47 * All GPIO drivers must implement:
48 * - set_mode(const GpioHandle&, PinMode)
49 * - write(const GpioHandle&, bool)
50 * - read(const GpioHandle&) -> bool
51 * - toggle(const GpioHandle&)
52 */
53
54// Detect: static void set_mode(const GpioHandle&, PinMode)
55template<typename T, typename = void>
56struct has_gpio_set_mode : std::false_type {};
57
58template<typename T>
59struct has_gpio_set_mode<T, std::void_t<decltype(
60 T::set_mode(std::declval<const sbl::GpioHandle&>(),
61 std::declval<sbl::hal::gpio::PinMode>()))>>
62 : std::true_type {};
63
64// Detect: static void write(const GpioHandle&, bool)
65template<typename T, typename = void>
66struct has_gpio_write : std::false_type {};
67
68template<typename T>
69struct has_gpio_write<T, std::void_t<decltype(
70 T::write(std::declval<const sbl::GpioHandle&>(),
71 std::declval<bool>()))>>
72 : std::true_type {};
73
74// Detect: static bool read(const GpioHandle&)
75template<typename T, typename = void>
76struct has_gpio_read : std::false_type {};
77
78template<typename T>
79struct has_gpio_read<T, std::void_t<decltype(
80 T::read(std::declval<const sbl::GpioHandle&>()))>>
81 : std::true_type {};
82
83// Detect: static void toggle(const GpioHandle&)
84template<typename T, typename = void>
85struct has_gpio_toggle : std::false_type {};
86
87template<typename T>
88struct has_gpio_toggle<T, std::void_t<decltype(
89 T::toggle(std::declval<const sbl::GpioHandle&>()))>>
90 : std::true_type {};
91
92/**
93 * @brief Combined GPIO driver interface check
94 *
95 * True if driver implements all required handle-first GPIO methods.
96 */
97template<typename GpioDriver>
105
106/**
107 * @brief Timer Driver Method Detection
108 *
109 * Required (all platforms):
110 * - busy_wait_ms(uint32_t)
111 * - millis() -> uint32_t
112 * - micros() -> uint32_t
113 *
114 * Optional (platform-specific):
115 * - init(uint32_t) — STM32 needs it, RP2xxx don't
116 */
117
118// Detect: static void busy_wait_ms(uint32_t)
119template<typename T, typename = void>
120struct has_timer_busy_wait_ms : std::false_type {};
121
122template<typename T>
123struct has_timer_busy_wait_ms<T, std::void_t<decltype(
124 T::busy_wait_ms(std::declval<uint32_t>()))>>
125 : std::true_type {};
126
127// Detect: static uint32_t millis()
128template<typename T, typename = void>
129struct has_timer_millis : std::false_type {};
130
131template<typename T>
132struct has_timer_millis<T, std::void_t<decltype(T::millis())>>
133 : std::true_type {};
134
135// Detect: static uint32_t micros()
136template<typename T, typename = void>
137struct has_timer_micros : std::false_type {};
138
139template<typename T>
140struct has_timer_micros<T, std::void_t<decltype(T::micros())>>
141 : std::true_type {};
142
143// Detect: static init(uint32_t) — optional
144template<typename T, typename = void>
145struct has_timer_init : std::false_type {};
146
147template<typename T>
148struct has_timer_init<T, std::void_t<decltype(
149 T::init(std::declval<uint32_t>()))>>
150 : std::true_type {};
151
152// Return type: micros() must return uint32_t
153template<typename T, typename = void>
154struct timer_micros_returns_uint32 : std::false_type {};
155
156template<typename T>
157struct timer_micros_returns_uint32<T, std::enable_if_t<
158 std::is_same_v<decltype(T::micros()), uint32_t>>> : std::true_type {};
159
160/**
161 * @brief Combined Timer driver interface check
162 *
163 * True if driver implements all required Timer methods.
164 */
165template<typename TimerDriver>
172
173/**
174 * @brief UART Driver Method Detection
175 *
176 * Detects presence of required methods in UART driver implementations.
177 *
178 * Required (all platforms):
179 * - init(const UartHandle&)
180 * - init_rx(const UartHandle&)
181 * - write_byte(uint8_t)
182 * - write(const uint8_t*, size_t)
183 * - write_string(const char*)
184 * - available() -> bool
185 * - read_byte() -> uint8_t
186 *
187 * Optional (platform-specific):
188 * - try_write_byte(uint8_t) -> bool
189 * - try_write_string(const char*) -> size_t
190 */
191
192// Detect: static init(const UartHandle&) — returns void or bool
193template<typename T, typename = void>
194struct has_uart_init : std::false_type {};
195
196template<typename T>
197struct has_uart_init<T, std::void_t<decltype(
198 T::init(std::declval<const sbl::UartHandle&>()))>>
199 : std::true_type {};
200
201// Detect: static init_rx(const UartHandle&) — returns void or bool
202template<typename T, typename = void>
203struct has_uart_init_rx : std::false_type {};
204
205template<typename T>
206struct has_uart_init_rx<T, std::void_t<decltype(
207 T::init_rx(std::declval<const sbl::UartHandle&>()))>>
208 : std::true_type {};
209
210// Detect: static void write_byte(uint8_t)
211template<typename T, typename = void>
212struct has_uart_write_byte : std::false_type {};
213
214template<typename T>
215struct has_uart_write_byte<T, std::void_t<decltype(
216 T::write_byte(std::declval<uint8_t>()))>>
217 : std::true_type {};
218
219// Detect: static void write_string(const char*)
220template<typename T, typename = void>
221struct has_uart_write_string : std::false_type {};
222
223template<typename T>
224struct has_uart_write_string<T, std::void_t<decltype(
225 T::write_string(std::declval<const char*>()))>>
226 : std::true_type {};
227
228// Detect: static bool available()
229template<typename T, typename = void>
230struct has_uart_available : std::false_type {};
231
232template<typename T>
233struct has_uart_available<T, std::void_t<decltype(T::available())>>
234 : std::true_type {};
235
236// Detect: static uint8_t read_byte()
237template<typename T, typename = void>
238struct has_uart_read_byte : std::false_type {};
239
240template<typename T>
241struct has_uart_read_byte<T, std::void_t<decltype(T::read_byte())>>
242 : std::true_type {};
243
244// Detect: static bool try_write_byte(uint8_t) — optional
245template<typename T, typename = void>
246struct has_uart_try_write_byte : std::false_type {};
247
248template<typename T>
249struct has_uart_try_write_byte<T, std::void_t<decltype(
250 T::try_write_byte(std::declval<uint8_t>()))>>
251 : std::true_type {};
252
253// Detect: static size_t try_write_string(const char*) — optional
254template<typename T, typename = void>
255struct has_uart_try_write_string : std::false_type {};
256
257template<typename T>
258struct has_uart_try_write_string<T, std::void_t<decltype(
259 T::try_write_string(std::declval<const char*>()))>>
260 : std::true_type {};
261
262// Return type: available() must return bool
263template<typename T, typename = void>
264struct uart_available_returns_bool : std::false_type {};
265
266template<typename T>
267struct uart_available_returns_bool<T, std::enable_if_t<
268 std::is_same_v<decltype(T::available()), bool>>> : std::true_type {};
269
270// Return type: read_byte() must return uint8_t
271template<typename T, typename = void>
272struct uart_read_byte_returns_uint8 : std::false_type {};
273
274template<typename T>
275struct uart_read_byte_returns_uint8<T, std::enable_if_t<
276 std::is_same_v<decltype(T::read_byte()), uint8_t>>> : std::true_type {};
277
278// Return type: try_write_byte() must return bool (if present)
279template<typename T, typename = void>
280struct uart_try_write_byte_returns_bool : std::false_type {};
281
282template<typename T>
283struct uart_try_write_byte_returns_bool<T, std::enable_if_t<
284 std::is_same_v<decltype(T::try_write_byte(std::declval<uint8_t>())), bool>>>
285 : std::true_type {};
286
287// Detect: static void write(const uint8_t*, std::size_t)
288template<typename T, typename = void>
289struct has_uart_write : std::false_type {};
290
291template<typename T>
292struct has_uart_write<T, std::void_t<decltype(
293 T::write(std::declval<const uint8_t*>(),
294 std::declval<std::size_t>()))>>
295 : std::true_type {};
296
297/**
298 * @brief Combined UART driver interface check
299 *
300 * True if driver implements all required UART methods.
301 */
302template<typename UartDriver>
313
314/**
315 * @brief ADC Driver Method Detection
316 *
317 * Detects presence of required methods in ADC driver implementations.
318 * ADC drivers must implement:
319 * - init()
320 * - configure_channel(const AdcHandle&, SampleTime)
321 * - start_conversion(const AdcHandle&)
322 * - is_conversion_complete() -> bool
323 * - read_raw() -> uint16_t
324 * - resolution_bits() -> uint8_t (constexpr)
325 */
326
327// Detect: static init() — returns void or bool
328template<typename T, typename = void>
329struct has_adc_init : std::false_type {};
330
331template<typename T>
332struct has_adc_init<T, std::void_t<decltype(T::init())>>
333 : std::true_type {};
334
335// Detect: static void configure_channel(const AdcHandle&, SampleTime)
336template<typename T, typename = void>
337struct has_adc_configure_channel : std::false_type {};
338
339template<typename T>
340struct has_adc_configure_channel<T, std::void_t<decltype(
341 T::configure_channel(std::declval<const sbl::AdcHandle&>(),
342 std::declval<sbl::hal::adc::SampleTime>()))>>
343 : std::true_type {};
344
345// Detect: static void start_conversion(const AdcHandle&)
346template<typename T, typename = void>
347struct has_adc_start_conversion : std::false_type {};
348
349template<typename T>
350struct has_adc_start_conversion<T, std::void_t<decltype(
351 T::start_conversion(std::declval<const sbl::AdcHandle&>()))>>
352 : std::true_type {};
353
354// Detect: static bool is_conversion_complete()
355template<typename T, typename = void>
356struct has_adc_is_conversion_complete : std::false_type {};
357
358template<typename T>
359struct has_adc_is_conversion_complete<T, std::void_t<decltype(T::is_conversion_complete())>>
360 : std::true_type {};
361
362// Detect: static uint16_t read_raw()
363template<typename T, typename = void>
364struct has_adc_read_raw : std::false_type {};
365
366template<typename T>
367struct has_adc_read_raw<T, std::void_t<decltype(T::read_raw())>>
368 : std::true_type {};
369
370// Detect: static constexpr uint8_t resolution_bits()
371template<typename T, typename = void>
372struct has_adc_resolution_bits : std::false_type {};
373
374template<typename T>
375struct has_adc_resolution_bits<T, std::void_t<decltype(T::resolution_bits())>>
376 : std::true_type {};
377
378/**
379 * @brief Combined ADC driver interface check
380 *
381 * True if driver implements all required ADC methods.
382 */
383template<typename AdcDriver>
393
394// Detect: static void start_dma_scan(...) — optional
395template<typename T, typename = void>
396struct has_adc_start_dma_scan : std::false_type {};
397
398template<typename T>
399struct has_adc_start_dma_scan<T, std::void_t<decltype(
400 T::start_dma_scan(std::declval<const sbl::AdcHandle*>(),
401 std::declval<uint8_t>(),
402 std::declval<uint16_t*>(),
403 std::declval<sbl::hal::adc::SampleTime>()))>>
404 : std::true_type {};
405
406// Detect: static void stop_dma_scan() — optional
407template<typename T, typename = void>
408struct has_adc_stop_dma_scan : std::false_type {};
409
410template<typename T>
411struct has_adc_stop_dma_scan<T, std::void_t<decltype(T::stop_dma_scan())>>
412 : std::true_type {};
413
414/**
415 * @brief Audio Driver Method Detection
416 *
417 * Detects presence of required methods in Audio driver implementations.
418 * Audio drivers must implement:
419 * - init()
420 * - init(const AudioConfig&)
421 * - set_callback(AudioCallback)
422 * - start()
423 * - stop()
424 */
425
426// Detect: static void init() [no-arg]
427template<typename T, typename = void>
428struct has_audio_init : std::false_type {};
429
430template<typename T>
431struct has_audio_init<T, std::void_t<decltype(T::init())>>
432 : std::true_type {};
433
434// Detect: static void init(const AudioConfig&)
435template<typename T, typename = void>
436struct has_audio_init_config : std::false_type {};
437
438template<typename T>
439struct has_audio_init_config<T, std::void_t<decltype(
440 T::init(std::declval<const sbl::hal::audio::AudioConfig&>()))>>
441 : std::true_type {};
442
443// Detect: static void set_callback(AudioCallback)
444template<typename T, typename = void>
445struct has_audio_set_callback : std::false_type {};
446
447template<typename T>
448struct has_audio_set_callback<T, std::void_t<decltype(
449 T::set_callback(std::declval<sbl::hal::audio::AudioCallback>()))>>
450 : std::true_type {};
451
452// Detect: static void start()
453template<typename T, typename = void>
454struct has_audio_start : std::false_type {};
455
456template<typename T>
457struct has_audio_start<T, std::void_t<decltype(T::start())>>
458 : std::true_type {};
459
460// Detect: static void stop()
461template<typename T, typename = void>
462struct has_audio_stop : std::false_type {};
463
464template<typename T>
465struct has_audio_stop<T, std::void_t<decltype(T::stop())>>
466 : std::true_type {};
467
468/**
469 * @brief Combined Audio driver interface check
470 *
471 * True if driver implements all required audio methods.
472 */
473template<typename AudioDriver>
482
483} // namespace validation
484} // namespace sbl
485
486#endif // SBL_VALIDATION_METHOD_DETECTION_HPP_
ADC handle type for hardware abstraction.
GPIO handle type for hardware abstraction.
ADC common types and enumerations.
Audio common types and configuration.
GPIO common types and enumerations.
Root namespace for all Sound Byte Libs functionality.
Definition aliases.hpp:24
Combined ADC driver interface check.
Combined Audio driver interface check.
Combined GPIO driver interface check.
ADC Driver Method Detection.
Audio Driver Method Detection.
GPIO Driver Method Detection (Handle-First API)
UART Driver Method Detection.
Combined Timer driver interface check.
Combined UART driver interface check.
UART handle type for hardware abstraction.