Skip to content

Commit 3c782db

Browse files
author
Evgeni Raikhel
committed
Add rs2::option_ref()/option_handle/composite_option, mirroring rs2::frame's is<T>()/as<T>()
Make options first-class-castable in C++, the same way frames already are: - rs2::options gains option_ref(rs2_option) - returns a lightweight, non-owning option_handle identifying a (this options object, id) pair. Named option_ref, not get_option_handle, to stay clearly distinct from the internal, differently- typed options_container::get_option_handler(). Available on both rs2::sensor and rs2::embedded_filter automatically, since both already inherit rs2::options. - rs2::option_handle: is<T>()/as<T>() construct a T from *this and rely on its converting constructor to flag validity via operator bool(), never throwing - the exact same pattern as rs2::video_frame(const rs2::frame&). - rs2::composite_option: the cast target. Its converting constructor calls the new rs2_is_option_extendable_to(RS2_EXTENSION_COMPOSITE_OPTION) check (guarded by a never-throwing rs2_supports_option() first, since - unlike the frame/sensor extension checks, which only ever risk a dynamic_cast - looking up an unregistered option id throws). get()/get_range() unwrap the SDK-allocated rs2_raw_data_buffer the same way rs2::safety_sensor::get_safety_preset does; set() takes a caller-owned buffer; is_read_only()/is_enabled()/get_description() delegate straight through (pure metadata, no value marshaling).
1 parent 121c3c1 commit 3c782db

2 files changed

Lines changed: 165 additions & 37 deletions

File tree

include/librealsense2/hpp/rs_composite_option.hpp

Lines changed: 145 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,66 +7,174 @@
77
#ifndef LIBREALSENSE_RS2_COMPOSITE_OPTION_HPP
88
#define LIBREALSENSE_RS2_COMPOSITE_OPTION_HPP
99

10-
#include "rs_sensor.hpp"
10+
#include "rs_options.hpp"
1111
#include "../h/rs_composite_option.h"
1212

