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