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