13+
#include <memory>
14+
#include <vector>
15+
#include <cstdint>
16+
1317
namespace rs2
1418
{
15-
// Generic "composite option" extension: any sensor that supports AT LEAST ONE composite
16-
// (multi-field, atomically-exchanged) option extends to this - see is<T>()/as<T>() in
17-
// rs_types.hpp, backed by rs2_is_sensor_extendable_to(RS2_EXTENSION_COMPOSITE_OPTIONS).
18-
// There is one wrapper class for every composite option, not one per feature: which option
19-
// you are getting/setting is selected by the rs2_composite_option_id argument.
20-
class composite_option_sensor : public sensor
19+
// Lightweight, non-owning reference to a single (options-object, rs2_option id) pair -
20+
// obtained via options::option_ref(). Mirrors rs2::frame's is<T>()/as<T>() dispatch
21+
// (rs_frame.hpp / rs2_is_frame_extendable_to) exactly, just for options instead of frames:
22+
// is<T>() / as<T>() construct a T from *this and rely on T's converting constructor to flag
23+
// validity (see composite_option below), rather than throwing.
24+
//
25+
// Does NOT own the underlying rs2_options* - it shares the lifetime of whichever
26+
// sensor/embedded_filter produced it via options::option_ref(); the caller must keep that
27+
// object alive for as long as this handle (or anything cast from it) is used.
28+
class option_handle
29+
{
30+
public:
31+
option_handle() = default;
32+
33+
option_handle( std::shared_ptr< rs2_options > options, rs2_option id )
34+
: _options( std::move( options ) )
35+
, _id( id )
36+
{
37+
}
38+
39+
template< class T >
40+
bool is() const
41+
{
42+
T extension( *this );
43+
return extension;
44+
}
45+
46+
template< class T >
47+
T as() const
48+
{
49+
T extension( *this );
50+
return extension;
51+
}
52+
53+
rs2_option get_option_id() const { return _id; }
54+
55+
operator bool() const { return _valid && _options != nullptr; }
56+
57+
protected:
58+
std::shared_ptr< rs2_options > _options;
59+
rs2_option _id = RS2_OPTION_COUNT;
60+
bool _valid = true;
61+
};
62+
63+
// Generic "composite option" extension: any option that is a multi-field, atomically
64+
// exchanged composite control (see rs_composite_option.h) extends to this - one wrapper
65+
// class shared by every such option, not one per feature. Which option this refers to is
66+
// carried by the underlying option_handle's id - obtain one via options::option_ref(id).
67+
class composite_option : public option_handle
2168
{
2269
public:
23-
composite_option_sensor(sensor s)
24-
: sensor(s.get())
70+
// Mirrors rs2::video_frame(const rs2::frame&) exactly (see rs_frame.hpp): checks the
71+
// extension via the C API and marks itself invalid (never throws) on mismatch. Guarded
72+
// by rs2_supports_option() first (a plain, never-throwing lookup) because - unlike
73+
// rs2_is_sensor_extendable_to/rs2_is_frame_extendable_to, which only ever risk a
74+
// dynamic_cast - rs2_is_option_extendable_to looks up the option by id first, which
75+
// throws (surfaced as an rs2_error) if this id isn't registered on the container at all;
76+
// that's a legitimate "not this option" case here, not an error to propagate.
77+
composite_option( const option_handle & h )
78+
: option_handle( h )
2579
{
26-
rs2_error* e = nullptr;
27-
if (rs2_is_sensor_extendable_to(_sensor.get(), RS2_EXTENSION_COMPOSITE_OPTIONS, &e) == 0 && !e)
80+
rs2_error * e = nullptr;
81+
bool extendable = _options && rs2_supports_option( _options.get(), _id, &e ) > 0;
82+
error::handle( e );
83+
if( extendable )
2884
{
29-
_sensor.reset();
85+
extendable = rs2_is_option_extendable_to( _options.get(), _id, RS2_EXTENSION_COMPOSITE_OPTION, &e ) > 0;
86+
error::handle( e );
3087
}
31-
error::handle(e);
88+
if( ! extendable )
89+
_valid = false;
3290
}
3391

34-
operator bool() const { return _sensor.get() != nullptr; }
35-
36-
// Single atomic UVC transaction (one set_xu round trip) - all fields of *data sent together.
37-
void set_composite_option(rs2_composite_option_id option_id, const void* data, unsigned int data_size) const
92+
// Single atomic UVC transaction (one set_xu round trip) - all fields of *data sent
93+
// together. No ownership transfer - data remains caller-owned, like options::set_option.
94+
void set( const void * data, size_t size ) const
3895
{
39-
rs2_error* e = nullptr;
40-
rs2_set_composite_option(_sensor.get(), option_id, data, data_size, &e);
41-
error::handle(e);
96+
rs2_error * e = nullptr;
97+
rs2_set_composite_option( _options.get(), _id, data, static_cast< unsigned int >( size ), &e );
98+
error::handle( e );
4299
}
43100

44101
// Single atomic UVC transaction (one get_xu round trip). The SDK allocates the result
45-
// (the caller has no generic way to know option_id's wire size in advance) and this
46-
// wrapper hides the raw rs2_raw_data_buffer/manual-free entirely, returning a plain
47-
// std::vector<uint8_t> of the option's raw payload bytes. The SDK ships no typed
48-
// struct for this - cast/memcpy the bytes into your own local struct matching the
49-
// layout documented on the corresponding rs2_composite_option_id enumerator (see
50-
// rs_composite_option.h).
51-
std::vector<uint8_t> get_composite_option(rs2_composite_option_id option_id) const
102+
// (the caller has no generic way to know this option's wire size in advance) and this
103+
// wrapper hides the raw rs2_raw_data_buffer/manual-free entirely (mirrors
104+
// rs2::safety_sensor::get_safety_preset's exact unwrap pattern), returning a plain
105+
// std::vector<uint8_t> of the option's current raw payload bytes.
106+
std::vector< uint8_t > get() const
107+
{
108+
rs2_error * e = nullptr;
109+
auto buffer = rs2_get_composite_option( _options.get(), _id, &e );
110+
return unwrap( buffer, e );
111+
}
112+
113+
// Same unwrap pattern as get(), for the option's supported {min,max,step,def} - see
114+
// rs_hkr_temporal_filter_dpp.h for RS2_OPTION_HKR_TEMPORAL_FILTER_DPP's range struct.
115+
std::vector< uint8_t > get_range() const
116+
{
117+
rs2_error * e = nullptr;
118+
auto buffer = rs2_get_composite_option_range( _options.get(), _id, &e );
119+
return unwrap( buffer, e );
120+
}
121+
122+
// Full functional parity with plain rs2::option-style metadata queries - pure
123+
// metadata/no value-marshaling, so these just delegate through normally.
124+
bool is_read_only() const
125+
{
126+
rs2_error * e = nullptr;
127+
auto res = rs2_is_option_read_only( _options.get(), _id, &e );
128+
error::handle( e );
129+
return res > 0;
130+
}
131+
132+
// Reflects real, current runtime availability (delegates to rs2_supports_option) - this
133+
// is NOT the mechanism used to block scalar rs2_get_option/rs2_set_option access on a
134+
// composite option (that's a hard-blocked, always-throws operation on the underlying
135+
// option implementation - see librealsense::composite_xu_option).
136+
bool is_enabled() const
52137
{
53-
rs2_error* e = nullptr;
54-
auto buffer = rs2_get_composite_option(_sensor.get(), option_id, &e);
138+
rs2_error * e = nullptr;
139+
auto res = rs2_supports_option( _options.get(), _id, &e );
140+
error::handle( e );
141+
return res > 0;
142+
}
55143

56-
std::shared_ptr<const rs2_raw_data_buffer> list(buffer, rs2_delete_raw_data);
57-
error::handle(e);
144+
const char * get_description() const
145+
{
146+
rs2_error * e = nullptr;
147+
auto res = rs2_get_option_description( _options.get(), _id, &e );
148+
error::handle( e );
149+
return res;
150+
}
58151

59-
auto size = rs2_get_raw_data_size(list.get(), &e);
60-
error::handle(e);
152+
private:
153+
static std::vector< uint8_t > unwrap( const rs2_raw_data_buffer * buffer, rs2_error * e )
154+
{
155+
std::shared_ptr< const rs2_raw_data_buffer > list( buffer, rs2_delete_raw_data );
156+
error::handle( e );
61157

62-
auto start = rs2_get_raw_data(list.get(), &e);
63-
error::handle(e);
158+
rs2_error * e2 = nullptr;
159+
auto size = rs2_get_raw_data_size( list.get(), &e2 );
160+
error::handle( e2 );
64161

65-
std::vector<uint8_t> result;
66-
result.insert(result.begin(), start, start + size);
162+
auto start = rs2_get_raw_data( list.get(), &e2 );
163+
error::handle( e2 );
67164

165+
std::vector< uint8_t > result;
166+
result.insert( result.begin(), start, start + size );
68167
return result;
69168
}
70169
};
170+
171+
inline option_handle options::option_ref( rs2_option id ) const
172+
{
173+
// Non-owning: no-op deleter. The real object (sensor/embedded_filter) that _options
174+
// aliases owns its own lifetime elsewhere - this handle just shares it, exactly as
175+
// rs2::options itself only ever holds a raw, non-owning rs2_options* alias (see
176+
// options::_options / options(rs2_options*) above).
177+
return option_handle( std::shared_ptr< rs2_options >( _options, []( rs2_options * ) {} ), id );
178+
}
71179
}
72180
#endif // LIBREALSENSE_RS2_COMPOSITE_OPTION_HPP

