|
12 | 12 | #include <vector> |
13 | 13 | #include <cstdint> |
14 | 14 | #include <cstddef> |
| 15 | +#include <cstring> |
| 16 | +#include <string> |
| 17 | +#include <stdexcept> |
| 18 | +#include <type_traits> |
| 19 | +#include <utility> |
15 | 20 |
|
16 | 21 |
|
17 | 22 | namespace rs2 |
@@ -372,6 +377,53 @@ namespace rs2 |
372 | 377 | return unwrap_raw_data_buffer( buffer, e ); |
373 | 378 | } |
374 | 379 |
|
| 380 | + /** |
| 381 | + * typed counterpart to get_composite_option() - casts the raw payload directly into T |
| 382 | + * instead of handing back bytes for the caller to memcpy themselves. Validates that the |
| 383 | + * device returned exactly sizeof(T) bytes, and - only for structs that carry a `version` |
| 384 | + * field, e.g. rs2_minz_control - that it isn't the "never populated" sentinel (0). |
| 385 | + * \param[in] id composite option id to read |
| 386 | + * \return T, populated from the option's current raw payload |
| 387 | + */ |
| 388 | + template< typename T > |
| 389 | + T get_composite_option_as( rs2_composite_option_id id ) const |
| 390 | + { |
| 391 | + T value{}; |
| 392 | + cast_composite_payload( get_composite_option( id ), value ); |
| 393 | + return value; |
| 394 | + } |
| 395 | + |
| 396 | + /** |
| 397 | + * typed counterpart to get_composite_option_range() - casts the raw {min,max,step,def} |
| 398 | + * payload directly into TRange (e.g. rs2_minz_control_range) instead of handing back |
| 399 | + * bytes. Same size/version validation as get_composite_option_as(). |
| 400 | + * \param[in] id composite option id to read |
| 401 | + * \return TRange, populated from the option's raw range payload |
| 402 | + */ |
| 403 | + template< typename TRange > |
| 404 | + TRange get_composite_option_range_as( rs2_composite_option_id id ) const |
| 405 | + { |
| 406 | + TRange range{}; |
| 407 | + cast_composite_payload( get_composite_option_range( id ), range ); |
| 408 | + return range; |
| 409 | + } |
| 410 | + |
| 411 | + /** |
| 412 | + * typed counterpart to set_composite_option() - sends value itself instead of a raw |
| 413 | + * pointer+size pair. For structs that carry a `version` field, rejects a value that is |
| 414 | + * still 0 (default-initialized) instead of silently sending an unpopulated wire header - |
| 415 | + * the most likely real bug being caught here is a get-modify-set that only touched one |
| 416 | + * field and left the rest, header included, zero-initialized. |
| 417 | + * \param[in] id composite option id to write |
| 418 | + * \param[in] value the caller's struct matching the option's documented wire layout |
| 419 | + */ |
| 420 | + template< typename T > |
| 421 | + void set_composite_option_from( rs2_composite_option_id id, const T & value ) const |
| 422 | + { |
| 423 | + check_version_populated( value ); |
| 424 | + set_composite_option( id, &value, sizeof( T ) ); |
| 425 | + } |
| 426 | + |
375 | 427 | /** |
376 | 428 | * check if particular composite option is supported (and currently enabled) |
377 | 429 | * \param[in] id composite option id to be checked |
@@ -476,6 +528,56 @@ namespace rs2 |
476 | 528 | return result; |
477 | 529 | } |
478 | 530 |
|
| 531 | + // ---- typed composite-option cast helpers (get_composite_option_as() and friends) ----- |
| 532 | + // |
| 533 | + // Detects whether T has a `.version` member WITHOUT requiring T to opt in explicitly - |
| 534 | + // this alone covers every composite-option struct in the SDK today: both range wrappers |
| 535 | + // (rs2_temporal_filter_dpp_range::version, rs2_minz_control_range::version) and any value |
| 536 | + // struct that embeds its own wire header (rs2_minz_control::version). A struct with no |
| 537 | + // such field (rs2_temporal_filter_dpp_config) falls through to the no-op overload below - |
| 538 | + // there is nothing to check for it. |
| 539 | + template< typename U > |
| 540 | + class has_version_member |
| 541 | + { |
| 542 | + template< typename V > static auto test( int ) -> decltype( std::declval< V >().version, std::true_type{} ); |
| 543 | + template< typename > static std::false_type test( ... ); |
| 544 | + public: |
| 545 | + static const bool value = decltype( test< U >( 0 ) )::value; |
| 546 | + }; |
| 547 | + |
| 548 | + template< typename T > |
| 549 | + static typename std::enable_if< ! has_version_member< T >::value >::type |
| 550 | + check_version_populated( const T & ) |
| 551 | + { |
| 552 | + // T has no version field (e.g. rs2_temporal_filter_dpp_config) - nothing to check. |
| 553 | + } |
| 554 | + |
| 555 | + template< typename T > |
| 556 | + static typename std::enable_if< has_version_member< T >::value >::type |
| 557 | + check_version_populated( const T & value ) |
| 558 | + { |
| 559 | + // Convention across every struct that carries this field (see rs_composite_option.h |
| 560 | + // and the per-feature headers it points to): valid versions start at 1. A 0 means |
| 561 | + // "never populated" - either get_composite_option_as() got back fewer meaningful |
| 562 | + // bytes than expected, or set_composite_option_from() is about to send a |
| 563 | + // default-initialized header the device never asked for. |
| 564 | + if( value.version == 0 ) |
| 565 | + throw std::runtime_error( "composite option struct has an unpopulated version field (0) " |
| 566 | + "- wrong struct for this option id, or a zero-initialized " |
| 567 | + "header about to be sent?" ); |
| 568 | + } |
| 569 | + |
| 570 | + template< typename T > |
| 571 | + static void cast_composite_payload( const std::vector< uint8_t > & raw, T & out ) |
| 572 | + { |
| 573 | + if( raw.size() != sizeof( T ) ) |
| 574 | + throw std::runtime_error( "composite option payload size (" + std::to_string( raw.size() ) |
| 575 | + + ") does not match sizeof(T) (" + std::to_string( sizeof( T ) ) |
| 576 | + + ") - wrong struct for this option id?" ); |
| 577 | + std::memcpy( &out, raw.data(), sizeof( T ) ); |
| 578 | + check_version_populated( out ); |
| 579 | + } |
| 580 | + |
479 | 581 | rs2_options* _options; |
480 | 582 | }; |
481 | 583 |
|
|
0 commit comments