open62541pp 0.18.0
C++ wrapper of open62541
Loading...
Searching...
No Matches
types_handling.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm> // for_each_n, transform
4#include <cassert>
5#include <cstring> // memcpy, memset
6#include <memory>
7#include <new> // bad_alloc
8
10#include "open62541pp/detail/traits.hpp" // IsOneOf
12
13namespace opcua::detail {
14
15/* ------------------------------------ Generic type handling ----------------------------------- */
16
17template <typename T>
18struct IsPointerFree
19 : IsOneOf<
20 T,
21 UA_Boolean,
22 UA_SByte,
23 UA_Byte,
24 UA_Int16,
25 UA_UInt16,
26 UA_Int32,
27 UA_UInt32,
28 UA_Int64,
29 UA_UInt64,
30 UA_Float,
31 UA_Double,
32 UA_DateTime,
33 UA_Guid,
34 UA_StatusCode> {};
35
36template <typename T>
37constexpr bool isBorrowed(const T& /* unused */) noexcept {
38 return false;
39}
40
41constexpr bool isBorrowed(const UA_Variant& native) noexcept {
42 return native.storageType == UA_VARIANT_DATA_NODELETE;
43}
44
45constexpr bool isBorrowed(const UA_DataValue& native) noexcept {
46 return native.value.storageType == UA_VARIANT_DATA_NODELETE;
47}
48
49constexpr bool isBorrowed(const UA_ExtensionObject& native) noexcept {
50 return native.encoding == UA_EXTENSIONOBJECT_DECODED_NODELETE;
51}
52
53template <typename T>
54constexpr bool isValidTypeCombination(const UA_DataType& type) {
55 if constexpr (std::is_void_v<T>) {
56 return true; // allow type-erasure
57 } else {
58 return sizeof(T) == type.memSize;
59 }
60}
61
62template <typename T>
63constexpr void clear(T& native, const UA_DataType& type) noexcept {
64 assert(isValidTypeCombination<T>(type));
65 // NOLINTNEXTLINE(bugprone-branch-clone)
66 if constexpr (IsPointerFree<T>::value) {
67 native = {};
68 } else if (isBorrowed(native)) {
69 native = {};
70 } else {
71 UA_clear(&native, &type);
72 }
73}
74
75template <typename T>
76void deallocate(T* native) noexcept {
77 UA_free(native); // NOLINT
78}
79
80template <typename T>
81void deallocate(T* native, const UA_DataType& type) noexcept {
82 assert(isValidTypeCombination<T>(type));
83 UA_delete(native, &type);
84}
85
86template <typename T>
87[[nodiscard]] T* allocate() {
88 auto* ptr = static_cast<T*>(UA_calloc(1, sizeof(T))); // NOLINT
89 if (ptr == nullptr) {
90 throw std::bad_alloc{};
91 }
92 return ptr;
93}
94
95template <typename T>
96[[nodiscard]] T* allocate(const UA_DataType& type) {
97 assert(isValidTypeCombination<T>(type));
98 auto* ptr = static_cast<T*>(UA_new(&type));
99 if (ptr == nullptr) {
100 throw std::bad_alloc{};
101 }
102 return ptr;
103}
104
105template <typename T>
106[[nodiscard]] auto allocateUniquePtr(const UA_DataType& type) {
107 auto deleter = [&type](T* native) { deallocate(native, type); };
108 return std::unique_ptr<T, decltype(deleter)>(allocate<T>(type), deleter);
109}
110
111template <typename T>
112[[nodiscard]] constexpr T copy(const T& src, const UA_DataType& type) noexcept(
113 IsPointerFree<T>::value
114) {
115 assert(isValidTypeCombination<T>(type));
116 if constexpr (!IsPointerFree<T>::value) {
117 T dst; // NOLINT, initialized in UA_copy function
118 throwIfBad(UA_copy(&src, &dst, &type));
119 return dst;
120 } else {
121 return src;
122 }
123}
124
125/* ----------------------------------- Generic array handling ----------------------------------- */
126
127/**
128 * Sentinel for empty but defined arrays.
129 *
130 * In OPC UA, arrays can have a length of zero or more with the usual meaning.
131 * In addition, arrays can be undefined. Then, they don't even have a length.
132 * In the binary encoding, this is indicated by an array of length -1.
133 *
134 * In open62541, size_t is used for array lengths.
135 * An undefined array has length 0 and the data pointer is NULL.
136 * An array of length 0 also has length 0 but a data pointer UA_EMPTY_ARRAY_SENTINEL (0x01).
137 */
138inline constexpr uintptr_t emptyArraySentinel = 0x01;
139
140template <typename T>
141T* stripEmptyArraySentinel(T* array) noexcept {
142 // NOLINTNEXTLINE
143 return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(array) & ~emptyArraySentinel);
144}
145
146template <typename T>
147void deallocateArray(T* array) noexcept {
148 UA_free(stripEmptyArraySentinel(array)); // NOLINT
149}
150
151template <typename T>
152void deallocateArray(T* array, size_t size, const UA_DataType& type) noexcept {
153 assert(isValidTypeCombination<T>(type));
154 std::for_each_n(array, size, [&](auto& item) { clear(item, type); });
155 deallocateArray(array);
156}
157
158template <typename T>
159[[nodiscard]] T* allocateArray(size_t size) {
160 if (size > UA_INT32_MAX) {
161 throw std::bad_alloc{};
162 }
163 if (size == 0) {
164 return reinterpret_cast<T*>(emptyArraySentinel); // NOLINT
165 }
166 auto* ptr = static_cast<T*>(UA_calloc(size, sizeof(T))); // NOLINT
167 if (ptr == nullptr) {
168 throw std::bad_alloc{};
169 }
170 return ptr;
171}
172
173template <typename T>
174[[nodiscard]] T* allocateArray(size_t size, [[maybe_unused]] const UA_DataType& type) {
175 assert(isValidTypeCombination<T>(type));
176 return allocateArray<T>(size);
177}
178
179template <typename T>
180[[nodiscard]] auto allocateArrayUniquePtr(size_t size, const UA_DataType& type) {
181 auto deleter = [&type, size](T* native) { deallocateArray(native, size, type); };
182 return std::unique_ptr<T, decltype(deleter)>(allocateArray<T>(size, type), deleter);
183}
184
185template <typename T>
186[[nodiscard]] T* copyArray(const T* src, size_t size) {
187 T* dst = allocateArray<T>(size);
188 if (src == nullptr) {
189 return dst;
190 }
191 std::memcpy(dst, src, size * sizeof(T));
192 return dst;
193}
194
195template <typename T>
196[[nodiscard]] T* copyArray(const T* src, size_t size, const UA_DataType& type) {
197 T* dst = allocateArray<T>(size, type);
198 if (src == nullptr) {
199 return dst;
200 }
201 if constexpr (!IsPointerFree<T>::value) {
202 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
203 std::transform(src, src + size, dst, [&](const T& item) { return copy(item, type); });
204 } else {
205 std::memcpy(dst, src, size * sizeof(T));
206 }
207 return dst;
208}
209
210template <typename T>
211void resizeArray(T*& array, size_t& size, size_t newSize, const UA_DataType& type) {
212 if (array == nullptr || newSize == size) {
213 return;
214 }
215 T* newArray = allocateArray<T>(newSize, type);
216 std::memcpy(newArray, array, std::min(size, newSize) * sizeof(T));
217 if (newSize > size) {
218 std::memset(newArray + size, 0, newSize - size); // NOLINT
219 }
220 deallocateArray<T>(array, size, type);
221 array = newArray;
222 size = newSize;
223}
224
225} // namespace opcua::detail
constexpr void throwIfBad(UA_StatusCode code)
Check the status code and throw a BadStatus exception if the status code is bad.
Definition exception.hpp:85
UA_UInt32 memSize
UA_StatusCode UA_copy(const void *src, void *dst, const UA_DataType *type)
UA_EXTENSIONOBJECT_DECODED_NODELETE
void * UA_new(const UA_DataType *type)
UA_VARIANT_DATA_NODELETE
void UA_clear(void *p, const UA_DataType *type)
void UA_delete(void *p, const UA_DataType *type)
#define UA_free
#define UA_calloc