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