include/librealsense2/hpp/rs_options.hpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@
1212

1313
namespace rs2
1414
{
15+
// Defined in rs_composite_option.hpp (included later by rs.hpp, after this header) - a
16+
// lightweight, non-owning (options-object, rs2_option id) reference, obtained via
17+
// options::option_ref() below and cast with is<T>()/as<T>() the same way rs2::frame is cast
18+
// to rs2::video_frame etc. Forward-declared here so options::option_ref() can be declared
19+
// (its out-of-line definition lives in rs_composite_option.hpp, once option_handle is fully
20+
// defined).
21+
class option_handle;
22+
1523
class option_value
1624
{
1725
std::shared_ptr< const rs2_option_value > _value;
@@ -320,6 +328,18 @@ namespace rs2
320328
return options_list( sptr );
321329
};
322330

331+
/**
332+
* Obtain a lightweight, non-owning handle identifying a single (this options object, id)
333+
* pair - shares the lifetime of whichever sensor/embedded_filter this options object came
334+
* from (does NOT take ownership of, or duplicate, the underlying handle). Cast it with
335+
* is<T>()/as<T>() to a more specific type - e.g. is<composite_option>()/as<composite_option>()
336+
* - the same way rs2::frame is cast to rs2::video_frame etc. See rs_composite_option.hpp.
337+
* Named option_ref (not get_option_handle) to stay clearly distinct from the internal,
338+
* same-ish-sounding options_container::get_option_handler().
339+
* \param[in] id option id to obtain a handle for
340+
*/
341+
option_handle option_ref( rs2_option id ) const;
342+
323343
options& operator=(const options& other)
324344
{
325345
_options = other._options;

0 commit comments

Comments
 (0)