open62541pp 0.19.0
C++ wrapper of open62541
Loading...
Searching...
No Matches
datatype.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm> // transform
4#include <cassert>
5#include <cstdint>
6#include <iterator> // prev
7#include <string_view>
8#include <type_traits>
9#include <utility> // move
10#include <vector>
11
12#include "open62541pp/common.hpp" // TypeIndex
13#include "open62541pp/config.hpp"
17#include "open62541pp/span.hpp"
18#include "open62541pp/typeregistry.hpp" // getDataType
19#include "open62541pp/types.hpp" // NodeId
21
22namespace opcua {
23
24template <>
27 static UA_DataTypeMember move(UA_DataTypeMember&& native) noexcept;
28 static void clear(UA_DataTypeMember& native) noexcept;
29};
30
31template <>
33 static UA_DataType copy(const UA_DataType& native);
34 static UA_DataType move(UA_DataType&& native) noexcept;
35 static void clear(UA_DataType& native) noexcept;
36};
37
38class DataTypeMember : public Wrapper<UA_DataTypeMember> {
39public:
40 using Wrapper::Wrapper;
41
42 std::string_view memberName() const noexcept {
43#if UAPP_HAS_TYPEDESCRIPTION
44 return handle()->memberName;
45#else
46 return {};
47#endif
48 }
49
50 void setMemberName([[maybe_unused]] std::string_view memberName) {
51#if UAPP_HAS_TYPEDESCRIPTION
52 detail::clear(handle()->memberName);
53 handle()->memberName = detail::allocCString(memberName);
54#endif
55 }
56
57 const UA_DataType* memberType() const noexcept {
58#if UAPP_OPEN62541_VER_GE(1, 3)
59 return handle()->memberType;
60#else
61 return nullptr;
62#endif
63 }
64
66#if UAPP_OPEN62541_VER_GE(1, 3)
68#else
69 if (memberType != nullptr) {
70 handle()->memberTypeIndex = memberType->typeIndex;
71 handle()->namespaceZero = memberType->typeId.namespaceIndex == 0;
72 }
73#endif
74 }
75
76 uint8_t padding() const noexcept {
77 return handle()->padding;
78 }
79
80 void setPadding(uint8_t padding) {
82 }
83
84 bool isArray() const noexcept {
85 return handle()->isArray;
86 }
87
88 void setIsArray(bool isArray) {
90 }
91
92 bool isOptional() const noexcept {
93#if UAPP_OPEN62541_VER_GE(1, 1)
94 return handle()->isOptional;
95#else
96 return false;
97#endif
98 }
99
100 void setIsOptional([[maybe_unused]] bool isOptional) {
101#if UAPP_OPEN62541_VER_GE(1, 1)
103#endif
104 }
105};
106
107/**
108 * UA_DataType wrapper class.
109 * @ingroup Wrapper
110 */
111class DataType : public Wrapper<UA_DataType> {
112public:
113 using Wrapper::Wrapper;
114
115 std::string_view typeName() const noexcept {
116#if UAPP_HAS_TYPEDESCRIPTION
117 return handle()->typeName;
118#else
119 return {};
120#endif
121 }
122
123 void setTypeName([[maybe_unused]] std::string_view typeName) noexcept {
124#if UAPP_HAS_TYPEDESCRIPTION
125 detail::clear(handle()->typeName);
126 handle()->typeName = detail::allocCString(typeName);
127#endif
128 }
129
130 NodeId typeId() const noexcept {
131 return NodeId{handle()->typeId}; // NOLINT
132 }
133
135 asWrapper<NodeId>(handle()->typeId) = std::move(typeId);
136 }
137
138 NodeId binaryEncodingId() const noexcept {
139#if UAPP_OPEN62541_VER_GE(1, 2)
140 return NodeId{handle()->binaryEncodingId}; // NOLINT
141#else
142 return NodeId{handle()->typeId.namespaceIndex, handle()->binaryEncodingId}; // NOLINT
143#endif
144 }
145
147#if UAPP_OPEN62541_VER_GE(1, 2)
148 asWrapper<NodeId>(handle()->binaryEncodingId) = std::move(binaryEncodingId);
149#else
151#endif
152 }
153
154 uint16_t memSize() const noexcept {
155 return handle()->memSize;
156 }
157
158 void setMemSize(uint16_t memSize) noexcept {
160 }
161
162 uint8_t typeKind() const noexcept {
163 return handle()->typeKind;
164 }
165
166 void setTypeKind(uint8_t typeKind) noexcept {
168 }
169
170 bool pointerFree() const noexcept {
171 return handle()->pointerFree;
172 }
173
174 void setPointerFree(bool pointerFree) noexcept {
176 }
177
178 bool overlayable() const noexcept {
179 return handle()->overlayable;
180 }
181
182 void setOverlayable(bool overlayable) noexcept {
184 }
185
187 return {asWrapper<DataTypeMember>(handle()->members), handle()->membersSize};
188 }
189
191};
192
193/// @relates DataType
194inline bool operator==(const UA_DataType& lhs, const UA_DataType& rhs) noexcept {
195 return lhs.typeId == rhs.typeId;
196}
197
198/// @relates DataType
199inline bool operator!=(const UA_DataType& lhs, const UA_DataType& rhs) noexcept {
200 return !(lhs == rhs);
201}
202
203/// @relates DataType
204inline bool operator==(const UA_DataTypeMember& lhs, const UA_DataTypeMember& rhs) noexcept {
205#if UAPP_OPEN62541_VER_GE(1, 3)
206 if (lhs.memberType == nullptr || rhs.memberType == nullptr) {
207 return false;
208 }
209 return (lhs.memberType == rhs.memberType) || (*lhs.memberType == *rhs.memberType);
210#else
211 return lhs.memberTypeIndex == rhs.memberTypeIndex;
212#endif
213}
214
215/// @relates DataType
216inline bool operator!=(const UA_DataTypeMember& lhs, const UA_DataTypeMember& rhs) noexcept {
217 return !(lhs == rhs);
218}
219
220/* --------------------------------------- DataTypeBuilder -------------------------------------- */
221
222namespace detail {
223
224template <auto memberPtr>
225const UA_DataType& getMemberDataType() {
226 using TMember = detail::MemberTypeT<decltype(memberPtr)>;
227 return getDataType<std::remove_pointer_t<TMember>>();
228}
229
230// https://gist.github.com/graphitemaster/494f21190bb2c63c5516
231template <typename T, typename TMember>
232size_t offsetOfMember(TMember T::*member) {
233 static T object{};
234 return size_t(&(object.*member)) - size_t(&object);
235}
236
237struct TagDataTypeAny;
238struct TagDataTypeEnum;
239struct TagDataTypeStruct;
240struct TagDataTypeUnion;
241
242} // namespace detail
243
244/**
245 * Builder to create DataType definitions of custom types.
246 *
247 * The attributes `memSize`, `padding`, `pointerFree`, `isArray` and `isOptional` are automatically
248 * deduced from the types itself.
249 */
250template <typename T, typename Tag = detail::TagDataTypeAny, typename U = struct DeferT>
252public:
253 /**
254 * Build a DataType definition for an enum.
255 * @param typeName Human-readable type name
256 * @param typeId NodeId of the type
257 * @param binaryEncodingId NodeId of data type when encoded as binary
258 */
259 static auto createEnum(std::string_view typeName, NodeId typeId, NodeId binaryEncodingId);
260
261 /**
262 * Build a DataType definition for a structure.
263 * A structure may have optional fields (pointers).
264 * @param typeName Human-readable type name
265 * @param typeId NodeId of the type
266 * @param binaryEncodingId NodeId of data type when encoded as binary
267 */
268 static auto createStructure(std::string_view typeName, NodeId typeId, NodeId binaryEncodingId);
269
270 /**
271 * Build a DataType definition for an union.
272 * Union type consist of a switch field and the actual union.
273 * @param typeName Human-readable type name
274 * @param typeId NodeId of the type
275 * @param binaryEncodingId NodeId of data type when encoded as binary
276 */
277 static auto createUnion(std::string_view typeName, NodeId typeId, NodeId binaryEncodingId);
278
279 /**
280 * Add a structure field.
281 * The offset is derived from the member pointer.
282 * @tparam field Member pointer, e.g. `&S::value`
283 * @param fieldName Human-readable field name
284 * @param fieldType Member data type
285 */
286 template <auto U::*field>
287 auto& addField(std::string_view fieldName, const UA_DataType& fieldType) {
288 using TMember = detail::MemberTypeT<decltype(field)>;
289 return addField<TMember>(fieldName, detail::offsetOfMember(field), fieldType);
290 }
291
292 /**
293 * Add a structure field (derive DataType from `field`).
294 * @overload
295 */
296 template <auto U::*field>
297 auto& addField(std::string_view fieldName) {
298 return addField<field>(fieldName, detail::getMemberDataType<field>());
299 }
300
301 /**
302 * Add a structure field with a manual offset.
303 * @tparam TMember Type of the member, e.g. `opcua::String`
304 * @param fieldName Human-readable field name
305 * @param offset Offset of the member in the structure
306 * @param fieldType Member data type
307 */
308 template <typename TMember>
309 auto& addField(std::string_view fieldName, size_t offset, const UA_DataType& fieldType);
310
311 /**
312 * Add a structure field with a manual offset (derive DataType from `TMember`).
313 * @overload
314 */
315 template <typename TMember>
316 auto& addField(std::string_view fieldName, size_t offset) {
317 return addField<TMember>(fieldName, offset, getDataType<std::remove_pointer_t<TMember>>());
318 }
319
320 /**
321 * Add a structure array field.
322 * Arrays must consists of two fields: its size (of type `size_t`) and the pointer to the data.
323 * No padding allowed between the size field and the array field.
324 * The offset is derived from the member pointer.
325 * @tparam fieldSize Member pointer to the size field, e.g. `&S::length`
326 * @tparam fieldArray Member pointer to the array field, e.g. `&S::data`
327 * @param fieldName Human-readable field name
328 * @param fieldType Member data type
329 */
330 template <size_t U::*fieldSize, auto U::*fieldArray>
331 auto& addField(std::string_view fieldName, const UA_DataType& fieldType) {
332 using TArray = detail::MemberTypeT<decltype(fieldArray)>;
333 return addField<TArray>(
334 fieldName,
335 detail::offsetOfMember(fieldSize),
336 detail::offsetOfMember(fieldArray),
337 fieldType
338 );
339 }
340
341 /**
342 * Add a structure array field (derive DataType from `fieldArray`).
343 * @overload
344 */
345 template <size_t U::*fieldSize, auto U::*fieldArray>
346 auto& addField(std::string_view fieldName) {
347 return addField<fieldSize, fieldArray>(fieldName, detail::getMemberDataType<fieldArray>());
348 }
349
350 /**
351 * Add a structure array field with a manual offset.
352 * @tparam TArray Type of the array field, e.g. `opcua::String`
353 * @param fieldName Human-readable field name
354 * @param offsetSize Offset of the size field in the structure
355 * @param offsetArray Offset of the array field in the structure
356 * @param fieldType Array field data type
357 */
358 template <typename TArray>
359 auto& addField(
360 std::string_view fieldName,
361 size_t offsetSize,
362 size_t offsetArray,
363 const UA_DataType& fieldType
364 );
365
366 /**
367 * Add a structure array field with a manual offset (derive DataType from `TArray`).
368 * @overload
369 */
370 template <typename TArray>
371 auto& addField(std::string_view fieldName, size_t offsetSize, size_t offsetArray) {
372 return addField<TArray>(
373 fieldName, offsetSize, offsetArray, getDataType<std::remove_pointer_t<TArray>>()
374 );
375 }
376
377 /**
378 * Add a union field.
379 * @tparam memberUnion Member pointer to the union, e.g. `&S::Uni`
380 * @tparam TField Type of the union field
381 * @param fieldName Human-readable field name
382 * @param fieldType Data type of the union field
383 */
384 template <auto U::*memberUnion, typename TField>
385 auto& addUnionField(std::string_view fieldName, const UA_DataType& fieldType);
386
387 /**
388 * Add a union field (derive DataType from `TField`).
389 * @overload
390 */
391 template <auto U::*memberUnion, typename TField>
392 auto& addUnionField(std::string_view fieldName) {
393 return addUnionField<memberUnion, TField>(fieldName, getDataType<TField>());
394 }
395
396 /**
397 * Create the actual DataType.
398 */
399 [[nodiscard]] DataType build();
400
401private:
402 template <typename, typename, typename>
403 friend class DataTypeBuilder;
404
405 explicit DataTypeBuilder(DataType dataType)
406 : dataType_{std::move(dataType)} {}
407
408 struct Field {
409 size_t memSize{};
410 size_t offset{};
411 DataTypeMember dataTypeMember;
412 };
413
414 DataType dataType_;
415 std::vector<Field> fields_;
416};
417
418/* --------------------------------------- Implementation --------------------------------------- */
419
420template <typename T, typename Tag, typename U>
422 std::string_view typeName, NodeId typeId, NodeId binaryEncodingId
423) {
424 static_assert(std::is_enum_v<T>, "T must be an enum");
425 DataType dt;
426 dt.setTypeName(typeName);
427 dt.setTypeId(std::move(typeId));
428 dt.setBinaryEncodingId(std::move(binaryEncodingId));
429 dt.setMemSize(sizeof(T)); // enums must be 32 bit!
431 dt.setPointerFree(true);
432 dt.setOverlayable(false);
434}
435
436template <typename T, typename Tag, typename U>
438 std::string_view typeName, NodeId typeId, NodeId binaryEncodingId
439) {
440 static_assert(std::is_class_v<T>, "T must be a struct or class");
441 DataType dt;
442 dt.setTypeName(typeName);
443 dt.setTypeId(std::move(typeId));
444 dt.setBinaryEncodingId(std::move(binaryEncodingId));
445 dt.setMemSize(sizeof(T));
447 dt.setPointerFree(true);
448 dt.setOverlayable(false);
450}
451
452template <typename T, typename Tag, typename U>
454 std::string_view typeName, NodeId typeId, NodeId binaryEncodingId
455) {
456 static_assert(std::is_class_v<T>, "T must be a struct or class");
457 DataType dt;
458 dt.setTypeName(typeName);
459 dt.setTypeId(std::move(typeId));
460 dt.setBinaryEncodingId(std::move(binaryEncodingId));
461 dt.setMemSize(sizeof(T));
463 dt.setPointerFree(true);
464 dt.setOverlayable(false);
466}
467
468template <typename T, typename Tag, typename U>
469template <typename TMember>
471 std::string_view fieldName, size_t offset, const UA_DataType& fieldType
472) {
473 static_assert(
474 std::is_same_v<Tag, detail::TagDataTypeStruct>,
475 "Built type must be a struct or class to add members"
476 );
477 assert(sizeof(std::remove_pointer_t<TMember>) == fieldType.memSize);
478 if (std::is_pointer_v<TMember>) {
479 dataType_.setTypeKind(UA_DATATYPEKIND_OPTSTRUCT);
480 }
481 if (std::is_pointer_v<TMember> || !fieldType.pointerFree) {
482 dataType_.setPointerFree(false);
483 }
484 DataTypeMember member;
485 member.setMemberName(fieldName);
486 member.setMemberType(&fieldType);
487 member.setPadding({}); // calculate padding between members later
488 member.setIsArray(false);
489 member.setIsOptional(std::is_pointer_v<TMember>);
490 fields_.push_back({sizeof(TMember), offset, std::move(member)});
491 return *this;
492}
493
494template <typename T, typename Tag, typename U>
495template <typename TArray>
497 const std::string_view fieldName,
498 size_t offsetSize,
499 [[maybe_unused]] size_t offsetArray,
500 const UA_DataType& fieldType
501) {
502 static_assert(
503 std::is_same_v<Tag, detail::TagDataTypeStruct>,
504 "Built type must be a struct or class to add members"
505 );
506 static_assert(std::is_pointer_v<TArray>, "TArray must be a pointer");
507 assert(
508 offsetArray == offsetSize + sizeof(size_t) &&
509 "No padding between members size and array allowed"
510 );
511 assert(sizeof(std::remove_pointer_t<TArray>) == fieldType.memSize);
512 dataType_.setPointerFree(false);
513 DataTypeMember member;
514 member.setMemberName(fieldName);
515 member.setMemberType(&fieldType);
516 member.setPadding({}); // calculate padding between members later
517 member.setIsArray(true);
518 member.setIsOptional(false);
519 fields_.push_back({
520 sizeof(size_t) + sizeof(TArray),
521 offsetSize, // offset/padding related to size field
522 std::move(member),
523 });
524 return *this;
525}
526
527template <typename T, typename Tag, typename U>
528template <auto U::*memberUnion, typename TField>
530 std::string_view fieldName, const UA_DataType& fieldType
531) {
532 using TUnion = detail::MemberTypeT<decltype(memberUnion)>;
533 static_assert(
534 std::is_same_v<Tag, detail::TagDataTypeUnion>,
535 "Built type must be a union to add union fields"
536 );
537 static_assert(std::is_union_v<TUnion>, "TUnion must be a union");
538 static_assert(sizeof(TField) <= sizeof(TUnion), "TField exceeds size of union");
539 const auto offset = detail::offsetOfMember(memberUnion);
540 assert(offset > 0 && "A union type must consist of a switch field and a union");
541 assert(sizeof(std::remove_pointer_t<TField>) == fieldType.memSize);
542 if (std::is_pointer_v<TField> || !fieldType.pointerFree) {
543 dataType_.setPointerFree(false);
544 }
545 DataTypeMember member;
546 member.setMemberName(fieldName);
547 member.setMemberType(&fieldType);
548 member.setPadding(static_cast<uint8_t>(offset)); // padding = offset of each field
549 member.setIsArray(false);
550 member.setIsOptional(std::is_pointer_v<TField>);
551 fields_.push_back({sizeof(TField), offset, std::move(member)});
552 return *this;
553}
554
555template <typename T, typename Tag, typename U>
557 static_assert(!std::is_same_v<Tag, detail::TagDataTypeAny>);
558 // sort members by offset
559 std::sort(fields_.begin(), fields_.end(), [](const auto& lhs, const auto& rhs) {
560 return lhs.offset < rhs.offset;
561 });
562 // calculate padding of struct members
563 if constexpr (std::is_same_v<Tag, detail::TagDataTypeStruct>) {
564 for (auto it = fields_.begin(); it < fields_.end(); ++it) {
565 if (it == fields_.begin()) {
566 it->dataTypeMember->padding = static_cast<uint8_t>(it->offset);
567 } else {
568 it->dataTypeMember->padding = static_cast<uint8_t>(
569 it->offset - (std::prev(it)->offset + std::prev(it)->memSize)
570 );
571 }
572 }
573 }
574 // generate and set members array
575 std::vector<UA_DataTypeMember> members(fields_.size());
576 std::transform(fields_.cbegin(), fields_.cend(), members.begin(), [](const auto& m) {
577 return asNative(m.dataTypeMember); // shallow copy
578 });
579 dataType_.setMembers({asWrapper<DataTypeMember>(members.data()), members.size()});
580 return dataType_;
581}
582
583/* ------------------------------------------- Helper ------------------------------------------- */
584
585namespace detail {
586
587void clear(UA_DataTypeMember& native) noexcept;
588void clear(UA_DataType& native) noexcept;
589void clear(UA_DataTypeArray& native) noexcept;
590
591void deallocate(const UA_DataTypeArray* head) noexcept;
592
593[[nodiscard]] UA_DataTypeMember copy(const UA_DataTypeMember& src);
594[[nodiscard]] UA_DataType copy(const UA_DataType& src);
595
596void addDataTypes(const UA_DataTypeArray*& head, Span<const DataType> types);
597
598} // namespace detail
599
600} // namespace opcua
Builder to create DataType definitions of custom types.
Definition datatype.hpp:251
DataType build()
Create the actual DataType.
Definition datatype.hpp:556
auto & addUnionField(std::string_view fieldName, const UA_DataType &fieldType)
Add a union field.
Definition datatype.hpp:529
auto & addField(std::string_view fieldName, const UA_DataType &fieldType)
Add a structure array field.
Definition datatype.hpp:331
auto & addField(std::string_view fieldName)
Add a structure field (derive DataType from field).
Definition datatype.hpp:297
static auto createStructure(std::string_view typeName, NodeId typeId, NodeId binaryEncodingId)
Build a DataType definition for a structure.
Definition datatype.hpp:437
auto & addField(std::string_view fieldName, size_t offset)
Add a structure field with a manual offset (derive DataType from TMember).
Definition datatype.hpp:316
static auto createUnion(std::string_view typeName, NodeId typeId, NodeId binaryEncodingId)
Build a DataType definition for an union.
Definition datatype.hpp:453
auto & addUnionField(std::string_view fieldName)
Add a union field (derive DataType from TField).
Definition datatype.hpp:392
auto & addField(std::string_view fieldName, size_t offsetSize, size_t offsetArray)
Add a structure array field with a manual offset (derive DataType from TArray).
Definition datatype.hpp:371
auto & addField(std::string_view fieldName)
Add a structure array field (derive DataType from fieldArray).
Definition datatype.hpp:346
auto & addField(std::string_view fieldName, const UA_DataType &fieldType)
Add a structure field.
Definition datatype.hpp:287
static auto createEnum(std::string_view typeName, NodeId typeId, NodeId binaryEncodingId)
Build a DataType definition for an enum.
Definition datatype.hpp:421
friend class DataTypeBuilder
Definition datatype.hpp:403
void setIsArray(bool isArray)
Definition datatype.hpp:88
bool isOptional() const noexcept
Definition datatype.hpp:92
void setPadding(uint8_t padding)
Definition datatype.hpp:80
void setMemberType(const UA_DataType *memberType)
Definition datatype.hpp:65
uint8_t padding() const noexcept
Definition datatype.hpp:76
bool isArray() const noexcept
Definition datatype.hpp:84
void setMemberName(std::string_view memberName)
Definition datatype.hpp:50
void setIsOptional(bool isOptional)
Definition datatype.hpp:100
const UA_DataType * memberType() const noexcept
Definition datatype.hpp:57
std::string_view memberName() const noexcept
Definition datatype.hpp:42
UA_DataType wrapper class.
Definition datatype.hpp:111
bool operator!=(const UA_DataTypeMember &lhs, const UA_DataTypeMember &rhs) noexcept
Definition datatype.hpp:216
bool operator!=(const UA_DataType &lhs, const UA_DataType &rhs) noexcept
Definition datatype.hpp:199
std::string_view typeName() const noexcept
Definition datatype.hpp:115
Span< const DataTypeMember > members() const noexcept
Definition datatype.hpp:186
bool pointerFree() const noexcept
Definition datatype.hpp:170
NodeId binaryEncodingId() const noexcept
Definition datatype.hpp:138
uint8_t typeKind() const noexcept
Definition datatype.hpp:162
bool overlayable() const noexcept
Definition datatype.hpp:178
void setTypeKind(uint8_t typeKind) noexcept
Definition datatype.hpp:166
uint16_t memSize() const noexcept
Definition datatype.hpp:154
NodeId typeId() const noexcept
Definition datatype.hpp:130
void setTypeId(NodeId typeId)
Definition datatype.hpp:134
void setOverlayable(bool overlayable) noexcept
Definition datatype.hpp:182
void setMembers(Span< const DataTypeMember > members)
void setMemSize(uint16_t memSize) noexcept
Definition datatype.hpp:158
void setPointerFree(bool pointerFree) noexcept
Definition datatype.hpp:174
void setTypeName(std::string_view typeName) noexcept
Definition datatype.hpp:123
void setBinaryEncodingId(NodeId binaryEncodingId)
Definition datatype.hpp:146
bool operator==(const UA_DataType &lhs, const UA_DataType &rhs) noexcept
Definition datatype.hpp:194
bool operator==(const UA_DataTypeMember &lhs, const UA_DataTypeMember &rhs) noexcept
Definition datatype.hpp:204
UA_NodeId wrapper class.
Definition types.hpp:641
T & identifier()
Get identifier reference.
Definition types.hpp:765
View to a contiguous sequence of objects, similar to std::span in C++20.
Definition span.hpp:29
Template base class to wrap (native) objects.
Definition wrapper.hpp:135
constexpr UA_DataTypeMember * handle() noexcept
Return pointer to native object.
Definition wrapper.hpp:221
constexpr Wrapper() noexcept=default
const UA_DataType & getDataType() noexcept
const UA_DataType * memberType
const char * memberName
UA_UInt32 overlayable
UA_UInt32 typeKind
const char * typeName
UA_UInt32 pointerFree
UA_UInt32 memSize
UA_NodeId typeId
UA_UInt32 membersSize
UA_NodeId binaryEncodingId
UA_UInt16 namespaceIndex
static void clear(UA_DataTypeMember &native) noexcept
static UA_DataTypeMember move(UA_DataTypeMember &&native) noexcept
static UA_DataTypeMember copy(const UA_DataTypeMember &native)
static void clear(UA_DataType &native) noexcept
static UA_DataType copy(const UA_DataType &native)
static UA_DataType move(UA_DataType &&native) noexcept
Default type handler providing standard copy and move operations.
Definition wrapper.hpp:52
UA_DATATYPEKIND_ENUM
UA_DATATYPEKIND_UNION
UA_DATATYPEKIND_OPTSTRUCT
UA_DATATYPEKIND_STRUCTURE