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