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