open62541pp 0.19.0
C++ wrapper of open62541
Loading...
Searching...
No Matches
wrapper.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <type_traits>
4#include <utility> // exchange, move
5
6#include "open62541pp/common.hpp" // TypeIndex
9
10namespace opcua {
11
12/**
13 * @defgroup Wrapper Wrapper classes
14 *
15 * All wrapper classes inherit from Wrapper.
16 * Native open62541 objects can be accessed using the Wrapper::handle() member function.
17 *
18 * Wrapper types are pointer-interconvertible to the wrapped native type and vice versa:
19 * - Use asNative(T*) or asNative(const T*)
20 * to cast wrapper object pointers to native object pointers.
21 * - Use asNative(T&) or asNative(const T&)
22 * to cast wrapper object references to native object references.
23 * - Use asWrapper<T>(typename T::NativeType*) or asWrapper<T>(const typename T::NativeType*)
24 * to cast native object pointers to wrapper object pointers.
25 * - Use asWrapper<T>(typename T::NativeType&) or asWrapper<T>(const typename T::NativeType&)
26 * to cast native object references to wrapper object references.
27 *
28 * According to the standard:
29 * > Two objects `a` and `b` are pointer-interconvertible if:
30 * > One is a standard-layout class object [wrapper] and the other is the first non-static data
31 * > member of that object [wrapped native type].
32 * Derived classes must fulfill the requirements of standard-layout types to be convertible.
33 * @see https://en.cppreference.com/w/cpp/language/static_cast#pointer-interconvertible
34 *
35 * @{
36 */
37
38/**
39 * Default type handler providing standard copy and move operations.
40 *
41 * You can specialize it for custom types to override default behavior. For example:
42 * @code
43 * template <>
44 * struct TypeHandler<MyType> {
45 * static constexpr MyType copy(const MyType& obj) { ... }
46 * static constexpr MyType move(MyType&& obj) noexcept { ... }
47 * static constexpr void clear(MyType& obj) noexcept { ... }
48 * };
49 * @endcode
50 */
51template <typename T>
53 static_assert(std::is_copy_constructible_v<T>);
54 static_assert(std::is_nothrow_move_constructible_v<T>);
55 static_assert(std::is_nothrow_default_constructible_v<T>);
56
57 static constexpr T copy(const T& object) {
58 return object;
59 };
60
61 static constexpr T move(T&& object) noexcept {
62 return std::move(object);
63 };
64};
65
66/**
67 * Specialized type handler for native types.
68 *
69 * @tparam T Native type, e.g. @ref UA_String
70 * @tparam Index Type index of the @ref UA_TYPES array, e.g. `UA_TYPES_STRING`
71 *
72 * @see TypeHandler
73 */
74template <typename T, TypeIndex Index>
76 static_assert(Index < UA_TYPES_COUNT);
77
78 static constexpr T copy(const T& native) {
79 return detail::copy(native, UA_TYPES[Index]);
80 };
81
82 static constexpr T move(T&& native) noexcept { // NOLINT(*not-moved)
83 return std::exchange(native, {});
84 };
85
86 static constexpr void clear(T& native) noexcept {
87 detail::clear(native, UA_TYPES[Index]);
88 };
89};
90
91namespace detail {
92
93template <typename Handler, typename T, typename = void>
94struct HasClear : std::false_type {};
95
96template <typename Handler, typename T>
97struct HasClear<Handler, T, std::void_t<decltype(Handler::clear(std::declval<T&>()))>>
98 : std::true_type {};
99
100template <typename WrapperType, bool Clear>
101class WrapperDestructorMixin {};
102
103template <typename WrapperType>
104class WrapperDestructorMixin<WrapperType, true> { // NOLINT(*special-member-functions)
105public:
106 ~WrapperDestructorMixin() noexcept {
107 auto& native = *static_cast<WrapperType*>(this)->handle();
108 WrapperType::HandlerType::clear(native);
109 }
110};
111
112} // namespace detail
113
114/**
115 * Template base class to wrap (native) objects.
116 *
117 * @tparam T Type of the native object.
118 * @tparam Handler Policy type that defines object operations. It must define:
119 * - `static T copy(const T&)` to copy the wrapped object,
120 * - `static T move(T&&) noexcept` to move the wrapped object,
121 * - `static void clear(T&) noexcept` to clear the wrapped object (optional).
122 * If clear is not defined, the wrapper does not define a destructor.
123 *
124 * The default @ref TypeHandler provides standard copy/move behavior.
125 * It can be specialized for custom behavior per type.
126 * Use @ref TypeHandlerNative for native open62541 types.
127 * @ref WrapperNative is a convenience alias for Wrapper using TypeHandlerNative.
128 *
129 * @warning No virtual destructor is defined; do not implement a destructor in derived classes.
130 */
131template <typename T, typename Handler = TypeHandler<T>>
133 : public detail::WrapperDestructorMixin<
134 Wrapper<T, Handler>,
135 detail::HasClear<Handler, T>::value> {
136public:
137 static_assert(std::is_nothrow_default_constructible_v<T>);
138 static_assert(std::is_invocable_v<decltype(Handler::copy), const T&>);
139 static_assert(std::is_nothrow_invocable_v<decltype(Handler::move), T&&>);
140
141 using NativeType = T;
142 using HandlerType = Handler;
143
144 constexpr Wrapper() noexcept = default;
145
146 /// Copy constructor.
147 constexpr Wrapper(const Wrapper& other)
148 : native_{Handler::copy(other.native())} {}
149
150 /// Copy constructor with native object.
151 constexpr explicit Wrapper(const T& native)
152 : native_{Handler::copy(native)} {}
153
154 /// Move constructor.
155 constexpr Wrapper(Wrapper&& other) noexcept
156 : native_{Handler::move(std::move(other.native()))} {}
157
158 /// Move constructor with native object.
159 constexpr Wrapper(T&& native) noexcept // NOLINT(*explicit-conversions)
160 : native_{Handler::move(std::move(native))} {}
161
162 ~Wrapper() noexcept = default;
163
164 /// Copy assignment.
165 constexpr Wrapper& operator=(const Wrapper& other) {
166 if (this != &other) {
167 clear();
168 this->native() = Handler::copy(other.native());
169 }
170 return *this;
171 }
172
173 /// Copy assignment with native object.
174 constexpr Wrapper& operator=(const T& native) {
175 if (&this->native() != &native) {
176 clear();
177 this->native() = Handler::copy(native);
178 }
179 return *this;
180 }
181
182 /// Move assignment.
183 constexpr Wrapper& operator=(Wrapper&& other) noexcept {
184 if (this != &other) {
185 clear();
186 this->native() = Handler::move(std::move(other.native()));
187 }
188 return *this;
189 }
190
191 /// Move assignment with native object.
192 constexpr Wrapper& operator=(T&& native) noexcept {
193 if (&this->native() != &native) {
194 clear();
195 this->native() = Handler::move(std::move(native));
196 }
197 return *this;
198 }
199
200 /// Implicit conversion to native object.
201 constexpr operator T&() noexcept { // NOLINT(*explicit-conversions)
202 return native_;
203 }
204
205 /// Implicit conversion to native object.
206 constexpr operator const T&() const noexcept { // NOLINT(*explicit-conversions)
207 return native_;
208 }
209
210 /// Member access to native object.
211 constexpr T* operator->() noexcept {
212 return &native_;
213 }
214
215 /// Member access to native object.
216 constexpr const T* operator->() const noexcept {
217 return &native_;
218 }
219
220 /// Return pointer to native object.
221 constexpr T* handle() noexcept {
222 return &native_;
223 }
224
225 /// Return pointer to native object.
226 constexpr const T* handle() const noexcept {
227 return &native_;
228 }
229
230 /// Swap with wrapper object.
231 constexpr void swap(Wrapper& other) noexcept {
232 using std::swap;
233 swap(this->native(), other.native());
234 }
235
236 /// Swap with native object.
237 constexpr void swap(T& native) noexcept {
238 using std::swap;
239 swap(this->native(), native);
240 }
241
242protected:
243 constexpr const T& native() const noexcept {
244 return native_;
245 }
246
247 constexpr T& native() noexcept {
248 return native_;
249 }
250
251 constexpr void clear() noexcept {
252 if constexpr (detail::HasClear<Handler, T>::value) {
253 Handler::clear(native_);
254 } else {
255 native_ = {};
256 }
257 }
258
259private:
260 T native_{};
261};
262
263/**
264 * Convenience alias for Wrapper using TypeHandlerNative.
265 * @see Wrapper
266 */
267template <typename T, TypeIndex Index>
269
270template <typename T, TypeIndex Index>
271class [[deprecated("use WrapperNative instead")]] TypeWrapper : public WrapperNative<T, Index> {
272public:
273 using WrapperNative<T, Index>::WrapperNative;
274 using WrapperNative<T, Index>::operator=;
275
276 static constexpr TypeIndex typeIndex() {
277 return Index;
278 }
279};
280
281/* -------------------------------------------- Trait ------------------------------------------- */
282
283template <typename T>
284struct IsWrapper {
285 // https://stackoverflow.com/a/51910887
286 template <typename U, typename Handler>
287 static std::true_type check(const Wrapper<U, Handler>&);
288 static std::false_type check(...);
289
290 using type = decltype(check(std::declval<T&>())); // NOLINT
291 static constexpr bool value = type::value;
292};
293
294/* ------------------------------ Cast native type to wrapper type ------------------------------ */
295
296namespace detail {
297
298template <typename T>
299using IsPointerInterconvertibleWrapper = std::conjunction<
301 std::is_standard_layout<T>,
302 std::is_standard_layout<typename T::NativeType>,
303 std::bool_constant<sizeof(T) == sizeof(typename T::NativeType)>>;
304
305} // namespace detail
306
307// NOLINTBEGIN(*casting-through-void)
308
309/// Cast native object pointers to Wrapper object pointers.
310/// @relatesalso Wrapper
311template <typename T>
312constexpr T* asWrapper(typename T::NativeType* native) noexcept {
313 static_assert(detail::IsPointerInterconvertibleWrapper<T>::value);
314 return static_cast<T*>(static_cast<void*>(native));
315}
316
317/// @copydoc asWrapper(typename T::NativeType*)
318/// @relatesalso Wrapper
319template <typename T>
320constexpr const T* asWrapper(const typename T::NativeType* native) noexcept {
321 static_assert(detail::IsPointerInterconvertibleWrapper<T>::value);
322 return static_cast<const T*>(static_cast<const void*>(native));
323}
324
325/// Cast native object references to Wrapper object references.
326/// @relatesalso Wrapper
327template <typename T>
328constexpr T& asWrapper(typename T::NativeType& native) noexcept {
329 return *asWrapper<T>(&native);
330}
331
332/// @copydoc asWrapper(typename T::NativeType&)
333/// @relatesalso Wrapper
334template <typename T>
335constexpr const T& asWrapper(const typename T::NativeType& native) noexcept {
336 return *asWrapper<T>(&native);
337}
338
339/// Cast Wrapper object pointers to native object pointers.
340/// @relatesalso Wrapper
341template <typename T>
342constexpr typename T::NativeType* asNative(T* wrapper) noexcept {
343 static_assert(detail::IsPointerInterconvertibleWrapper<T>::value);
344 return static_cast<typename T::NativeType*>(static_cast<void*>(wrapper));
345}
346
347/// @copydoc asNative(T*)
348/// @relatesalso Wrapper
349template <typename T>
350constexpr const typename T::NativeType* asNative(const T* wrapper) noexcept {
351 static_assert(detail::IsPointerInterconvertibleWrapper<T>::value);
352 return static_cast<const typename T::NativeType*>(static_cast<const void*>(wrapper));
353}
354
355/// Cast Wrapper object references to native object references.
356/// @relatesalso Wrapper
357template <typename T>
358constexpr typename T::NativeType& asNative(T& wrapper) noexcept {
359 return *asNative(&wrapper);
360}
361
362/// @copydoc asNative(T&)
363/// @relatesalso Wrapper
364template <typename T>
365constexpr const typename T::NativeType& asNative(const T& wrapper) noexcept {
366 return *asNative(&wrapper);
367}
368
369// NOLINTEND(*casting-through-void)
370
371/* --------------------------------- TypeRegistry specialization -------------------------------- */
372
373template <typename T>
375 T,
376 std::enable_if_t<IsWrapper<T>::value && IsRegistered<typename T::NativeType>::value>> {
377 static const UA_DataType& getDataType() noexcept {
379 }
380};
381
382/**
383 * @}
384 */
385
386} // namespace opcua
static constexpr TypeIndex typeIndex()
Definition wrapper.hpp:276
Template base class to wrap (native) objects.
Definition wrapper.hpp:135
constexpr Wrapper & operator=(T &&native) noexcept
Move assignment with native object.
Definition wrapper.hpp:192
constexpr Wrapper & operator=(const T &native)
Copy assignment with native object.
Definition wrapper.hpp:174
~Wrapper() noexcept=default
constexpr T * handle() noexcept
Return pointer to native object.
Definition wrapper.hpp:221
constexpr Wrapper(T &&native) noexcept
Move constructor with native object.
Definition wrapper.hpp:159
constexpr const T & native() const noexcept
Definition wrapper.hpp:243
constexpr Wrapper() noexcept=default
constexpr void clear() noexcept
Definition wrapper.hpp:251
constexpr Wrapper(const T &native)
Copy constructor with native object.
Definition wrapper.hpp:151
constexpr const T * handle() const noexcept
Return pointer to native object.
Definition wrapper.hpp:226
Handler HandlerType
Definition wrapper.hpp:142
constexpr Wrapper(Wrapper &&other) noexcept
Move constructor.
Definition wrapper.hpp:155
constexpr void swap(T &native) noexcept
Swap with native object.
Definition wrapper.hpp:237
constexpr void swap(Wrapper &other) noexcept
Swap with wrapper object.
Definition wrapper.hpp:231
constexpr Wrapper & operator=(Wrapper &&other) noexcept
Move assignment.
Definition wrapper.hpp:183
constexpr T * operator->() noexcept
Member access to native object.
Definition wrapper.hpp:211
constexpr T & native() noexcept
Definition wrapper.hpp:247
constexpr const T * operator->() const noexcept
Member access to native object.
Definition wrapper.hpp:216
constexpr T & asWrapper(typename T::NativeType &native) noexcept
Cast native object references to Wrapper object references.
Definition wrapper.hpp:328
constexpr const T * asWrapper(const typename T::NativeType *native) noexcept
Cast native object pointers to Wrapper object pointers.
Definition wrapper.hpp:320
constexpr const T & asWrapper(const typename T::NativeType &native) noexcept
Cast native object references to Wrapper object references.
Definition wrapper.hpp:335
constexpr T::NativeType * asNative(T *wrapper) noexcept
Cast Wrapper object pointers to native object pointers.
Definition wrapper.hpp:342
constexpr T * asWrapper(typename T::NativeType *native) noexcept
Cast native object pointers to Wrapper object pointers.
Definition wrapper.hpp:312
constexpr const T::NativeType * asNative(const T *wrapper) noexcept
Cast Wrapper object pointers to native object pointers.
Definition wrapper.hpp:350
constexpr T::NativeType & asNative(T &wrapper) noexcept
Cast Wrapper object references to native object references.
Definition wrapper.hpp:358
constexpr const T::NativeType & asNative(const T &wrapper) noexcept
Cast Wrapper object references to native object references.
Definition wrapper.hpp:365
uint16_t TypeIndex
Type index of the UA_TYPES array.
Definition common.hpp:21
decltype(check(std::declval< T & >())) type
Definition wrapper.hpp:290
static std::false_type check(...)
static constexpr bool value
Definition wrapper.hpp:291
static std::true_type check(const Wrapper< U, Handler > &)
Specialized type handler for native types.
Definition wrapper.hpp:75
static constexpr T copy(const T &native)
Definition wrapper.hpp:78
static constexpr void clear(T &native) noexcept
Definition wrapper.hpp:86
static constexpr T move(T &&native) noexcept
Definition wrapper.hpp:82
Default type handler providing standard copy and move operations.
Definition wrapper.hpp:52
static constexpr T copy(const T &object)
Definition wrapper.hpp:57
static constexpr T move(T &&object) noexcept
Definition wrapper.hpp:61
const UA_DataType UA_TYPES[191]
#define UA_TYPES_COUNT