open62541pp 0.18.0
C++ wrapper of open62541
Loading...
Searching...
No Matches
types.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm> // copy
4#include <array>
5#include <cassert>
6#include <chrono>
7#include <cstdint>
8#include <functional> // hash
9#include <initializer_list>
10#include <iosfwd> // forward declare ostream
11#include <iterator> // reverse_iterator
12#include <optional>
13#include <ratio>
14#include <string>
15#include <string_view>
16#include <type_traits> // is_same_v
17#include <utility> // move
18#include <variant>
19#include <vector>
20
21#include "open62541pp/common.hpp" // NamespaceIndex
22#include "open62541pp/config.hpp"
23#include "open62541pp/detail/iterator.hpp" // TransformIterator
25#include "open62541pp/detail/string_utils.hpp" // allocNativeString
29#include "open62541pp/span.hpp"
34
35namespace opcua {
36
37/* ----------------------------------------- StatusCode ----------------------------------------- */
38
39/**
40 * UA_StatusCode wrapper class.
41 * StatusCode can be used interchangeably with @ref UA_StatusCode due to implicit conversions
42 * (without any overhead) but provides some methods to simplify the handling with status codes.
43 * @see statuscodes.h
44 * @see https://reference.opcfoundation.org/Core/Part4/v105/docs/7.39
45 * @ingroup Wrapper
46 */
47class StatusCode : public Wrapper<UA_StatusCode> {
48public:
49 /// Create a StatusCode with the default status code `UA_STATUSCODE_GOOD`.
50 constexpr StatusCode() noexcept = default;
51
52 constexpr StatusCode(UA_StatusCode code) noexcept // NOLINT(hicpp-explicit-conversions)
53 : Wrapper(code) {}
54
55 /// Explicitly get underlying UA_StatusCode.
56 constexpr UA_StatusCode get() const noexcept {
57 return native();
58 }
59
60 /// Get human-readable name of the StatusCode.
61 /// This feature might be disabled to create a smaller binary with the
62 /// `UA_ENABLE_STATUSCODE_DESCRIPTIONS` build-flag. Then the function returns an empty string
63 /// for every StatusCode.
64 std::string_view name() const noexcept {
65 return {UA_StatusCode_name(native())};
66 }
67
68 /// Check if the status code is good.
69 constexpr bool isGood() const noexcept {
70 return detail::isGood(native());
71 }
72
73 /// Check if the status code is uncertain.
74 constexpr bool isUncertain() const noexcept {
75 return detail::isUncertain(native());
76 }
77
78 /// Check if the status code is bad.
79 constexpr bool isBad() const noexcept {
80 return detail::isBad(native());
81 }
82
83 /// Throw a BadStatus exception if the status code is bad.
84 /// @exception BadStatus
85 constexpr void throwIfBad() const {
87 }
88};
89
90/* --------------------------------------- StringLikeMixin -------------------------------------- */
91
92namespace detail {
93
94/**
95 * CRTP mixin class to provide a STL-compatible string interface.
96 */
97template <typename WrapperType, typename CharT>
98class StringLikeMixin {
99public:
100 // clang-format off
101 using value_type = CharT;
102 using size_type = size_t;
103 using difference_type = std::ptrdiff_t;
104 using pointer = value_type*;
105 using const_pointer = const value_type*;
106 using reference = value_type&;
107 using const_reference = const value_type&;
108 using iterator = pointer;
109 using const_iterator = const_pointer;
110 using reverse_iterator = std::reverse_iterator<iterator>;
111 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
112 // clang-format on
113
114 size_t size() const noexcept {
115 const auto& native = asNative(static_cast<const WrapperType&>(*this));
116 return native.length;
117 }
118
119 size_t length() const noexcept {
120 return size();
121 }
122
123 bool empty() const noexcept {
124 return size() == 0;
125 }
126
127 pointer data() noexcept {
128 auto& native = asNative(static_cast<WrapperType&>(*this));
129 return reinterpret_cast<pointer>(native.data); // NOLINT
130 }
131
132 const_pointer data() const noexcept {
133 const auto& native = asNative(static_cast<const WrapperType&>(*this));
134 return reinterpret_cast<const_pointer>(native.data); // NOLINT
135 }
136
137 reference operator[](size_t index) noexcept {
138 assert(index < size());
139 return data()[index];
140 }
141
142 const_reference operator[](size_t index) const noexcept {
143 assert(index < size());
144 return data()[index];
145 }
146
147 reference front() noexcept {
148 assert(!empty());
149 return *data();
150 }
151
152 const_reference front() const noexcept {
153 assert(!empty());
154 return *data();
155 }
156
157 reference back() noexcept {
158 assert(!empty());
159 return *(data() + size() - 1);
160 }
161
162 const_reference back() const noexcept {
163 assert(!empty());
164 return *(data() + size() - 1);
165 }
166
167 iterator begin() noexcept {
168 return {data()};
169 }
170
171 const_iterator begin() const noexcept {
172 return {data()};
173 }
174
175 const_iterator cbegin() const noexcept {
176 return {data()};
177 }
178
179 iterator end() noexcept {
180 return {data() + size()};
181 }
182
183 const_iterator end() const noexcept {
184 return {data() + size()};
185 }
186
187 const_iterator cend() const noexcept {
188 return {data() + size()};
189 }
190
191 reverse_iterator rbegin() noexcept {
192 return reverse_iterator(end());
193 }
194
195 const_reverse_iterator rbegin() const noexcept {
196 return const_reverse_iterator(end());
197 }
198
199 const_reverse_iterator crbegin() const noexcept {
200 return const_reverse_iterator(cend());
201 }
202
203 reverse_iterator rend() noexcept {
204 return reverse_iterator(begin());
205 }
206
207 const_reverse_iterator rend() const noexcept {
208 return const_reverse_iterator(begin());
209 }
210
211 const_reverse_iterator crend() const noexcept {
212 return const_reverse_iterator(cbegin());
213 }
214
215protected:
216 void init(size_t length) {
217 auto& native = asNative(static_cast<WrapperType&>(*this));
218 native.length = length;
219 if (length > 0) {
220 native.data = static_cast<uint8_t*>(UA_malloc(length)); // NOLINT
221 if (data() == nullptr) {
222 throw BadStatus(UA_STATUSCODE_BADOUTOFMEMORY);
223 }
224 }
225 }
226
227 template <typename InputIt>
228 void init(InputIt first, InputIt last) {
229 init(first, last, typename std::iterator_traits<InputIt>::iterator_category{});
230 }
231
232 template <typename InputIt, typename Tag>
233 void init(InputIt first, InputIt last, Tag /* unused */) {
234 init(std::distance(first, last));
235 std::copy(first, last, data());
236 }
237
238 template <typename InputIt>
239 void init(InputIt first, InputIt last, std::input_iterator_tag /* unused */) {
240 // input iterator can only be read once -> buffer data in vector
241 std::vector<uint8_t> buffer(first, last);
242 init(buffer.size());
243 std::copy(buffer.begin(), buffer.end(), data());
244 }
245};
246
247} // namespace detail
248
249/* ------------------------------------------- String ------------------------------------------- */
250
251/**
252 * UA_String wrapper class.
253 * @ingroup Wrapper
254 */
256 : public TypeWrapper<UA_String, UA_TYPES_STRING>,
257 public detail::StringLikeMixin<String, char> {
258public:
260
261 explicit String(std::string_view str)
262 : TypeWrapper(detail::allocNativeString(str)) {}
263
264 template <typename InputIt>
265 String(InputIt first, InputIt last) {
266 init(first, last);
267 }
268
269 String(std::initializer_list<char> values) {
270 init(values.begin(), values.end());
271 }
272
273 /// Assign null-termined character string.
274 String& operator=(const char* str) {
275 *this = String(str);
276 return *this;
277 }
278
279 /// Assign std::string_view.
280 template <typename Traits>
281 String& operator=(std::basic_string_view<char, Traits> str) {
282 *this = String(str);
283 return *this;
284 }
285
286 /// Implicit conversion to std::string_view.
287 template <typename Traits>
288 operator std::basic_string_view<char, Traits>() const noexcept { // NOLINT(*-conversions)
289 return {data(), size()};
290 }
291
292 /// @deprecated Use conversion with `static_cast` instead
293 [[deprecated("use conversion with static_cast instead")]]
294 std::string_view get() const noexcept {
295 return {data(), size()};
296 }
297};
298
299/// @relates String
300inline bool operator==(const UA_String& lhs, const UA_String& rhs) noexcept {
301 return UA_String_equal(&lhs, &rhs);
302}
303
304/// @relates String
305inline bool operator!=(const UA_String& lhs, const UA_String& rhs) noexcept {
306 return !(lhs == rhs);
307}
308
309/// @relates String
310inline bool operator==(const String& lhs, const String& rhs) noexcept {
311 return asNative(lhs) == asNative(rhs);
312}
313
314/// @relates String
315inline bool operator!=(const String& lhs, const String& rhs) noexcept {
316 return !(lhs == rhs);
317}
318
319/// @relates String
320inline bool operator==(const String& lhs, std::string_view rhs) noexcept {
321 return static_cast<std::string_view>(lhs) == rhs;
322}
323
324/// @relates String
325inline bool operator!=(const String& lhs, std::string_view rhs) noexcept {
326 return static_cast<std::string_view>(lhs) != rhs;
327}
328
329/// @relates String
330inline bool operator==(std::string_view lhs, const String& rhs) noexcept {
331 return lhs == static_cast<std::string_view>(rhs);
332}
333
334/// @relates String
335inline bool operator!=(std::string_view lhs, const String& rhs) noexcept {
336 return lhs != static_cast<std::string_view>(rhs);
337}
338
339/// @relates String
340std::ostream& operator<<(std::ostream& os, const String& str);
341
342template <typename Traits>
343struct TypeConverter<std::basic_string_view<char, Traits>> {
344 using Type = std::basic_string_view<char, Traits>;
346
347 static void fromNative(const NativeType& src, Type& dst) {
348 dst = Type{src.data(), src.size()};
349 }
350
351 static void toNative(Type src, NativeType& dst) {
352 dst = NativeType{src};
353 }
354};
355
356template <typename Traits, typename Allocator>
357struct TypeConverter<std::basic_string<char, Traits, Allocator>> {
358 using Type = std::basic_string<char, Traits, Allocator>;
360
361 static void fromNative(const NativeType& src, Type& dst) {
362 dst = Type{src.data(), src.size()};
363 }
364
365 static void toNative(const Type& src, NativeType& dst) {
366 dst = NativeType{src};
367 }
368};
369
370template <>
371struct TypeConverter<const char*> {
372 using Type = const char*;
374
375 static void toNative(Type src, NativeType& dst) {
376 dst = NativeType{src};
377 }
378};
379
380template <size_t N>
381struct TypeConverter<char[N]> { // NOLINT
382 using Type = char[N]; // NOLINT
384
385 static void toNative(const Type& src, NativeType& dst) {
386 dst = NativeType{std::string_view{static_cast<const char*>(src), N}};
387 }
388};
389
390/* ------------------------------------------ DateTime ------------------------------------------ */
391
392/**
393 * UA_DateTime wrapper class.
394 *
395 * An instance in time. A DateTime value is encoded as a 64-bit signed integer which represents the
396 * number of 100 nanosecond intervals since January 1, 1601 (UTC).
397 *
398 * @see https://reference.opcfoundation.org/Core/Part6/v105/docs/5.2.2.5
399 * @ingroup Wrapper
400 */
401class DateTime : public TypeWrapper<UA_DateTime, UA_TYPES_DATETIME> {
402public:
403 using DefaultClock = std::chrono::system_clock;
404 using UaDuration = std::chrono::duration<int64_t, std::ratio<1, 10'000'000>>;
405
407
408 template <typename Clock, typename Duration>
409 DateTime(std::chrono::time_point<Clock, Duration> timePoint) // NOLINT(*-explicit-conversions)
410 : DateTime(fromTimePoint(timePoint)) {}
411
412 /// Get current DateTime.
413 static DateTime now() noexcept {
414 return DateTime(UA_DateTime_now()); // NOLINT
415 }
416
417 /// Get DateTime from std::chrono::time_point.
418 template <typename Clock, typename Duration>
419 static DateTime fromTimePoint(std::chrono::time_point<Clock, Duration> timePoint) {
420 return DateTime(
421 int64_t{UA_DATETIME_UNIX_EPOCH} +
422 std::chrono::duration_cast<UaDuration>(timePoint.time_since_epoch()).count()
423 );
424 }
425
426 /// Get DateTime from Unix time.
427 static DateTime fromUnixTime(int64_t unixTime) noexcept {
428 return DateTime(UA_DateTime_fromUnixTime(unixTime)); // NOLINT
429 }
430
431 /// Offset of local time to UTC.
432 static int64_t localTimeUtcOffset() noexcept {
434 }
435
436 /// Convert to std::chrono::time_point.
437 template <typename Clock = DefaultClock, typename Duration = UaDuration>
438 std::chrono::time_point<Clock, Duration> toTimePoint() const {
439 const std::chrono::time_point<Clock, Duration> unixEpoch{};
440 if (get() < UA_DATETIME_UNIX_EPOCH) {
441 return unixEpoch;
442 }
443 const auto sinceEpoch = UaDuration(get() - UA_DATETIME_UNIX_EPOCH);
444 return unixEpoch + std::chrono::duration_cast<Duration>(sinceEpoch);
445 }
446
447 /// Convert to Unix time (number of seconds since January 1, 1970 UTC).
448 int64_t toUnixTime() const noexcept {
449 if (get() < UA_DATETIME_UNIX_EPOCH) {
450 return 0;
451 }
452 return UA_DateTime_toUnixTime(get());
453 }
454
455 /// Convert to UA_DateTimeStruct.
456 UA_DateTimeStruct toStruct() const noexcept {
457 return UA_DateTime_toStruct(get());
458 }
459
460 /// Get DateTime value as 100 nanosecond intervals since January 1, 1601 (UTC).
461 int64_t get() const noexcept {
462 return *handle();
463 }
464
465 /// Convert to string with given format (same format codes as strftime).
466 /// @see https://en.cppreference.com/w/cpp/chrono/c/strftime
467 std::string format(std::string_view format, bool localtime = false) const;
468};
469
470template <typename Clock, typename Duration>
471struct TypeConverter<std::chrono::time_point<Clock, Duration>> {
472 using Type = std::chrono::time_point<Clock, Duration>;
474
475 static void fromNative(const NativeType& src, Type& dst) {
476 dst = src.toTimePoint<Clock, Duration>();
477 }
478
479 static void toNative(const Type& src, NativeType& dst) {
480 dst = DateTime::fromTimePoint(src);
481 }
482};
483
484/* -------------------------------------------- Guid -------------------------------------------- */
485
486/**
487 * UA_Guid wrapper class.
488 * @see https://reference.opcfoundation.org/Core/Part6/v105/docs/5.1.3
489 * @ingroup Wrapper
490 */
491class Guid : public TypeWrapper<UA_Guid, UA_TYPES_GUID> {
492public:
494
495 explicit Guid(std::array<uint8_t, 16> data) noexcept
496 : Guid(UA_Guid{
497 // NOLINTBEGIN(hicpp-signed-bitwise)
498 static_cast<uint32_t>(
499 (data[0] << 24U) | (data[1] << 16U) | (data[2] << 8U) | data[3]
500 ),
501 static_cast<uint16_t>((data[4] << 8U) | data[5]),
502 static_cast<uint16_t>((data[6] << 8U) | data[7]),
503 // NOLINTEND(hicpp-signed-bitwise)
504 {data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15]},
505 }) {}
506
507 Guid(uint32_t data1, uint16_t data2, uint16_t data3, std::array<uint8_t, 8> data4) noexcept
508 : Guid(UA_Guid{
509 data1,
510 data2,
511 data3,
512 {data4[0], data4[1], data4[2], data4[3], data4[4], data4[5], data4[6], data4[7]},
513 }) {}
514
515 /// Generate random Guid.
516 static Guid random() noexcept {
517 return Guid(UA_Guid_random()); // NOLINT
518 }
519
520#if UAPP_HAS_PARSING
521 /// Parse Guid from its string representation.
522 /// Format: `C496578A-0DFE-4B8F-870A-745238C6AEAE`.
523 static Guid parse(std::string_view str) {
524 Guid guid;
525 throwIfBad(UA_Guid_parse(guid.handle(), detail::toNativeString(str)));
526 return guid;
527 }
528#endif
529
530 /// @deprecated Use free function opcua::toString(const T&) instead
531 [[deprecated("use free function toString instead")]]
532 std::string toString() const;
533};
534
535/// @relates Guid
536inline bool operator==(const UA_Guid& lhs, const UA_Guid& rhs) noexcept {
537 return UA_Guid_equal(&lhs, &rhs);
538}
539
540/// @relates Guid
541inline bool operator!=(const UA_Guid& lhs, const UA_Guid& rhs) noexcept {
542 return !(lhs == rhs);
543}
544
545/* ----------------------------------------- ByteString ----------------------------------------- */
546
547/**
548 * UA_ByteString wrapper class.
549 * @ingroup Wrapper
550 */
552 : public TypeWrapper<UA_ByteString, UA_TYPES_BYTESTRING>,
553 public detail::StringLikeMixin<ByteString, uint8_t> {
554public:
556
557 explicit ByteString(std::string_view str)
558 : TypeWrapper(detail::allocNativeString(str)) {}
559
560 explicit ByteString(const char* str) // required to avoid ambiguity
561 : ByteString(std::string_view(str)) {}
562
564 init(bytes.begin(), bytes.end());
565 }
566
567 template <typename InputIt>
568 ByteString(InputIt first, InputIt last) {
569 init(first, last);
570 }
571
572 /// Parse ByteString from Base64 encoded string.
573 /// @note Supported since open62541 v1.1
574 static ByteString fromBase64(std::string_view encoded);
575
576 /// Explicit conversion to std::string_view.
577 template <typename Traits>
578 explicit operator std::basic_string_view<char, Traits>() const noexcept {
579 return {reinterpret_cast<const char*>(data()), size()}; // NOLINT
580 }
581
582 /// Convert to Base64 encoded string.
583 /// @note Supported since open62541 v1.1
585
586 /// @deprecated Use conversion with `static_cast` instead
587 [[deprecated("use conversion with static_cast instead")]]
588 std::string_view get() const noexcept {
589 return {reinterpret_cast<const char*>(data()), size()}; // NOLINT
590 }
591};
592
593/* ----------------------------------------- XmlElement ----------------------------------------- */
594
595/**
596 * UA_XmlElement wrapper class.
597 * @ingroup Wrapper
598 */
600 : public TypeWrapper<UA_XmlElement, UA_TYPES_XMLELEMENT>,
601 public detail::StringLikeMixin<XmlElement, char> {
602public:
604
605 explicit XmlElement(std::string_view str)
606 : TypeWrapper(detail::allocNativeString(str)) {}
607
608 template <typename InputIt>
609 XmlElement(InputIt first, InputIt last) {
610 init(first, last);
611 }
612
613 /// Assign null-termined character string.
614 XmlElement& operator=(const char* str) {
615 *this = XmlElement(str);
616 return *this;
617 }
618
619 /// Assign std::string_view.
620 template <typename Traits>
621 XmlElement& operator=(std::basic_string_view<char, Traits> str) {
622 *this = XmlElement(str);
623 return *this;
624 }
625
626 /// Implicit conversion to std::string_view.
627 template <typename Traits>
628 operator std::basic_string_view<char, Traits>() const noexcept { // NOLINT(*-conversions)
629 return {data(), size()};
630 }
631
632 /// @deprecated Use conversion with `static_cast` instead
633 [[deprecated("use conversion with static_cast instead")]]
634 std::string_view get() const noexcept {
635 return {data(), size()};
636 }
637};
638
639/* ------------------------------------------- NodeId ------------------------------------------- */
640
641namespace detail {
642template <typename T, typename = void>
643struct IsNodeIdEnum : std::false_type {};
644
645template <typename T>
646struct IsNodeIdEnum<T, std::void_t<decltype(namespaceOf(std::declval<T>()))>> : std::is_enum<T> {};
647} // namespace detail
648
649/**
650 * NodeId types.
651 * @see UA_NodeIdType
652 */
659
660/**
661 * UA_NodeId wrapper class.
662 * @see https://reference.opcfoundation.org/Core/Part3/v105/docs/8.2
663 * @ingroup Wrapper
664 */
665class NodeId : public TypeWrapper<UA_NodeId, UA_TYPES_NODEID> {
666public:
668
669 /// Create NodeId with numeric identifier.
671 handle()->namespaceIndex = namespaceIndex;
672 handle()->identifierType = UA_NODEIDTYPE_NUMERIC;
673 handle()->identifier.numeric = identifier; // NOLINT
674 }
675
676 /// Create NodeId with String identifier.
678 handle()->namespaceIndex = namespaceIndex;
679 handle()->identifierType = UA_NODEIDTYPE_STRING;
680 handle()->identifier.string = detail::allocNativeString(identifier); // NOLINT
681 }
682
683 /// Create NodeId with Guid identifier.
685 handle()->namespaceIndex = namespaceIndex;
686 handle()->identifierType = UA_NODEIDTYPE_GUID;
687 handle()->identifier.guid = identifier; // NOLINT
688 }
689
690 /// Create NodeId with ByteString identifier.
692 handle()->namespaceIndex = namespaceIndex;
693 handle()->identifierType = UA_NODEIDTYPE_BYTESTRING;
694 // force zero-initialization, only first member of identifier union is zero-initialized
695 handle()->identifier.byteString = {}; // NOLINT
696 asWrapper<ByteString>(handle()->identifier.byteString) = std::move(identifier); // NOLINT
697 }
698
699 /// Create NodeId from enum class with numeric identifiers like `opcua::ObjectId`.
700 /// The namespace is retrieved by calling e.g. `namespaceOf(opcua::ObjectId)`.
701 /// Make sure to provide an overload for custom enum types.
702 template <typename T, typename = std::enable_if_t<detail::IsNodeIdEnum<T>::value>>
703 NodeId(T identifier) noexcept // NOLINT(hicpp-explicit-conversions)
704 : NodeId(namespaceOf(identifier).index, static_cast<uint32_t>(identifier)) {}
705
706#if UAPP_HAS_PARSING
707 /// Parse NodeId from its string representation.
708 /// Format: `ns=<namespaceindex>;<type>=<value>`, e.g. `i=13` or `ns=10;s=HelloWorld`
709 /// @see https://reference.opcfoundation.org/Core/Part6/v104/docs/5.3.1.10
710 static NodeId parse(std::string_view str) {
711 NodeId id;
712 throwIfBad(UA_NodeId_parse(id.handle(), detail::toNativeString(str)));
713 return id;
714 }
715#endif
716
717 bool isNull() const noexcept {
718 return UA_NodeId_isNull(handle());
719 }
720
721 uint32_t hash() const noexcept {
722 return UA_NodeId_hash(handle());
723 }
724
726 return handle()->namespaceIndex;
727 }
728
729 /// @deprecated Use namespaceIndex() instead
730 [[deprecated("use namespaceIndex() instead")]]
732 return namespaceIndex();
733 }
734
735 NodeIdType identifierType() const noexcept {
736 return static_cast<NodeIdType>(handle()->identifierType);
737 }
738
739 /// @deprecated Use identifierType() instead
740 [[deprecated("use identifierType() instead")]]
741 NodeIdType getIdentifierType() const noexcept {
742 return identifierType();
743 }
744
745 /**
746 * Get identifier pointer or `nullptr` on error.
747 * @tparam T Requested identifier type, should be either:
748 * - `uint32_t` for NodeIdType::Numeric
749 * - `String` for NodeIdType::String
750 * - `Guid` for NodeIdType::Guid
751 * - `ByteString` for NodeIdType::ByteString
752 * @return Pointer to the stored identifier
753 * or a `nullptr` if `T` doesn't match the stored identifier type
754 */
755 template <typename T>
756 T* identifierIf() noexcept {
757 return const_cast<T*>(std::as_const(*this).identifierIf<T>()); // NOLINT
758 }
759
760 /// @copydoc identifierIf
761 template <typename T>
762 const T* identifierIf() const noexcept {
763 static_assert(
764 detail::IsOneOf<T, uint32_t, String, Guid, ByteString>::value,
765 "Invalid type for NodeId identifier"
766 );
767 // NOLINTBEGIN(cppcoreguidelines-pro-type-union-access)
768 if constexpr (std::is_same_v<T, uint32_t>) {
770 ? &handle()->identifier.numeric
771 : nullptr;
772 } else if constexpr (std::is_same_v<T, String>) {
774 ? asWrapper<String>(&handle()->identifier.string)
775 : nullptr;
776 } else if constexpr (std::is_same_v<T, Guid>) {
778 ? asWrapper<Guid>(&handle()->identifier.guid)
779 : nullptr;
780 } else if constexpr (std::is_same_v<T, ByteString>) {
782 ? asWrapper<ByteString>(&handle()->identifier.byteString)
783 : nullptr;
784 } else {
785 return nullptr;
786 }
787 // NOLINTEND(cppcoreguidelines-pro-type-union-access)
788 }
789
790 /**
791 * Get identifier reference.
792 * @tparam T Requested identifier type, should be either:
793 * - `uint32_t` for NodeIdType::Numeric
794 * - `String` for NodeIdType::String
795 * - `Guid` for NodeIdType::Guid
796 * - `ByteString` for NodeIdType::ByteString
797 * @return Reference to the stored identifier
798 * @throws TypeError If the requested type `T` doesn't match the stored identifier type
799 */
800 template <typename T>
802 return const_cast<T&>(std::as_const(*this).identifier<T>()); // NOLINT
803 }
804
805 /// @copydoc identifier
806 template <typename T>
807 const T& identifier() const {
808 if (auto* ptr = identifierIf<T>()) {
809 return *ptr;
810 }
811 throw TypeError("NodeId identifier type doesn't match the requested type");
812 }
813
814 /// Get identifier variant.
815 /// @deprecated Use identifier<T>() or identifierIf<T>() instead
816 [[deprecated("use identifier<T>() or identifierIf<T>() instead")]]
817 std::variant<uint32_t, String, Guid, ByteString> getIdentifier() const {
818 return getIdentifierImpl();
819 }
820
821 /// Get identifier by template type.
822 /// @deprecated Use identifier<T>() or identifierIf<T>() instead
823 template <typename T>
824 [[deprecated("use identifier<T>() or identifierIf<T>() instead")]]
825 auto getIdentifierAs() const {
826 return getIdentifierAsImpl<T>();
827 }
828
829 /// Get identifier by NodeIdType enum.
830 /// @deprecated Use identifier<T>() or identifierIf<T>() instead
831 template <NodeIdType E>
832 [[deprecated("use identifier<T>() or identifierIf<T>() instead")]]
833 auto getIdentifierAs() const {
834 return getIdentifierAsImpl<E>();
835 }
836
837 /// @deprecated Use free function opcua::toString(const T&) instead
838 [[deprecated("use free function toString instead")]]
839 std::string toString() const;
840
841private:
842 std::variant<uint32_t, String, Guid, ByteString> getIdentifierImpl() const {
843 switch (handle()->identifierType) {
845 return handle()->identifier.numeric; // NOLINT
847 return String(handle()->identifier.string); // NOLINT
849 return Guid(handle()->identifier.guid); // NOLINT
851 return ByteString(handle()->identifier.byteString); // NOLINT
852 default:
853 return {};
854 }
855 }
856
857 template <typename T>
858 auto getIdentifierAsImpl() const {
859 return std::get<T>(getIdentifierImpl());
860 }
861
862 template <NodeIdType E>
863 auto getIdentifierAsImpl() const {
864 if constexpr (E == NodeIdType::Numeric) {
865 return getIdentifierAsImpl<uint32_t>();
866 } else if constexpr (E == NodeIdType::String) {
867 return getIdentifierAsImpl<String>();
868 } else if constexpr (E == NodeIdType::Guid) {
869 return getIdentifierAsImpl<Guid>();
870 } else if constexpr (E == NodeIdType::ByteString) {
871 return getIdentifierAsImpl<ByteString>();
872 }
873 }
874};
875
876/// @relates NodeId
877inline bool operator==(const UA_NodeId& lhs, const UA_NodeId& rhs) noexcept {
878 return UA_NodeId_equal(&lhs, &rhs);
879}
880
881/// @relates NodeId
882inline bool operator!=(const UA_NodeId& lhs, const UA_NodeId& rhs) noexcept {
883 return !(lhs == rhs);
884}
885
886/// @relates NodeId
887inline bool operator<(const UA_NodeId& lhs, const UA_NodeId& rhs) noexcept {
888 return UA_NodeId_order(&lhs, &rhs) == UA_ORDER_LESS;
889}
890
891/// @relates NodeId
892inline bool operator>(const UA_NodeId& lhs, const UA_NodeId& rhs) noexcept {
893 return UA_NodeId_order(&lhs, &rhs) == UA_ORDER_MORE;
894}
895
896/// @relates NodeId
897inline bool operator<=(const UA_NodeId& lhs, const UA_NodeId& rhs) noexcept {
898 return (lhs < rhs) || (lhs == rhs);
899}
900
901/// @relates NodeId
902inline bool operator>=(const UA_NodeId& lhs, const UA_NodeId& rhs) noexcept {
903 return (lhs > rhs) || (lhs == rhs);
904}
905
906/* --------------------------------------- ExpandedNodeId --------------------------------------- */
907
908/**
909 * UA_ExpandedNodeId wrapper class.
910 * @see https://reference.opcfoundation.org/Core/Part4/v105/docs/7.16
911 * @ingroup Wrapper
912 */
913class ExpandedNodeId : public TypeWrapper<UA_ExpandedNodeId, UA_TYPES_EXPANDEDNODEID> {
914public:
916
917 explicit ExpandedNodeId(NodeId id) noexcept {
918 asWrapper<NodeId>(handle()->nodeId) = std::move(id);
919 }
920
921 ExpandedNodeId(NodeId id, std::string_view namespaceUri, uint32_t serverIndex) {
922 asWrapper<NodeId>(handle()->nodeId) = std::move(id);
923 handle()->namespaceUri = detail::allocNativeString(namespaceUri);
924 handle()->serverIndex = serverIndex;
925 }
926
927#if UAPP_HAS_PARSING
928 /// Parse ExpandedNodeId from its string representation.
929 /// Format: `svr=<serverindex>;ns=<namespaceindex>;<type>=<value>` or
930 /// `svr=<serverindex>;nsu=<uri>;<type>=<value>`
931 /// @see https://reference.opcfoundation.org/Core/Part6/v104/docs/5.3.1.11
932 static ExpandedNodeId parse(std::string_view str) {
934 throwIfBad(UA_ExpandedNodeId_parse(id.handle(), detail::toNativeString(str)));
935 return id;
936 }
937#endif
938
939 bool isLocal() const noexcept {
940 return handle()->serverIndex == 0;
941 }
942
943 uint32_t hash() const noexcept {
945 }
946
947 NodeId& nodeId() noexcept {
948 return asWrapper<NodeId>(handle()->nodeId);
949 }
950
951 const NodeId& nodeId() const noexcept {
952 return asWrapper<NodeId>(handle()->nodeId);
953 }
954
955 /// @deprecated Use nodeId() instead
956 [[deprecated("use nodeId() instead")]]
957 NodeId& getNodeId() noexcept {
958 return nodeId();
959 }
960
961 /// @deprecated Use nodeId() instead
962 [[deprecated("use nodeId() instead")]]
963 const NodeId& getNodeId() const noexcept {
964 return nodeId();
965 }
966
967 std::string_view namespaceUri() const {
968 return detail::toStringView(handle()->namespaceUri);
969 }
970
971 /// @deprecated Use namespaceUri() instead
972 [[deprecated("use namespaceUri() instead")]]
973 std::string_view getNamespaceUri() const {
974 return namespaceUri();
975 }
976
977 uint32_t serverIndex() const noexcept {
978 return handle()->serverIndex;
979 }
980
981 /// @deprecated Use serverIndex() instead
982 [[deprecated("use serverIndex() instead")]]
983 uint32_t getServerIndex() const noexcept {
984 return serverIndex();
985 }
986
987 /// @deprecated Use free function opcua::toString(const T&) instead
988 [[deprecated("use free function toString instead")]]
989 std::string toString() const;
990};
991
992/// @relates ExpandedNodeId
993inline bool operator==(const UA_ExpandedNodeId& lhs, const UA_ExpandedNodeId& rhs) noexcept {
994 return UA_ExpandedNodeId_equal(&lhs, &rhs);
995}
996
997/// @relates ExpandedNodeId
998inline bool operator!=(const UA_ExpandedNodeId& lhs, const UA_ExpandedNodeId& rhs) noexcept {
999 return !(lhs == rhs);
1000}
1001
1002/// @relates ExpandedNodeId
1003inline bool operator<(const UA_ExpandedNodeId& lhs, const UA_ExpandedNodeId& rhs) noexcept {
1004 return UA_ExpandedNodeId_order(&lhs, &rhs) == UA_ORDER_LESS;
1005}
1006
1007/// @relates ExpandedNodeId
1008inline bool operator>(const UA_ExpandedNodeId& lhs, const UA_ExpandedNodeId& rhs) noexcept {
1009 return UA_ExpandedNodeId_order(&lhs, &rhs) == UA_ORDER_MORE;
1010}
1011
1012/// @relates ExpandedNodeId
1013inline bool operator<=(const UA_ExpandedNodeId& lhs, const UA_ExpandedNodeId& rhs) noexcept {
1014 return (lhs < rhs) || (lhs == rhs);
1015}
1016
1017/// @relates ExpandedNodeId
1018inline bool operator>=(const UA_ExpandedNodeId& lhs, const UA_ExpandedNodeId& rhs) noexcept {
1019 return (lhs > rhs) || (lhs == rhs);
1020}
1021
1022/* ---------------------------------------- QualifiedName --------------------------------------- */
1023
1024/**
1025 * UA_QualifiedName wrapper class.
1026 * @ingroup Wrapper
1027 */
1028class QualifiedName : public TypeWrapper<UA_QualifiedName, UA_TYPES_QUALIFIEDNAME> {
1029public:
1031
1033 handle()->namespaceIndex = namespaceIndex;
1034 handle()->name = detail::allocNativeString(name);
1035 }
1036
1038 return handle()->namespaceIndex;
1039 }
1040
1041 /// @deprecated Use namespaceIndex() instead
1042 [[deprecated("use namespaceIndex() instead")]]
1044 return namespaceIndex();
1045 }
1046
1047 std::string_view name() const noexcept {
1048 return detail::toStringView(handle()->name);
1049 }
1050
1051 /// @deprecated Use name() instead
1052 [[deprecated("use name() instead")]]
1053 std::string_view getName() const noexcept {
1054 return name();
1055 }
1056};
1057
1058/// @relates QualifiedName
1059inline bool operator==(const UA_QualifiedName& lhs, const UA_QualifiedName& rhs) noexcept {
1060 return (lhs.namespaceIndex == rhs.namespaceIndex) && (lhs.name == rhs.name);
1061}
1062
1063/// @relates QualifiedName
1064inline bool operator!=(const UA_QualifiedName& lhs, const UA_QualifiedName& rhs) noexcept {
1065 return !(lhs == rhs);
1066}
1067
1068/* ---------------------------------------- LocalizedText --------------------------------------- */
1069
1070/**
1071 * UA_LocalizedText wrapper class.
1072 * The format of locale is `<language>[-<country/region>]`:
1073 * - `<language>` is the two letter ISO 639 code for a language
1074 * - `<country/region>` is the two letter ISO 3166 code for the country/region
1075 * @see https://reference.opcfoundation.org/Core/Part3/v105/docs/8.5/
1076 * @see https://reference.opcfoundation.org/Core/Part3/v105/docs/8.4/
1077 * @ingroup Wrapper
1078 */
1079class LocalizedText : public TypeWrapper<UA_LocalizedText, UA_TYPES_LOCALIZEDTEXT> {
1080public:
1082
1083 LocalizedText(std::string_view locale, std::string_view text) {
1084 handle()->locale = detail::allocNativeString(locale);
1085 handle()->text = detail::allocNativeString(text);
1086 }
1087
1088 std::string_view locale() const noexcept {
1089 return detail::toStringView(handle()->locale);
1090 }
1091
1092 /// @deprecated Use locale() instead
1093 [[deprecated("use locale() instead")]]
1094 std::string_view getLocale() const noexcept {
1095 return locale();
1096 }
1097
1098 std::string_view text() const noexcept {
1099 return detail::toStringView(handle()->text);
1100 }
1101
1102 /// @deprecated Use text() instead
1103 [[deprecated("use text() instead")]]
1104 std::string_view getText() const noexcept {
1105 return text();
1106 }
1107};
1108
1109/// @relates LocalizedText
1110inline bool operator==(const UA_LocalizedText& lhs, const UA_LocalizedText& rhs) noexcept {
1111 return (lhs.locale == rhs.locale) && (lhs.text == rhs.text);
1112}
1113
1114/// @relates LocalizedText
1115inline bool operator!=(const UA_LocalizedText& lhs, const UA_LocalizedText& rhs) noexcept {
1116 return !(lhs == rhs);
1117}
1118
1119/* ------------------------------------------- Variant ------------------------------------------ */
1120
1121/**
1122 * Policies for variant factory methods Variant::fromScalar, Variant::fromArray.
1123 */
1124enum class VariantPolicy {
1125 // clang-format off
1126 Copy, ///< Store copy of scalar/array inside the variant.
1127 Reference, ///< Store reference to scalar/array inside the variant.
1128 ///< Both scalars and arrays must be mutable native/wrapper types.
1129 ///< Arrays must store the elements contiguously in memory.
1130 // clang-format on
1131};
1132
1134public:
1135 using TypeError::TypeError;
1136};
1137
1138/**
1139 * UA_Variant wrapper class.
1140 *
1141 * Variants may contain scalar values or arrays of any type together with a type definition.
1142 * The standard mandates that variants contain built-in data types only.
1143 * open62541 transparently handles this wrapping in the encoding layer. If the type is unknown to
1144 * the receiver, the variant stores the original ExtensionObject in binary or XML encoding.
1145 *
1146 * open62541pp enhances variant handling with the following features:
1147 *
1148 * 1. **Type category detection**: Identifies whether `T` is scalar or array:
1149 * - **Scalar**, if `T` is:
1150 * - A registered type (TypeRegistry spezialization). This applies to native types and
1151 * @ref Wrapper "wrapper types".
1152 * - A convertible type (TypeConverter spezialization).
1153 * - **Array**, if `T` is a container type (e.g. `std::vector` or `std::list`) and does not
1154 * satisfy the criterias of a scalar type.
1155 *
1156 * Applied in @ref Variant::Variant "constructors", @ref assign, and @ref to functions:
1157 *
1158 * @code
1159 * opcua::Variant var(5); // set scalar (via constructor)
1160 * auto value = var.to<int>(); // convert scalar
1161 *
1162 * std::array<int> array{1, 2, 3};
1163 * var.assign(array); // set array (via assign)
1164 * auto vec = var.to<std::vector<int>>(); // convert array
1165 * @endcode
1166 *
1167 * 2. **Type definition retrieval**: Automatically retrieves UA_DataType via TypeRegistry.
1168 * For every registered type `T`, the type definition parameter can be omitted:
1169 *
1170 * @code
1171 * opcua::Variant var(5, UA_TYPES[UA_TYPES_INT]); // explicit type definition
1172 * opcua::Variant var(5); // auto-detected type definition
1173 * @endcode
1174 *
1175 * 3. **Type conversion**: Convert non-native types using TypeConverter.
1176 * Native `UA_*` types can be assigned and retrieved to/from variants without any conversion,
1177 * because their binary layout is described by the type definition (UA_DataType). The same is
1178 * true for wrapper types, that share the exact memory layout as their wrapped native type.
1179 * Non-native types, like `std::string` from the STL, may not be describeable by UA_DataType
1180 * because their memory layout is an implementation detail.
1181 * Instead, the conversion between non-native and native types can be defined with template
1182 * specializations of TypeConverter. If a type is convertible (TypeConverter specialization),
1183 * the Variant automatically manages the conversion, requiring a copy:
1184 *
1185 * @code
1186 * opcua::Variant var(std::string("test")); // convert to native type (copy)
1187 * auto& native = var.scalar<opcua::String>(); // reference to native type (no copy)
1188 * auto str = var.to<std::string>(); // conversion (copy required)
1189 * @endcode
1190 *
1191 * @ingroup Wrapper
1192 */
1193class Variant : public TypeWrapper<UA_Variant, UA_TYPES_VARIANT> {
1194private:
1195 template <typename T>
1196 static constexpr bool isVariant =
1197 std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, Variant> ||
1198 std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, UA_Variant>;
1199
1200public:
1202
1203 /// Create Variant from a pointer to a scalar/array (no copy).
1204 /// @see assign(T*)
1205 template <typename T, typename = std::enable_if_t<!std::is_const_v<T>>>
1206 explicit Variant(T* ptr) noexcept {
1207 assign(ptr);
1208 }
1209
1210 /// Create Variant from a pointer to a scalar/array with a custom data type (no copy).
1211 /// @see assign(T*, const UA_DataType&)
1212 template <typename T, typename = std::enable_if_t<!std::is_const_v<T>>>
1213 Variant(T* ptr, const UA_DataType& type) noexcept {
1214 assign(ptr, type);
1215 }
1216
1217 /// Create Variant from a scalar/array (copy).
1218 /// @see assign(const T&)
1219 template <typename T, typename = std::enable_if_t<!isVariant<T>>>
1220 explicit Variant(const T& value) {
1221 assign(value);
1222 }
1223
1224 /// Create Variant from a scalar/array with a custom data type (copy).
1225 /// @see assign(const T&, const UA_DataType&)
1226 template <typename T>
1227 Variant(const T& value, const UA_DataType& type) {
1228 assign(value, type);
1229 }
1230
1231 /// Create Variant from a range of elements (copy).
1232 /// @see assign(InputIt, InputIt)
1233 template <typename InputIt>
1234 Variant(InputIt first, InputIt last) {
1235 assign(first, last);
1236 }
1237
1238 /// Create Variant from a range of elements with a custom data type (copy).
1239 /// @see assign(InputIt, InputIt, const UA_DataType&)
1240 template <typename InputIt>
1241 Variant(InputIt first, InputIt last, const UA_DataType& type) {
1242 assign(first, last, type);
1243 }
1244
1245 /// @deprecated Use new universal Variant constructor instead
1246 template <VariantPolicy Policy = VariantPolicy::Copy, typename T, typename... Args>
1247 [[deprecated("use new universal Variant constructor instead")]] [[nodiscard]]
1248 static Variant fromScalar(T&& value, Args&&... args) {
1249 if constexpr (Policy == VariantPolicy::Copy) {
1250 return Variant{std::forward<T>(value), std::forward<Args>(args)...};
1251 } else {
1252 return Variant{&value, std::forward<Args>(args)...};
1253 }
1254 }
1255
1256 /// @deprecated Use new universal Variant constructor instead
1257 template <VariantPolicy Policy = VariantPolicy::Copy, typename T, typename... Args>
1258 [[deprecated("use new universal Variant constructor instead")]] [[nodiscard]]
1259 static Variant fromArray(T&& array, Args&&... args) {
1260 if constexpr (Policy == VariantPolicy::Copy) {
1261 return Variant{std::forward<T>(array), std::forward<Args>(args)...};
1262 } else {
1263 return Variant{&array, std::forward<Args>(args)...};
1264 }
1265 }
1266
1267 /// @deprecated Use new universal Variant constructor instead
1268 template <VariantPolicy Policy = VariantPolicy::Copy, typename InputIt, typename... Args>
1269 [[deprecated("use new universal Variant constructor instead")]] [[nodiscard]]
1270 static Variant fromArray(InputIt first, InputIt last, Args&&... args) {
1271 return Variant{first, last, std::forward<Args>(args)...};
1272 }
1273
1274 /**
1275 * @name Modifiers
1276 * Modify internal scalar/array value.
1277 * @{
1278 */
1279
1280 void assign(std::nullptr_t ptr) noexcept = delete;
1281 void assign(std::nullptr_t ptr, const UA_DataType& type) noexcept = delete;
1282
1283 /**
1284 * Assign pointer to scalar/array to variant (no copy).
1285 * The object will *not* be deleted when the Variant is destructed.
1286 * @param ptr Non-const pointer to a value to assign to the variant. This can be:
1287 * - A pointer to a scalar native or wrapper value.
1288 * - A pointer to a contiguous container such as `std::array` or `std::vector`
1289 * holding native or wrapper elements.
1290 * The underlying array must be accessible with `std::data` and `std::size`.
1291 * - A `nullptr`, in which case the variant will be cleared.
1292 */
1293 template <typename T, typename = std::enable_if_t<!std::is_const_v<T>>>
1294 void assign(T* ptr) noexcept {
1295 if constexpr (isArrayType<T>()) {
1296 using ValueType = detail::RangeValueT<T>;
1297 assertIsRegistered<ValueType>();
1298 assign(ptr, opcua::getDataType<ValueType>());
1299 } else {
1300 assertIsRegistered<T>();
1301 assign(ptr, opcua::getDataType<T>());
1302 }
1303 }
1304
1305 /**
1306 * Assign pointer to scalar/array to variant with custom data type (no copy).
1307 * @copydetails assign(T*)
1308 * @param type Custom data type.
1309 */
1310 template <typename T, typename = std::enable_if_t<!std::is_const_v<T>>>
1311 void assign(T* ptr, const UA_DataType& type) noexcept {
1312 if (ptr == nullptr) {
1313 clear();
1314 } else if constexpr (isArrayType<T>()) {
1315 setArrayImpl(std::data(*ptr), std::size(*ptr), type, UA_VARIANT_DATA_NODELETE);
1316 } else {
1317 setScalarImpl(ptr, type, UA_VARIANT_DATA_NODELETE);
1318 }
1319 }
1320
1321 /**
1322 * Assign scalar/array to variant (copy and convert if required).
1323 * @param value Value to copy to the variant. It can be:
1324 * - A scalar native, wrapper or convertible value.
1325 * - A container with native, wrapper or convertible elements.
1326 * The container must implement `begin()` and `end()`.
1327 */
1328 template <typename T>
1329 void assign(const T& value) {
1330 if constexpr (isArrayType<T>()) {
1331 assign(std::begin(value), std::end(value));
1332 } else {
1333 assertIsRegisteredOrConvertible<T>();
1334 if constexpr (detail::IsRegistered<T>::value) {
1335 setScalarCopyImpl(value, opcua::getDataType<T>());
1336 } else {
1337 setScalarCopyConvertImpl(value);
1338 }
1339 }
1340 }
1341
1342 /**
1343 * Assign scalar/array to variant with custom data type (copy).
1344 * @param value Value to copy to the variant. It can be:
1345 * - A scalar native or wrapper value.
1346 * - A container with native or wrapper elements.
1347 * The container must implement `begin()` and `end()`.
1348 * @param type Custom data type.
1349 */
1350 template <typename T>
1351 void assign(const T& value, const UA_DataType& type) {
1352 if constexpr (isArrayType<T>()) {
1353 setArrayCopyImpl(std::begin(value), std::end(value), type);
1354 } else {
1355 setScalarCopyImpl(value, type);
1356 }
1357 }
1358
1359 /**
1360 * Assign range to variant (copy and convert if required).
1361 * @param first Iterator to the beginning of the range.
1362 * @param last Iterator to the end of the range.
1363 * @tparam InputIt Iterator of a container with native, wrapper or convertible elements.
1364 */
1365 template <typename InputIt>
1366 void assign(InputIt first, InputIt last) {
1367 using ValueType = typename std::iterator_traits<InputIt>::value_type;
1368 assertIsRegisteredOrConvertible<ValueType>();
1369 if constexpr (detail::IsRegistered<ValueType>::value) {
1370 setArrayCopyImpl(first, last, opcua::getDataType<ValueType>());
1371 } else {
1372 setArrayCopyConvertImpl(first, last);
1373 }
1374 }
1375
1376 /**
1377 * Assign range to variant with custom data type (copy).
1378 * @param first Iterator to the beginning of the range.
1379 * @param last Iterator to the end of the range.
1380 * @param type Custom data type.
1381 * @tparam InputIt Iterator of a container with native or wrapper elements.
1382 */
1383 template <typename InputIt>
1384 void assign(InputIt first, InputIt last, const UA_DataType& type) {
1385 setArrayCopyImpl(first, last, type);
1386 }
1387
1388 /// Assign pointer to scalar/array to variant (no copy).
1389 /// @see assign(T*)
1390 template <typename T, typename = std::enable_if_t<!isVariant<T>>>
1391 Variant& operator=(T* value) noexcept {
1392 assign(value);
1393 return *this;
1394 }
1395
1396 /// Assign scalar/array to variant (copy and convert if required).
1397 /// @see assign(const T&)
1398 template <typename T, typename = std::enable_if_t<!isVariant<T>>>
1399 Variant& operator=(const T& value) {
1400 assign(value);
1401 return *this;
1402 }
1403
1404 /// @deprecated Use assign overload with pointer instead
1405 template <typename T, typename... Args>
1406 [[deprecated("use assign overload with pointer instead")]]
1407 void setScalar(T& value, Args&&... args) noexcept {
1408 assign(&value, std::forward<Args>(args)...);
1409 }
1410
1411 /// @deprecated Use assign overload instead
1412 template <typename T, typename... Args>
1413 [[deprecated("use assign overload instead")]]
1414 void setScalarCopy(const T& value, Args&&... args) {
1415 assign(value, std::forward<Args>(args)...);
1416 }
1417
1418 /// @deprecated Use assign overload with pointer instead
1419 template <typename T, typename... Args>
1420 [[deprecated("use assign overload with pointer instead")]]
1421 void setArray(T& array, Args&&... args) noexcept {
1422 assign(&array, std::forward<Args>(args)...);
1423 }
1424
1425 /// @deprecated Use assign overload instead
1426 template <typename T, typename... Args>
1427 [[deprecated("use assign overload instead")]]
1428 void setArrayCopy(const T& array, Args&&... args) {
1429 assign(array, std::forward<Args>(args)...);
1430 }
1431
1432 /// @deprecated Use assign overload instead
1433 template <typename InputIt, typename... Args>
1434 [[deprecated("use assign overload instead")]]
1435 void setArrayCopy(InputIt first, InputIt last, Args&&... args) {
1436 assign(first, last, std::forward<Args>(args)...);
1437 }
1438
1439 /**
1440 * @name Observers
1441 * Check the type category, type definition and array structure of the internal value.
1442 */
1443
1444 /// Check if the variant is empty.
1445 bool empty() const noexcept {
1446 return handle()->type == nullptr;
1447 }
1448
1449 /// @deprecated Use empty() instead
1450 [[deprecated("use empty() instead")]]
1451 bool isEmpty() const noexcept {
1452 return empty();
1453 }
1454
1455 /// Check if the variant is a scalar.
1456 bool isScalar() const noexcept {
1457 return (
1458 !empty() && handle()->arrayLength == 0 &&
1459 handle()->data > UA_EMPTY_ARRAY_SENTINEL // NOLINT
1460 );
1461 }
1462
1463 /// Check if the variant is an array.
1464 bool isArray() const noexcept {
1465 return !empty() && !isScalar();
1466 }
1467
1468 /// Check if the variant type is equal to the provided data type.
1469 bool isType(const UA_DataType* type) const noexcept {
1470 return (
1471 handle()->type != nullptr && type != nullptr && handle()->type->typeId == type->typeId
1472 );
1473 }
1474
1475 /// Check if the variant type is equal to the provided data type.
1476 bool isType(const UA_DataType& type) const noexcept {
1477 return isType(&type);
1478 }
1479
1480 /// Check if the variant type is equal to the provided data type node id.
1481 bool isType(const NodeId& id) const noexcept {
1482 return (handle()->type != nullptr) && (handle()->type->typeId == id);
1483 }
1484
1485 /// Check if the variant type is equal to the provided template type.
1486 template <typename T>
1487 bool isType() const noexcept {
1488 return isType(opcua::getDataType<T>());
1489 }
1490
1491 /// Get data type.
1492 const UA_DataType* type() const noexcept {
1493 return handle()->type;
1494 }
1495
1496 /// @deprecated Use type() instead
1497 [[deprecated("use type() instead")]]
1498 const UA_DataType* getDataType() const noexcept {
1499 return type();
1500 }
1501
1502 /// Get array length or 0 if variant is not an array.
1503 size_t arrayLength() const noexcept {
1504 return handle()->arrayLength;
1505 }
1506
1507 /// @deprecated Use arrayLength() instead
1508 [[deprecated("use arrayLength() instead")]]
1509 size_t getArrayLength() const noexcept {
1510 return arrayLength();
1511 }
1512
1513 /// Get array dimensions.
1515 return {handle()->arrayDimensions, handle()->arrayDimensionsSize};
1516 }
1517
1518 /// @deprecated Use arrayDimensions() instead
1519 [[deprecated("use arrayDimensions() instead")]]
1521 return arrayDimensions();
1522 }
1523
1524 /**
1525 * @name Accessors
1526 * Access and convert internal scalar/array value.
1527 * @{
1528 */
1529
1530 /// Get pointer to the underlying data.
1531 /// Check the properties and data type before casting it to the actual type.
1532 /// Use the methods @ref isScalar, @ref isArray, @ref isType / @ref type.
1533 void* data() noexcept {
1534 return handle()->data;
1535 }
1536
1537 /// @copydoc data
1538 const void* data() const noexcept {
1539 return handle()->data;
1540 }
1541
1542 /// Get reference to scalar value with given template type (only native or wrapper types).
1543 /// @exception BadVariantAccess If the variant is not a scalar or not of type `T`.
1544 template <typename T>
1545 T& scalar() & {
1546 assertIsRegistered<T>();
1547 checkIsScalar();
1548 checkIsType<T>();
1549 return *static_cast<T*>(handle()->data);
1550 }
1551
1552 /// @copydoc scalar()&
1553 template <typename T>
1554 const T& scalar() const& {
1555 assertIsRegistered<T>();
1556 checkIsScalar();
1557 checkIsType<T>();
1558 return *static_cast<const T*>(handle()->data);
1559 }
1560
1561 /// @copydoc scalar()&
1562 template <typename T>
1563 T&& scalar() && {
1564 return std::move(scalar<T>());
1565 }
1566
1567 /// @copydoc scalar()&
1568 template <typename T>
1569 const T&& scalar() const&& {
1570 return std::move(scalar<T>());
1571 }
1572
1573 /// @deprecated Use scalar() instead
1574 template <typename T>
1575 [[deprecated("use scalar() instead")]]
1577 return scalar<T>();
1578 }
1579
1580 /// @deprecated Use scalar() instead
1581 template <typename T>
1582 [[deprecated("use scalar() instead")]]
1583 const T& getScalar() const {
1584 return scalar<T>();
1585 }
1586
1587 /// @deprecated Use to<T>() instead
1588 template <typename T>
1589 [[deprecated("use to<T>() instead")]]
1590 T getScalarCopy() const {
1591 return to<T>();
1592 }
1593
1594 /// Get reference to array with given template type (only native or wrapper types).
1595 /// @exception BadVariantAccess If the variant is not an array or not of type `T`.
1596 template <typename T>
1598 assertIsRegistered<T>();
1599 checkIsArray();
1600 checkIsType<T>();
1601 return Span<T>(static_cast<T*>(handle()->data), handle()->arrayLength);
1602 }
1603
1604 /// Get reference to array with given template type (only native or wrapper types).
1605 /// @exception BadVariantAccess If the variant is not an array or not of type `T`.
1606 template <typename T>
1608 assertIsRegistered<T>();
1609 checkIsArray();
1610 checkIsType<T>();
1611 return Span<const T>(static_cast<const T*>(handle()->data), handle()->arrayLength);
1612 }
1613
1614 /// @deprecated Use array() instead
1615 template <typename T>
1616 [[deprecated("use array() instead")]]
1618 return array<T>();
1619 }
1620
1621 /// @deprecated Use array() instead
1622 template <typename T>
1623 [[deprecated("use array() instead")]]
1625 return array<T>();
1626 }
1627
1628 /// @deprecated Use to<std::vector<T>>() instead
1629 template <typename T>
1630 [[deprecated("use to<std::vector<T>>() instead")]]
1631 std::vector<T> getArrayCopy() const {
1632 return to<std::vector<T>>();
1633 }
1634
1635 /**
1636 * Converts the variant to the specified type `T` with automatic conversion if required.
1637 *
1638 * Determines the type category (scalar or array) based on the characteristics of `T` using
1639 * @ref Variant "type category detection".
1640 * If `T` is a container, it must be constructible from an iterator pair.
1641 *
1642 * @code
1643 * // Scalar
1644 * opcua::Variant var(11);
1645 * const auto value = var.to<int>();
1646 * @endcode
1647 *
1648 * @code
1649 * // Array
1650 * std::array<std::string, 3> array{"One", "Two", "Three"};
1651 * opcua::Variant var(array);
1652 * const auto vec = var.to<std::vector<std::string>>();
1653 * const auto lst = var.to<std::list<opcua::String>>();
1654 * @endcode
1655 *
1656 * @exception BadVariantAccess If the variant is not convertible to `T`.
1657 */
1658 template <typename T>
1659 [[nodiscard]] T to() const {
1660 if constexpr (isArrayType<T>()) {
1661 return toArrayImpl<T>();
1662 } else {
1663 return toScalarImpl<T>();
1664 }
1665 }
1666
1667 /**
1668 * @}
1669 */
1670
1671private:
1672 template <typename T>
1673 static constexpr bool isScalarType() noexcept {
1674 return detail::IsRegistered<T>::value || detail::IsConvertible<T>::value;
1675 }
1676
1677 template <typename T>
1678 static constexpr bool isArrayType() noexcept {
1679 return detail::IsRange<T>::value && !isScalarType<T>();
1680 }
1681
1682 template <typename T>
1683 static constexpr void assertIsRegistered() {
1684 static_assert(
1685 detail::IsRegistered<T>::value,
1686 "Template type must be a native/wrapper type to assign or get scalar/array without copy"
1687 );
1688 }
1689
1690 template <typename T>
1691 static constexpr void assertIsRegisteredOrConvertible() {
1692 static_assert(
1693 detail::IsRegistered<T>::value || detail::IsConvertible<T>::value,
1694 "Template type must be either a native/wrapper type or a convertible type. "
1695 "If the type is a native type: Provide the type definition (UA_DataType) manually or "
1696 "register the type with a TypeRegistry template specialization. "
1697 "If the type should be converted: Add a template specialization for TypeConverter."
1698 );
1699 }
1700
1701 template <typename T>
1702 static constexpr void assertNoVariant() {
1703 static_assert(!isVariant<T>, "Variants cannot directly contain another variant");
1704 }
1705
1706 void checkIsScalar() const {
1707 if (!isScalar()) {
1708 throw BadVariantAccess("Variant is not a scalar");
1709 }
1710 }
1711
1712 void checkIsArray() const {
1713 if (!isArray()) {
1714 throw BadVariantAccess("Variant is not an array");
1715 }
1716 }
1717
1718 template <typename T>
1719 void checkIsType() const {
1720 const auto* dt = type();
1721 if (dt == nullptr || dt->typeId != opcua::getDataType<T>().typeId) {
1722 throw BadVariantAccess("Variant does not contain a value convertible to template type");
1723 }
1724 }
1725
1726 template <typename T>
1727 T toScalarImpl() const;
1728 template <typename T>
1729 T toArrayImpl() const;
1730
1731 template <typename T>
1732 inline void setScalarImpl(
1733 T* data, const UA_DataType& type, UA_VariantStorageType storageType
1734 ) noexcept;
1735 template <typename T>
1736 inline void setArrayImpl(
1737 T* data, size_t arrayLength, const UA_DataType& type, UA_VariantStorageType storageType
1738 ) noexcept;
1739 template <typename T>
1740 inline void setScalarCopyImpl(const T& value, const UA_DataType& type);
1741 template <typename T>
1742 inline void setScalarCopyConvertImpl(const T& value);
1743 template <typename InputIt>
1744 inline void setArrayCopyImpl(InputIt first, InputIt last, const UA_DataType& type);
1745 template <typename InputIt>
1746 inline void setArrayCopyConvertImpl(InputIt first, InputIt last);
1747};
1748
1749template <typename T>
1750T Variant::toScalarImpl() const {
1751 assertIsRegisteredOrConvertible<T>();
1752 if constexpr (detail::IsRegistered<T>::value) {
1753 return scalar<T>();
1754 } else {
1755 using Native = typename TypeConverter<T>::NativeType;
1756 return detail::fromNative<T>(scalar<Native>());
1757 }
1758}
1759
1760template <typename T>
1761T Variant::toArrayImpl() const {
1762 using ValueType = typename T::value_type;
1763 assertIsRegisteredOrConvertible<ValueType>();
1764 if constexpr (detail::IsRegistered<ValueType>::value) {
1765 auto native = array<ValueType>();
1766 return T(native.begin(), native.end());
1767 } else {
1768 using Native = typename TypeConverter<ValueType>::NativeType;
1769 auto native = array<Native>();
1770 return T(
1771 detail::TransformIterator(native.begin(), detail::fromNative<ValueType>),
1772 detail::TransformIterator(native.end(), detail::fromNative<ValueType>)
1773 );
1774 }
1775}
1776
1777template <typename T>
1778void Variant::setScalarImpl(
1779 T* data, const UA_DataType& type, UA_VariantStorageType storageType
1780) noexcept {
1781 assertNoVariant<T>();
1782 assert(sizeof(T) == type.memSize);
1783 clear();
1784 handle()->type = &type;
1785 handle()->storageType = storageType;
1786 handle()->data = data;
1787}
1788
1789template <typename T>
1790void Variant::setArrayImpl(
1791 T* data, size_t arrayLength, const UA_DataType& type, UA_VariantStorageType storageType
1792) noexcept {
1793 assertNoVariant<T>();
1794 assert(sizeof(T) == type.memSize);
1795 clear();
1796 handle()->type = &type;
1797 handle()->storageType = storageType;
1798 handle()->data = data;
1799 handle()->arrayLength = arrayLength;
1800}
1801
1802template <typename T>
1803void Variant::setScalarCopyImpl(const T& value, const UA_DataType& type) {
1804 auto native = detail::allocateUniquePtr<T>(type);
1805 *native = detail::copy(value, type);
1806 setScalarImpl(native.release(), type, UA_VARIANT_DATA); // move ownership
1807}
1808
1809template <typename T>
1810void Variant::setScalarCopyConvertImpl(const T& value) {
1811 using Native = typename TypeConverter<T>::NativeType;
1812 const auto& type = opcua::getDataType<Native>();
1813 auto native = detail::allocateUniquePtr<Native>(type);
1814 *native = detail::toNative(value);
1815 setScalarImpl(native.release(), type, UA_VARIANT_DATA); // move ownership
1816}
1817
1818template <typename InputIt>
1819void Variant::setArrayCopyImpl(InputIt first, InputIt last, const UA_DataType& type) {
1820 using ValueType = typename std::iterator_traits<InputIt>::value_type;
1821 const size_t size = std::distance(first, last);
1822 auto native = detail::allocateArrayUniquePtr<ValueType>(size, type);
1823 std::transform(first, last, native.get(), [&](const ValueType& value) {
1824 return detail::copy(value, type);
1825 });
1826 setArrayImpl(native.release(), size, type, UA_VARIANT_DATA); // move ownership
1827}
1828
1829template <typename InputIt>
1830void Variant::setArrayCopyConvertImpl(InputIt first, InputIt last) {
1831 using ValueType = typename std::iterator_traits<InputIt>::value_type;
1832 using Native = typename TypeConverter<ValueType>::NativeType;
1833 const auto& type = opcua::getDataType<Native>();
1834 const size_t size = std::distance(first, last);
1835 auto native = detail::allocateArrayUniquePtr<Native>(size, type);
1836 std::transform(first, last, native.get(), [&](const ValueType& value) {
1837 return detail::toNative(value);
1838 });
1839 setArrayImpl(native.release(), size, type, UA_VARIANT_DATA); // move ownership
1840}
1841
1842/* ------------------------------------------ DataValue ----------------------------------------- */
1843
1844/**
1845 * UA_DataValue wrapper class.
1846 * @see https://reference.opcfoundation.org/Core/Part4/v105/docs/7.11
1847 * @ingroup Wrapper
1848 */
1849class DataValue : public TypeWrapper<UA_DataValue, UA_TYPES_DATAVALUE> {
1850public:
1852
1853 explicit DataValue(Variant value) noexcept {
1854 setValue(std::move(value));
1855 }
1856
1858 Variant value,
1859 std::optional<DateTime> sourceTimestamp, // NOLINT
1860 std::optional<DateTime> serverTimestamp, // NOLINT
1861 std::optional<uint16_t> sourcePicoseconds,
1862 std::optional<uint16_t> serverPicoseconds,
1863 std::optional<StatusCode> status
1864 ) noexcept
1866 UA_Variant{},
1867 sourceTimestamp.value_or(UA_DateTime{}),
1868 serverTimestamp.value_or(UA_DateTime{}),
1869 sourcePicoseconds.value_or(uint16_t{}),
1870 serverPicoseconds.value_or(uint16_t{}),
1871 status.value_or(UA_StatusCode{}),
1872 false,
1873 sourceTimestamp.has_value(),
1874 serverTimestamp.has_value(),
1875 sourcePicoseconds.has_value(),
1876 serverPicoseconds.has_value(),
1877 status.has_value(),
1878 }) {
1879 setValue(std::move(value));
1880 }
1881
1882 /// Create DataValue from scalar value.
1883 /// @deprecated Use constructor with new universal Variant constructor instead:
1884 /// `opcua::DataValue dv(opcua::Variant(value))`
1885 template <VariantPolicy Policy = VariantPolicy::Copy, typename... Args>
1886 [[deprecated("use constructor with new universal Variant constructor instead")]] [[nodiscard]]
1887 static DataValue fromScalar(Args&&... args) {
1888 return DataValue(Variant::fromScalar<Policy>(std::forward<Args>(args)...));
1889 }
1890
1891 /// Create DataValue from array.
1892 /// @see Variant::fromArray
1893 /// @deprecated Use constructor with new universal Variant constructor instead:
1894 /// `opcua::DataValue dv(opcua::Variant(array))`
1895 template <VariantPolicy Policy = VariantPolicy::Copy, typename... Args>
1896 [[deprecated("use constructor with new universal Variant constructor instead")]] [[nodiscard]]
1897 static DataValue fromArray(Args&&... args) {
1898 return DataValue(Variant::fromArray<Policy>(std::forward<Args>(args)...));
1899 }
1900
1901 /// Set value (copy).
1902 void setValue(const Variant& value) {
1903 asWrapper<Variant>(handle()->value) = value;
1904 handle()->hasValue = true;
1905 }
1906
1907 /// Set value (move).
1908 void setValue(Variant&& value) noexcept {
1909 asWrapper<Variant>(handle()->value) = std::move(value);
1910 handle()->hasValue = true;
1911 }
1912
1913 /// Set source timestamp for the value.
1915 handle()->sourceTimestamp = sourceTimestamp.get();
1916 handle()->hasSourceTimestamp = true;
1917 }
1918
1919 /// Set server timestamp for the value.
1921 handle()->serverTimestamp = serverTimestamp.get();
1922 handle()->hasServerTimestamp = true;
1923 }
1924
1925 /// Set picoseconds interval added to the source timestamp.
1926 void setSourcePicoseconds(uint16_t sourcePicoseconds) noexcept {
1927 handle()->sourcePicoseconds = sourcePicoseconds;
1928 handle()->hasSourcePicoseconds = true;
1929 }
1930
1931 /// Set picoseconds interval added to the server timestamp.
1932 void setServerPicoseconds(uint16_t serverPicoseconds) noexcept {
1933 handle()->serverPicoseconds = serverPicoseconds;
1934 handle()->hasServerPicoseconds = true;
1935 }
1936
1937 /// Set status.
1938 void setStatus(StatusCode status) noexcept {
1939 handle()->status = status;
1940 handle()->hasStatus = true;
1941 }
1942
1943 bool hasValue() const noexcept {
1944 return handle()->hasValue;
1945 }
1946
1947 bool hasSourceTimestamp() const noexcept {
1948 return handle()->hasSourceTimestamp;
1949 }
1950
1951 bool hasServerTimestamp() const noexcept {
1952 return handle()->hasServerTimestamp;
1953 }
1954
1955 bool hasSourcePicoseconds() const noexcept {
1956 return handle()->hasSourcePicoseconds;
1957 }
1958
1959 bool hasServerPicoseconds() const noexcept {
1960 return handle()->hasServerPicoseconds;
1961 }
1962
1963 bool hasStatus() const noexcept {
1964 return handle()->hasStatus;
1965 }
1966
1967 /// Get value.
1968 Variant& value() & noexcept {
1969 return asWrapper<Variant>(handle()->value);
1970 }
1971
1972 /// Get value.
1973 const Variant& value() const& noexcept {
1974 return asWrapper<Variant>(handle()->value);
1975 }
1976
1977 /// Get value (rvalue).
1978 Variant&& value() && noexcept {
1979 return std::move(value());
1980 }
1981
1982 /// Get value (rvalue).
1983 const Variant&& value() const&& noexcept {
1984 return std::move(value()); // NOLINT(*move-const-arg)
1985 }
1986
1987 /// @deprecated Use value() instead
1988 [[deprecated("use value() instead")]]
1989 Variant& getValue() & noexcept {
1990 return value();
1991 }
1992
1993 /// @deprecated Use value() instead
1994 [[deprecated("use value() instead")]]
1995 const Variant& getValue() const& noexcept {
1996 return value();
1997 }
1998
1999 /// @deprecated Use value() instead
2000 [[deprecated("use value() instead")]]
2001 Variant&& getValue() && noexcept {
2002 return std::move(value());
2003 }
2004
2005 /// @deprecated Use value() instead
2006 [[deprecated("use value() instead")]]
2007 const Variant&& getValue() const&& noexcept {
2008 return std::move(value()); // NOLINT(*move-const-arg)
2009 }
2010
2011 /// Get source timestamp for the value.
2012 DateTime sourceTimestamp() const noexcept {
2013 return DateTime(handle()->sourceTimestamp); // NOLINT
2014 }
2015
2016 /// @deprecated Use sourceTimestamp() instead
2017 [[deprecated("use sourceTimestamp() instead")]]
2018 DateTime getSourceTimestamp() const noexcept {
2019 return sourceTimestamp();
2020 }
2021
2022 /// Get server timestamp for the value.
2023 DateTime serverTimestamp() const noexcept {
2024 return DateTime(handle()->serverTimestamp); // NOLINT
2025 }
2026
2027 /// @deprecated Use serverTimestamp() instead
2028 [[deprecated("use serverTimestamp() instead")]]
2029 DateTime getServerTimestamp() const noexcept {
2030 return serverTimestamp();
2031 }
2032
2033 /// Get picoseconds interval added to the source timestamp.
2034 uint16_t sourcePicoseconds() const noexcept {
2035 return handle()->sourcePicoseconds;
2036 }
2037
2038 /// @deprecated Use sourcePicoseconds() instead
2039 [[deprecated("use sourcePicoseconds() instead")]]
2040 uint16_t getSourcePicoseconds() const noexcept {
2041 return sourcePicoseconds();
2042 }
2043
2044 /// Get picoseconds interval added to the server timestamp.
2045 uint16_t serverPicoseconds() const noexcept {
2046 return handle()->serverPicoseconds;
2047 }
2048
2049 /// @deprecated Use serverPicoseconds() instead
2050 [[deprecated("use serverPicoseconds() instead")]]
2051 uint16_t getServerPicoseconds() const noexcept {
2052 return serverPicoseconds();
2053 }
2054
2055 /// Get status.
2056 StatusCode status() const noexcept {
2057 return handle()->status;
2058 }
2059
2060 /// @deprecated Use status() instead
2061 [[deprecated("use status() instead")]]
2062 StatusCode getStatus() const noexcept {
2063 return status();
2064 }
2065};
2066
2067/* --------------------------------------- ExtensionObject -------------------------------------- */
2068
2069/**
2070 * Extension object encoding.
2071 * @see UA_ExtensionObjectEncoding
2072 */
2082
2083/**
2084 * UA_ExtensionObject wrapper class.
2085 *
2086 * ExtensionObjects may contain scalars of any data type. Even those that are unknown to the
2087 * receiver. If the received data type is unknown, the encoded string and target NodeId is stored
2088 * instead of the decoded data.
2089 *
2090 * @see https://reference.opcfoundation.org/Core/Part6/v105/docs/5.1.6
2091 * @see https://reference.opcfoundation.org/Core/Part6/v105/docs/5.2.2.15
2092 * @ingroup Wrapper
2093 */
2094class ExtensionObject : public TypeWrapper<UA_ExtensionObject, UA_TYPES_EXTENSIONOBJECT> {
2095private:
2096 template <typename T>
2097 static constexpr bool isExtensionObject =
2098 std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, ExtensionObject> ||
2099 std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, UA_ExtensionObject>;
2100
2101public:
2103
2104 /**
2105 * Create ExtensionObject from a pointer to a decoded object (no copy).
2106 * The object will *not* be deleted when the ExtensionObject is destructed.
2107 * @param ptr Pointer to decoded object (native or wrapper).
2108 */
2109 template <typename T>
2110 explicit ExtensionObject(T* ptr) noexcept
2111 : ExtensionObject(ptr, getDataType<T>()) {}
2112
2113 /**
2114 * Create ExtensionObject from a pointer to a decoded object with a custom data type (no copy).
2115 * The object will *not* be deleted when the ExtensionObject is destructed.
2116 * @param ptr Pointer to decoded object (native or wrapper).
2117 * @param type Data type of the decoded object.
2118 */
2119 template <typename T>
2120 ExtensionObject(T* ptr, const UA_DataType& type) noexcept {
2121 if (ptr == nullptr) {
2122 return;
2123 }
2124 assert(sizeof(T) == type.memSize);
2126 handle()->content.decoded.type = &type; // NOLINT
2127 handle()->content.decoded.data = ptr; // NOLINT
2128 }
2129
2130 /**
2131 * Create ExtensionObject from a decoded object (copy).
2132 * @param decoded Decoded object (native or wrapper).
2133 */
2134 template <typename T, typename = std::enable_if_t<!isExtensionObject<T>>>
2135 explicit ExtensionObject(const T& decoded)
2136 : ExtensionObject(decoded, getDataType<T>()) {}
2137
2138 /**
2139 * Create ExtensionObject from a decoded object with a custom data type (copy).
2140 * @param decoded Decoded object (native or wrapper).
2141 * @param type Data type of the decoded object.
2142 */
2143 template <typename T, typename = std::enable_if_t<!isExtensionObject<T>>>
2144 explicit ExtensionObject(const T& decoded, const UA_DataType& type) {
2145 auto ptr = detail::allocateUniquePtr<T>(type);
2146 *ptr = detail::copy(decoded, type);
2147 handle()->encoding = UA_EXTENSIONOBJECT_DECODED;
2148 handle()->content.decoded.type = &type; // NOLINT
2149 handle()->content.decoded.data = ptr.release(); // NOLINT
2150 }
2151
2152 /// @deprecated Use new universal ExtensionObject constructor instead
2153 template <typename T, typename... Args>
2154 [[deprecated("use new universal ExtensionObject constructor instead")]] [[nodiscard]]
2155 static ExtensionObject fromDecoded(T& data, Args&&... args) noexcept {
2156 return ExtensionObject(&data, std::forward<Args>(args)...);
2157 }
2158
2159 /// @deprecated Use new universal ExtensionObject constructor instead
2160 template <typename T, typename... Args>
2161 [[deprecated("use new universal ExtensionObject constructor instead")]] [[nodiscard]]
2162 static ExtensionObject fromDecodedCopy(const T& data, Args&&... args) {
2163 return ExtensionObject(data, std::forward<Args>(args)...);
2164 }
2165
2166 /// Check if the ExtensionObject is empty
2167 bool empty() const noexcept {
2169 }
2170
2171 /// @deprecated Use empty() instead
2172 [[deprecated("use empty() instead")]]
2173 bool isEmpty() const noexcept {
2174 return empty();
2175 }
2176
2177 /// Check if the ExtensionObject is encoded (usually if the data type is unknown).
2178 bool isEncoded() const noexcept {
2180 (handle()->encoding == UA_EXTENSIONOBJECT_ENCODED_XML);
2181 }
2182
2183 /// Check if the ExtensionObject is decoded.
2184 bool isDecoded() const noexcept {
2187 }
2188
2189 /// Get the encoding.
2191 return static_cast<ExtensionObjectEncoding>(handle()->encoding);
2192 }
2193
2194 /// @deprecated Use encoding() instead
2195 [[deprecated("use encoding() instead")]]
2197 return encoding();
2198 }
2199
2200 /// Get the encoded type id.
2201 /// Returns `nullptr` if ExtensionObject is not encoded.
2202 const NodeId* encodedTypeId() const noexcept {
2203 return isEncoded()
2204 ? asWrapper<NodeId>(&handle()->content.encoded.typeId) // NOLINT
2205 : nullptr;
2206 }
2207
2208 /// @deprecated Use encodedTypeId() instead
2209 [[deprecated("use encodedTypeId() instead")]]
2210 const NodeId* getEncodedTypeId() const noexcept {
2211 return encodedTypeId();
2212 }
2213
2214 /// Get the encoded body in binary format.
2215 /// Returns `nullptr` if ExtensionObject is not encoded in binary format.
2216 const ByteString* encodedBinary() const noexcept {
2217 return handle()->encoding == UA_EXTENSIONOBJECT_ENCODED_BYTESTRING
2218 ? asWrapper<ByteString>(&handle()->content.encoded.body) // NOLINT
2219 : nullptr;
2220 }
2221
2222 /// Get the encoded body in XML format.
2223 /// Returns `nullptr` if ExtensionObject is not encoded in XML format.
2224 const XmlElement* encodedXml() const noexcept {
2225 return handle()->encoding == UA_EXTENSIONOBJECT_ENCODED_XML
2226 ? asWrapper<XmlElement>(&handle()->content.encoded.body) // NOLINT
2227 : nullptr;
2228 }
2229
2230 /// @deprecated Use encodedBinary() or encodedXml() instead
2231 [[deprecated("use encodedBinary() or encodedXml() instead")]]
2232 const ByteString* getEncodedBody() const noexcept {
2233 return isEncoded()
2234 ? asWrapper<ByteString>(&handle()->content.encoded.body) // NOLINT
2235 : nullptr;
2236 }
2237
2238 /// Get the decoded data type.
2239 /// Returns `nullptr` if ExtensionObject is not decoded.
2240 const UA_DataType* decodedType() const noexcept {
2241 return isDecoded()
2242 ? handle()->content.decoded.type // NOLINT
2243 : nullptr;
2244 }
2245
2246 /// @deprecated Use decodedType() instead
2247 [[deprecated("use decodedType() instead")]]
2248 const UA_DataType* getDecodedDataType() const noexcept {
2249 return decodedType();
2250 }
2251
2252 /// Get pointer to the decoded data with given template type.
2253 /// Returns `nullptr` if the ExtensionObject is either not decoded or the decoded data is not of
2254 /// type `T`.
2255 template <typename T>
2256 T* decodedData() noexcept {
2257 return isDecodedType<T>() ? static_cast<T*>(decodedData()) : nullptr;
2258 }
2259
2260 /// Get const pointer to the decoded data with given template type.
2261 /// Returns `nullptr` if the ExtensionObject is either not decoded or the decoded data is not of
2262 /// type `T`.
2263 template <typename T>
2264 const T* decodedData() const noexcept {
2265 return isDecodedType<T>() ? static_cast<const T*>(decodedData()) : nullptr;
2266 }
2267
2268 /// @deprecated Use decodedData<T>() instead
2269 template <typename T>
2270 [[deprecated("use decodedData<T>() instead")]]
2271 T* getDecodedData() noexcept {
2272 return decodedData<T>();
2273 }
2274
2275 /// @deprecated Use decodedData<T>() instead
2276 template <typename T>
2277 [[deprecated("use decodedData<T>() instead")]]
2278 const T* getDecodedData() const noexcept {
2279 return decodedData<T>();
2280 }
2281
2282 /// Get pointer to the decoded data.
2283 /// Returns `nullptr` if the ExtensionObject is not decoded.
2284 /// @warning Type erased version, use with caution.
2285 void* decodedData() noexcept {
2286 return isDecoded()
2287 ? handle()->content.decoded.data // NOLINT
2288 : nullptr;
2289 }
2290
2291 /// Get pointer to the decoded data.
2292 /// Returns `nullptr` if the ExtensionObject is not decoded.
2293 /// @warning Type erased version, use with caution.
2294 const void* decodedData() const noexcept {
2295 return isDecoded()
2296 ? handle()->content.decoded.data // NOLINT
2297 : nullptr;
2298 }
2299
2300 /// @deprecated Use decodedData() instead
2301 [[deprecated("use decodedData() instead")]]
2302 void* getDecodedData() noexcept {
2303 return decodedData();
2304 }
2305
2306 /// @deprecated Use decodedData() instead
2307 [[deprecated("use decodedData() instead")]]
2308 const void* getDecodedData() const noexcept {
2309 return decodedData();
2310 }
2311
2312private:
2313 template <typename T>
2314 bool isDecodedType() const noexcept {
2315 const auto* type = decodedType();
2316 return (type != nullptr) && (type->typeId == getDataType<T>().typeId);
2317 }
2318};
2319
2320/* --------------------------------------- DiagnosticInfo --------------------------------------- */
2321
2322/**
2323 * UA_DiagnosticInfo wrapper class.
2324 * @see https://reference.opcfoundation.org/Core/Part4/v105/docs/7.12
2325 * @ingroup Wrapper
2326 */
2327class DiagnosticInfo : public TypeWrapper<UA_DiagnosticInfo, UA_TYPES_DIAGNOSTICINFO> {
2328public:
2330
2331 bool hasSymbolicId() const noexcept {
2332 return handle()->hasSymbolicId;
2333 }
2334
2335 bool hasNamespaceUri() const noexcept {
2336 return handle()->hasNamespaceUri;
2337 }
2338
2339 bool hasLocalizedText() const noexcept {
2340 return handle()->hasLocalizedText;
2341 }
2342
2343 bool hasLocale() const noexcept {
2344 return handle()->hasLocale;
2345 }
2346
2347 bool hasAdditionalInfo() const noexcept {
2348 return handle()->hasAdditionalInfo;
2349 }
2350
2351 bool hasInnerStatusCode() const noexcept {
2352 return handle()->hasInnerStatusCode;
2353 }
2354
2355 bool hasInnerDiagnosticInfo() const noexcept {
2356 return handle()->hasInnerDiagnosticInfo;
2357 }
2358
2359 int32_t symbolicId() const noexcept {
2360 return handle()->symbolicId;
2361 }
2362
2363 /// @deprecated Use symbolicId() instead
2364 [[deprecated("use symbolicId() instead")]]
2365 int32_t getSymbolicId() const noexcept {
2366 return symbolicId();
2367 }
2368
2369 int32_t namespaceUri() const noexcept {
2370 return handle()->namespaceUri;
2371 }
2372
2373 /// @deprecated Use namespaceUri() instead
2374 [[deprecated("use namespaceUri() instead")]]
2375 int32_t getNamespaceUri() const noexcept {
2376 return namespaceUri();
2377 }
2378
2379 int32_t localizedText() const noexcept {
2380 return handle()->localizedText;
2381 }
2382
2383 /// @deprecated Use localizedText() instead
2384 [[deprecated("use localizedText() instead")]]
2385 int32_t getLocalizedText() const noexcept {
2386 return localizedText();
2387 }
2388
2389 int32_t locale() const noexcept {
2390 return handle()->locale;
2391 }
2392
2393 /// @deprecated Use locale() instead
2394 [[deprecated("use locale() instead")]]
2395 int32_t getLocale() const noexcept {
2396 return locale();
2397 }
2398
2399 const String& additionalInfo() const noexcept {
2400 return asWrapper<String>(handle()->additionalInfo);
2401 }
2402
2403 /// @deprecated Use additionalInfo() instead
2404 [[deprecated("use additionalInfo() instead")]]
2405 const String& getAdditionalInfo() const noexcept {
2406 return additionalInfo();
2407 }
2408
2409 StatusCode innerStatusCode() const noexcept {
2410 return handle()->innerStatusCode;
2411 }
2412
2413 /// @deprecated Use innerStatusCode() instead
2414 [[deprecated("use innerStatusCode() instead")]]
2416 return innerStatusCode();
2417 }
2418
2419 const DiagnosticInfo* innerDiagnosticInfo() const noexcept {
2420 return asWrapper<DiagnosticInfo>(handle()->innerDiagnosticInfo);
2421 }
2422
2423 /// @deprecated Use innerDiagnosticInfo() instead
2424 [[deprecated("use innerDiagnosticInfo() instead")]]
2425 const DiagnosticInfo* getInnerDiagnosticInfo() const noexcept {
2426 return innerDiagnosticInfo();
2427 }
2428};
2429
2430/* ---------------------------------------- NumericRange ---------------------------------------- */
2431
2433
2434/// @relates NumericRange
2435inline bool operator==(
2436 const NumericRangeDimension& lhs, const NumericRangeDimension& rhs
2437) noexcept {
2438 return (lhs.min == rhs.min) && (lhs.max == rhs.max);
2439}
2440
2441/// @relates NumericRange
2442inline bool operator!=(
2443 const NumericRangeDimension& lhs, const NumericRangeDimension& rhs
2444) noexcept {
2445 return !(lhs == rhs);
2446}
2447
2448/**
2449 * UA_NumericRange wrapper class.
2450 *
2451 * Numeric ranges indicate subsets of (multidimensional) arrays.
2452 * They are no official data type in the OPC UA standard and are transmitted only with a string
2453 * encoding, such as "1:2,0:3,5". The colon separates min/max index and the comma separates
2454 * dimensions. A single value indicates a range with a single element (min==max).
2455 *
2456 * @see https://reference.opcfoundation.org/Core/Part4/v105/docs/7.27
2457 * @ingroup Wrapper
2458 */
2459class NumericRange : public Wrapper<UA_NumericRange> {
2460public:
2461 NumericRange() = default;
2462
2463 /// Create a NumericRange from the encoded representation, e.g. `1:2,0:3,5`.
2464 explicit NumericRange(std::string_view encodedRange);
2465
2466 /// @overload
2467 explicit NumericRange(const char* encodedRange) // required to avoid ambiguity
2468 : NumericRange(std::string_view(encodedRange)) {}
2469
2470 /// Create a NumericRange from dimensions.
2473
2474 /// Create a NumericRange from native object (copy).
2476 : Wrapper(copy(native)) {}
2477
2478 /// Create a NumericRange from native object (move).
2480 : Wrapper(std::exchange(native, {})) {}
2481
2483 clear();
2484 }
2485
2487 : Wrapper(copy(other.native())) {}
2488
2489 NumericRange(NumericRange&& other) noexcept
2490 : Wrapper(std::exchange(other.native(), {})) {}
2491
2493 if (this != &other) {
2494 clear();
2495 native() = copy(other.native());
2496 }
2497 return *this;
2498 }
2499
2501 if (this != &other) {
2502 clear();
2503 native() = std::exchange(other.native(), {});
2504 }
2505 return *this;
2506 }
2507
2508 bool empty() const noexcept {
2509 return native().dimensionsSize == 0;
2510 }
2511
2515
2516 /// @deprecated Use free function opcua::toString(const T&) instead
2517 [[deprecated("use free function toString instead")]]
2518 std::string toString() const;
2519
2520private:
2521 void clear() noexcept {
2522 detail::deallocateArray(native().dimensions);
2523 native() = {};
2524 }
2525
2526 [[nodiscard]] static UA_NumericRange copy(const UA_NumericRangeDimension* array, size_t size) {
2527 UA_NumericRange result{};
2528 result.dimensions = detail::copyArray(array, size);
2529 result.dimensionsSize = size;
2530 return result;
2531 }
2532
2533 [[nodiscard]] static UA_NumericRange copy(const UA_NumericRange& other) {
2534 return copy(other.dimensions, other.dimensionsSize);
2535 }
2536};
2537
2539
2540/* --------------------------------------- Free functions --------------------------------------- */
2541
2542/**
2543 * Converts an object to its string representation.
2544 *
2545 * This function wraps @ref UA_print to generate a string representation of the given object, based
2546 * on its data type description.
2547 *
2548 * @note
2549 * Requires open62541 v1.2 or later.
2550 * The open62541 library must be compiled with the `UA_ENABLE_TYPEDESCRIPTION` option.
2551 * If these conditions are not met, the function returns an empty String.
2552 *
2553 * @param object Native or wrapper object.
2554 * @param type Data type of `object`.
2555 *
2556 * @relates TypeWrapper
2557 * @ingroup Wrapper
2558 */
2559template <typename T>
2560String toString(const T& object, const UA_DataType& type) {
2561 String output;
2562 if constexpr (UAPP_HAS_TOSTRING) {
2563 throwIfBad(UA_print(&object, &type, output.handle()));
2564 }
2565 return output;
2566}
2567
2568/**
2569 * @overload
2570 * @relates TypeWrapper
2571 * @ingroup Wrapper
2572 */
2573template <typename T, typename = std::enable_if_t<detail::IsRegistered<T>::value>>
2574String toString(const T& object) {
2575 return toString(object, getDataType<T>());
2576}
2577
2578} // namespace opcua
2579
2580/* ---------------------------------- std::hash specializations --------------------------------- */
2581
2582template <>
2583struct std::hash<opcua::NodeId> {
2584 std::size_t operator()(const opcua::NodeId& id) const noexcept {
2585 return id.hash();
2586 }
2587};
2588
2589template <>
2590struct std::hash<opcua::ExpandedNodeId> {
2591 std::size_t operator()(const opcua::ExpandedNodeId& id) const noexcept {
2592 return id.hash();
2593 }
2594};
UA_ByteString wrapper class.
Definition types.hpp:553
std::string_view get() const noexcept
Definition types.hpp:588
ByteString(InputIt first, InputIt last)
Definition types.hpp:568
ByteString(std::string_view str)
Definition types.hpp:557
String toBase64() const
Convert to Base64 encoded string.
ByteString(Span< const uint8_t > bytes)
Definition types.hpp:563
static ByteString fromBase64(std::string_view encoded)
Parse ByteString from Base64 encoded string.
ByteString(const char *str)
Definition types.hpp:560
UA_DataValue wrapper class.
Definition types.hpp:1849
bool hasServerPicoseconds() const noexcept
Definition types.hpp:1959
void setSourceTimestamp(DateTime sourceTimestamp) noexcept
Set source timestamp for the value.
Definition types.hpp:1914
uint16_t serverPicoseconds() const noexcept
Get picoseconds interval added to the server timestamp.
Definition types.hpp:2045
bool hasValue() const noexcept
Definition types.hpp:1943
DateTime getServerTimestamp() const noexcept
Definition types.hpp:2029
DataValue(Variant value) noexcept
Definition types.hpp:1853
const Variant && getValue() const &&noexcept
Definition types.hpp:2007
uint16_t getServerPicoseconds() const noexcept
Definition types.hpp:2051
StatusCode status() const noexcept
Get status.
Definition types.hpp:2056
const Variant && value() const &&noexcept
Get value (rvalue).
Definition types.hpp:1983
DateTime serverTimestamp() const noexcept
Get server timestamp for the value.
Definition types.hpp:2023
bool hasSourceTimestamp() const noexcept
Definition types.hpp:1947
void setServerPicoseconds(uint16_t serverPicoseconds) noexcept
Set picoseconds interval added to the server timestamp.
Definition types.hpp:1932
uint16_t sourcePicoseconds() const noexcept
Get picoseconds interval added to the source timestamp.
Definition types.hpp:2034
const Variant & getValue() const &noexcept
Definition types.hpp:1995
Variant && value() &&noexcept
Get value (rvalue).
Definition types.hpp:1978
bool hasStatus() const noexcept
Definition types.hpp:1963
static DataValue fromScalar(Args &&... args)
Create DataValue from scalar value.
Definition types.hpp:1887
void setValue(Variant &&value) noexcept
Set value (move).
Definition types.hpp:1908
void setServerTimestamp(DateTime serverTimestamp) noexcept
Set server timestamp for the value.
Definition types.hpp:1920
Variant & getValue() &noexcept
Definition types.hpp:1989
static DataValue fromArray(Args &&... args)
Create DataValue from array.
Definition types.hpp:1897
StatusCode getStatus() const noexcept
Definition types.hpp:2062
void setStatus(StatusCode status) noexcept
Set status.
Definition types.hpp:1938
Variant & value() &noexcept
Get value.
Definition types.hpp:1968
void setSourcePicoseconds(uint16_t sourcePicoseconds) noexcept
Set picoseconds interval added to the source timestamp.
Definition types.hpp:1926
bool hasSourcePicoseconds() const noexcept
Definition types.hpp:1955
DateTime sourceTimestamp() const noexcept
Get source timestamp for the value.
Definition types.hpp:2012
uint16_t getSourcePicoseconds() const noexcept
Definition types.hpp:2040
bool hasServerTimestamp() const noexcept
Definition types.hpp:1951
void setValue(const Variant &value)
Set value (copy).
Definition types.hpp:1902
Variant && getValue() &&noexcept
Definition types.hpp:2001
DataValue(Variant value, std::optional< DateTime > sourceTimestamp, std::optional< DateTime > serverTimestamp, std::optional< uint16_t > sourcePicoseconds, std::optional< uint16_t > serverPicoseconds, std::optional< StatusCode > status) noexcept
Definition types.hpp:1857
const Variant & value() const &noexcept
Get value.
Definition types.hpp:1973
DateTime getSourceTimestamp() const noexcept
Definition types.hpp:2018
UA_DateTime wrapper class.
Definition types.hpp:401
int64_t get() const noexcept
Get DateTime value as 100 nanosecond intervals since January 1, 1601 (UTC).
Definition types.hpp:461
UA_DateTimeStruct toStruct() const noexcept
Convert to UA_DateTimeStruct.
Definition types.hpp:456
static DateTime fromUnixTime(int64_t unixTime) noexcept
Get DateTime from Unix time.
Definition types.hpp:427
static DateTime now() noexcept
Get current DateTime.
Definition types.hpp:413
static DateTime fromTimePoint(std::chrono::time_point< Clock, Duration > timePoint)
Get DateTime from std::chrono::time_point.
Definition types.hpp:419
int64_t toUnixTime() const noexcept
Convert to Unix time (number of seconds since January 1, 1970 UTC).
Definition types.hpp:448
static int64_t localTimeUtcOffset() noexcept
Offset of local time to UTC.
Definition types.hpp:432
DateTime(std::chrono::time_point< Clock, Duration > timePoint)
Definition types.hpp:409
std::chrono::duration< int64_t, std::ratio< 1, 10 '000 '000 > > UaDuration
Definition types.hpp:404
std::string format(std::string_view format, bool localtime=false) const
Convert to string with given format (same format codes as strftime).
std::chrono::time_point< Clock, Duration > toTimePoint() const
Convert to std::chrono::time_point.
Definition types.hpp:438
std::chrono::system_clock DefaultClock
Definition types.hpp:403
UA_DiagnosticInfo wrapper class.
Definition types.hpp:2327
bool hasInnerStatusCode() const noexcept
Definition types.hpp:2351
bool hasLocalizedText() const noexcept
Definition types.hpp:2339
int32_t getSymbolicId() const noexcept
Definition types.hpp:2365
int32_t symbolicId() const noexcept
Definition types.hpp:2359
int32_t getNamespaceUri() const noexcept
Definition types.hpp:2375
bool hasSymbolicId() const noexcept
Definition types.hpp:2331
int32_t locale() const noexcept
Definition types.hpp:2389
StatusCode innerStatusCode() const noexcept
Definition types.hpp:2409
const String & additionalInfo() const noexcept
Definition types.hpp:2399
bool hasLocale() const noexcept
Definition types.hpp:2343
bool hasInnerDiagnosticInfo() const noexcept
Definition types.hpp:2355
int32_t getLocalizedText() const noexcept
Definition types.hpp:2385
int32_t getLocale() const noexcept
Definition types.hpp:2395
const DiagnosticInfo * innerDiagnosticInfo() const noexcept
Definition types.hpp:2419
const String & getAdditionalInfo() const noexcept
Definition types.hpp:2405
int32_t namespaceUri() const noexcept
Definition types.hpp:2369
const DiagnosticInfo * getInnerDiagnosticInfo() const noexcept
Definition types.hpp:2425
StatusCode getInnerStatusCode() const noexcept
Definition types.hpp:2415
bool hasNamespaceUri() const noexcept
Definition types.hpp:2335
int32_t localizedText() const noexcept
Definition types.hpp:2379
bool hasAdditionalInfo() const noexcept
Definition types.hpp:2347
UA_ExpandedNodeId wrapper class.
Definition types.hpp:913
static ExpandedNodeId parse(std::string_view str)
Parse ExpandedNodeId from its string representation.
Definition types.hpp:932
bool operator<=(const UA_ExpandedNodeId &lhs, const UA_ExpandedNodeId &rhs) noexcept
Definition types.hpp:1013
uint32_t hash() const noexcept
Definition types.hpp:943
ExpandedNodeId(NodeId id) noexcept
Definition types.hpp:917
const NodeId & getNodeId() const noexcept
Definition types.hpp:963
ExpandedNodeId(NodeId id, std::string_view namespaceUri, uint32_t serverIndex)
Definition types.hpp:921
bool operator>=(const UA_ExpandedNodeId &lhs, const UA_ExpandedNodeId &rhs) noexcept
Definition types.hpp:1018
bool isLocal() const noexcept
Definition types.hpp:939
bool operator!=(const UA_ExpandedNodeId &lhs, const UA_ExpandedNodeId &rhs) noexcept
Definition types.hpp:998
uint32_t serverIndex() const noexcept
Definition types.hpp:977
std::string toString() const
bool operator<(const UA_ExpandedNodeId &lhs, const UA_ExpandedNodeId &rhs) noexcept
Definition types.hpp:1003
std::string_view namespaceUri() const
Definition types.hpp:967
uint32_t getServerIndex() const noexcept
Definition types.hpp:983
const NodeId & nodeId() const noexcept
Definition types.hpp:951
NodeId & nodeId() noexcept
Definition types.hpp:947
NodeId & getNodeId() noexcept
Definition types.hpp:957
bool operator>(const UA_ExpandedNodeId &lhs, const UA_ExpandedNodeId &rhs) noexcept
Definition types.hpp:1008
std::string_view getNamespaceUri() const
Definition types.hpp:973
bool operator==(const UA_ExpandedNodeId &lhs, const UA_ExpandedNodeId &rhs) noexcept
Definition types.hpp:993
UA_ExtensionObject wrapper class.
Definition types.hpp:2094
bool isDecoded() const noexcept
Check if the ExtensionObject is decoded.
Definition types.hpp:2184
bool isEmpty() const noexcept
Definition types.hpp:2173
const UA_DataType * getDecodedDataType() const noexcept
Definition types.hpp:2248
ExtensionObject(T *ptr, const UA_DataType &type) noexcept
Create ExtensionObject from a pointer to a decoded object with a custom data type (no copy).
Definition types.hpp:2120
const UA_DataType * decodedType() const noexcept
Get the decoded data type.
Definition types.hpp:2240
const XmlElement * encodedXml() const noexcept
Get the encoded body in XML format.
Definition types.hpp:2224
static ExtensionObject fromDecoded(T &data, Args &&... args) noexcept
Definition types.hpp:2155
T * decodedData() noexcept
Get pointer to the decoded data with given template type.
Definition types.hpp:2256
ExtensionObject(T *ptr) noexcept
Create ExtensionObject from a pointer to a decoded object (no copy).
Definition types.hpp:2110
T * getDecodedData() noexcept
Definition types.hpp:2271
bool empty() const noexcept
Check if the ExtensionObject is empty.
Definition types.hpp:2167
const ByteString * getEncodedBody() const noexcept
Definition types.hpp:2232
ExtensionObject(const T &decoded)
Create ExtensionObject from a decoded object (copy).
Definition types.hpp:2135
const NodeId * encodedTypeId() const noexcept
Get the encoded type id.
Definition types.hpp:2202
const T * getDecodedData() const noexcept
Definition types.hpp:2278
const NodeId * getEncodedTypeId() const noexcept
Definition types.hpp:2210
void * decodedData() noexcept
Get pointer to the decoded data.
Definition types.hpp:2285
const T * decodedData() const noexcept
Get const pointer to the decoded data with given template type.
Definition types.hpp:2264
ExtensionObjectEncoding getEncoding() const noexcept
Definition types.hpp:2196
void * getDecodedData() noexcept
Definition types.hpp:2302
bool isEncoded() const noexcept
Check if the ExtensionObject is encoded (usually if the data type is unknown).
Definition types.hpp:2178
ExtensionObject(const T &decoded, const UA_DataType &type)
Create ExtensionObject from a decoded object with a custom data type (copy).
Definition types.hpp:2144
const ByteString * encodedBinary() const noexcept
Get the encoded body in binary format.
Definition types.hpp:2216
ExtensionObjectEncoding encoding() const noexcept
Get the encoding.
Definition types.hpp:2190
const void * getDecodedData() const noexcept
Definition types.hpp:2308
const void * decodedData() const noexcept
Get pointer to the decoded data.
Definition types.hpp:2294
static ExtensionObject fromDecodedCopy(const T &data, Args &&... args)
Definition types.hpp:2162
UA_Guid wrapper class.
Definition types.hpp:491
Guid(uint32_t data1, uint16_t data2, uint16_t data3, std::array< uint8_t, 8 > data4) noexcept
Definition types.hpp:507
Guid(std::array< uint8_t, 16 > data) noexcept
Definition types.hpp:495
bool operator==(const UA_Guid &lhs, const UA_Guid &rhs) noexcept
Definition types.hpp:536
static Guid parse(std::string_view str)
Parse Guid from its string representation.
Definition types.hpp:523
bool operator!=(const UA_Guid &lhs, const UA_Guid &rhs) noexcept
Definition types.hpp:541
std::string toString() const
static Guid random() noexcept
Generate random Guid.
Definition types.hpp:516
UA_LocalizedText wrapper class.
Definition types.hpp:1079
bool operator==(const UA_LocalizedText &lhs, const UA_LocalizedText &rhs) noexcept
Definition types.hpp:1110
std::string_view text() const noexcept
Definition types.hpp:1098
std::string_view locale() const noexcept
Definition types.hpp:1088
std::string_view getLocale() const noexcept
Definition types.hpp:1094
std::string_view getText() const noexcept
Definition types.hpp:1104
bool operator!=(const UA_LocalizedText &lhs, const UA_LocalizedText &rhs) noexcept
Definition types.hpp:1115
LocalizedText(std::string_view locale, std::string_view text)
Definition types.hpp:1083
UA_NodeId wrapper class.
Definition types.hpp:665
const T & identifier() const
Get identifier reference.
Definition types.hpp:807
std::string toString() const
NodeId(T identifier) noexcept
Create NodeId from enum class with numeric identifiers like opcua::ObjectId.
Definition types.hpp:703
NodeIdType identifierType() const noexcept
Definition types.hpp:735
static NodeId parse(std::string_view str)
Parse NodeId from its string representation.
Definition types.hpp:710
NamespaceIndex namespaceIndex() const noexcept
Definition types.hpp:725
NodeId(NamespaceIndex namespaceIndex, uint32_t identifier) noexcept
Create NodeId with numeric identifier.
Definition types.hpp:670
NodeId(NamespaceIndex namespaceIndex, ByteString identifier) noexcept
Create NodeId with ByteString identifier.
Definition types.hpp:691
bool operator!=(const UA_NodeId &lhs, const UA_NodeId &rhs) noexcept
Definition types.hpp:882
std::variant< uint32_t, String, Guid, ByteString > getIdentifier() const
Get identifier variant.
Definition types.hpp:817
bool operator<(const UA_NodeId &lhs, const UA_NodeId &rhs) noexcept
Definition types.hpp:887
bool operator>(const UA_NodeId &lhs, const UA_NodeId &rhs) noexcept
Definition types.hpp:892
NodeId(NamespaceIndex namespaceIndex, Guid identifier) noexcept
Create NodeId with Guid identifier.
Definition types.hpp:684
const T * identifierIf() const noexcept
Get identifier pointer or nullptr on error.
Definition types.hpp:762
auto getIdentifierAs() const
Get identifier by template type.
Definition types.hpp:825
bool operator==(const UA_NodeId &lhs, const UA_NodeId &rhs) noexcept
Definition types.hpp:877
bool isNull() const noexcept
Definition types.hpp:717
bool operator>=(const UA_NodeId &lhs, const UA_NodeId &rhs) noexcept
Definition types.hpp:902
bool operator<=(const UA_NodeId &lhs, const UA_NodeId &rhs) noexcept
Definition types.hpp:897
T * identifierIf() noexcept
Get identifier pointer or nullptr on error.
Definition types.hpp:756
NamespaceIndex getNamespaceIndex() const noexcept
Definition types.hpp:731
NodeIdType getIdentifierType() const noexcept
Definition types.hpp:741
T & identifier()
Get identifier reference.
Definition types.hpp:801
uint32_t hash() const noexcept
Definition types.hpp:721
NodeId(NamespaceIndex namespaceIndex, std::string_view identifier)
Create NodeId with String identifier.
Definition types.hpp:677
UA_NumericRange wrapper class.
Definition types.hpp:2459
bool empty() const noexcept
Definition types.hpp:2508
NumericRange(UA_NumericRange &&native) noexcept
Create a NumericRange from native object (move).
Definition types.hpp:2479
NumericRange(std::string_view encodedRange)
Create a NumericRange from the encoded representation, e.g. 1:2,0:3,5.
bool operator!=(const NumericRangeDimension &lhs, const NumericRangeDimension &rhs) noexcept
Definition types.hpp:2442
NumericRange(const char *encodedRange)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition types.hpp:2467
NumericRange(Span< const NumericRangeDimension > dimensions)
Create a NumericRange from dimensions.
Definition types.hpp:2471
Span< const NumericRangeDimension > dimensions() const noexcept
Definition types.hpp:2512
NumericRange(const NumericRange &other)
Definition types.hpp:2486
bool operator==(const NumericRangeDimension &lhs, const NumericRangeDimension &rhs) noexcept
Definition types.hpp:2435
NumericRange()=default
NumericRange(NumericRange &&other) noexcept
Definition types.hpp:2489
NumericRange(const UA_NumericRange &native)
Create a NumericRange from native object (copy).
Definition types.hpp:2475
std::string toString() const
NumericRange & operator=(const NumericRange &other)
Definition types.hpp:2492
NumericRange & operator=(NumericRange &&other) noexcept
Definition types.hpp:2500
UA_QualifiedName wrapper class.
Definition types.hpp:1028
bool operator!=(const UA_QualifiedName &lhs, const UA_QualifiedName &rhs) noexcept
Definition types.hpp:1064
NamespaceIndex getNamespaceIndex() const noexcept
Definition types.hpp:1043
bool operator==(const UA_QualifiedName &lhs, const UA_QualifiedName &rhs) noexcept
Definition types.hpp:1059
NamespaceIndex namespaceIndex() const noexcept
Definition types.hpp:1037
QualifiedName(NamespaceIndex namespaceIndex, std::string_view name)
Definition types.hpp:1032
std::string_view name() const noexcept
Definition types.hpp:1047
std::string_view getName() const noexcept
Definition types.hpp:1053
View to a contiguous sequence of objects, similar to std::span in C++20.
Definition span.hpp:30
constexpr iterator begin() const noexcept
Definition span.hpp:133
constexpr iterator end() const noexcept
Definition span.hpp:137
UA_StatusCode wrapper class.
Definition types.hpp:47
constexpr void throwIfBad() const
Throw a BadStatus exception if the status code is bad.
Definition types.hpp:85
constexpr bool isUncertain() const noexcept
Check if the status code is uncertain.
Definition types.hpp:74
constexpr bool isBad() const noexcept
Check if the status code is bad.
Definition types.hpp:79
std::string_view name() const noexcept
Get human-readable name of the StatusCode.
Definition types.hpp:64
constexpr bool isGood() const noexcept
Check if the status code is good.
Definition types.hpp:69
constexpr UA_StatusCode get() const noexcept
Explicitly get underlying UA_StatusCode.
Definition types.hpp:56
constexpr StatusCode() noexcept=default
Create a StatusCode with the default status code UA_STATUSCODE_GOOD.
UA_String wrapper class.
Definition types.hpp:257
String & operator=(std::basic_string_view< char, Traits > str)
Assign std::string_view.
Definition types.hpp:281
String(std::initializer_list< char > values)
Definition types.hpp:269
bool operator!=(const String &lhs, const String &rhs) noexcept
Definition types.hpp:315
bool operator==(std::string_view lhs, const String &rhs) noexcept
Definition types.hpp:330
std::string_view get() const noexcept
Definition types.hpp:294
bool operator==(const String &lhs, std::string_view rhs) noexcept
Definition types.hpp:320
String(InputIt first, InputIt last)
Definition types.hpp:265
bool operator==(const String &lhs, const String &rhs) noexcept
Definition types.hpp:310
String & operator=(const char *str)
Assign null-termined character string.
Definition types.hpp:274
bool operator!=(const String &lhs, std::string_view rhs) noexcept
Definition types.hpp:325
bool operator!=(std::string_view lhs, const String &rhs) noexcept
Definition types.hpp:335
String(std::string_view str)
Definition types.hpp:261
std::ostream & operator<<(std::ostream &os, const String &str)
bool operator!=(const UA_String &lhs, const UA_String &rhs) noexcept
Definition types.hpp:305
bool operator==(const UA_String &lhs, const UA_String &rhs) noexcept
Definition types.hpp:300
Exception for type-related errors.
Definition exception.hpp:45
Template base class to wrap UA_* type objects that require manual memory management.
constexpr TypeWrapper() noexcept=default
UA_Variant wrapper class.
Definition types.hpp:1193
Variant(InputIt first, InputIt last, const UA_DataType &type)
Create Variant from a range of elements with a custom data type (copy).
Definition types.hpp:1241
T getScalarCopy() const
Definition types.hpp:1590
Span< T > getArray()
Definition types.hpp:1617
bool isType(const UA_DataType &type) const noexcept
Check if the variant type is equal to the provided data type.
Definition types.hpp:1476
const void * data() const noexcept
Get pointer to the underlying data.
Definition types.hpp:1538
void assign(InputIt first, InputIt last)
Assign range to variant (copy and convert if required).
Definition types.hpp:1366
Variant(InputIt first, InputIt last)
Create Variant from a range of elements (copy).
Definition types.hpp:1234
void assign(InputIt first, InputIt last, const UA_DataType &type)
Assign range to variant with custom data type (copy).
Definition types.hpp:1384
T && scalar() &&
Get reference to scalar value with given template type (only native or wrapper types).
Definition types.hpp:1563
Variant(T *ptr, const UA_DataType &type) noexcept
Create Variant from a pointer to a scalar/array with a custom data type (no copy).
Definition types.hpp:1213
const UA_DataType * getDataType() const noexcept
Definition types.hpp:1498
const T & scalar() const &
Get reference to scalar value with given template type (only native or wrapper types).
Definition types.hpp:1554
Variant & operator=(T *value) noexcept
Assign pointer to scalar/array to variant (no copy).
Definition types.hpp:1391
const T && scalar() const &&
Get reference to scalar value with given template type (only native or wrapper types).
Definition types.hpp:1569
void setScalar(T &value, Args &&... args) noexcept
Definition types.hpp:1407
Span< const uint32_t > getArrayDimensions() const noexcept
Definition types.hpp:1520
size_t getArrayLength() const noexcept
Definition types.hpp:1509
Span< T > array()
Get reference to array with given template type (only native or wrapper types).
Definition types.hpp:1597
static Variant fromArray(T &&array, Args &&... args)
Definition types.hpp:1259
void assign(const T &value)
Assign scalar/array to variant (copy and convert if required).
Definition types.hpp:1329
static Variant fromArray(InputIt first, InputIt last, Args &&... args)
Definition types.hpp:1270
T & scalar() &
Get reference to scalar value with given template type (only native or wrapper types).
Definition types.hpp:1545
std::vector< T > getArrayCopy() const
Definition types.hpp:1631
void setArrayCopy(InputIt first, InputIt last, Args &&... args)
Definition types.hpp:1435
bool isType(const UA_DataType *type) const noexcept
Check if the variant type is equal to the provided data type.
Definition types.hpp:1469
T to() const
Converts the variant to the specified type T with automatic conversion if required.
Definition types.hpp:1659
void assign(T *ptr) noexcept
Assign pointer to scalar/array to variant (no copy).
Definition types.hpp:1294
bool isType(const NodeId &id) const noexcept
Check if the variant type is equal to the provided data type node id.
Definition types.hpp:1481
Span< const T > getArray() const
Definition types.hpp:1624
bool empty() const noexcept
Check if the variant is empty.
Definition types.hpp:1445
static Variant fromScalar(T &&value, Args &&... args)
Definition types.hpp:1248
Variant & operator=(const T &value)
Assign scalar/array to variant (copy and convert if required).
Definition types.hpp:1399
void setScalarCopy(const T &value, Args &&... args)
Definition types.hpp:1414
void setArray(T &array, Args &&... args) noexcept
Definition types.hpp:1421
size_t arrayLength() const noexcept
Get array length or 0 if variant is not an array.
Definition types.hpp:1503
const UA_DataType * type() const noexcept
Get data type.
Definition types.hpp:1492
bool isEmpty() const noexcept
Definition types.hpp:1451
Variant(T *ptr) noexcept
Create Variant from a pointer to a scalar/array (no copy).
Definition types.hpp:1206
Variant(const T &value)
Create Variant from a scalar/array (copy).
Definition types.hpp:1220
void assign(std::nullptr_t ptr, const UA_DataType &type) noexcept=delete
Span< const uint32_t > arrayDimensions() const noexcept
Get array dimensions.
Definition types.hpp:1514
bool isArray() const noexcept
Check if the variant is an array.
Definition types.hpp:1464
void setArrayCopy(const T &array, Args &&... args)
Definition types.hpp:1428
Variant(const T &value, const UA_DataType &type)
Create Variant from a scalar/array with a custom data type (copy).
Definition types.hpp:1227
Span< const T > array() const
Get reference to array with given template type (only native or wrapper types).
Definition types.hpp:1607
T & getScalar()
Definition types.hpp:1576
void assign(std::nullptr_t ptr) noexcept=delete
void assign(T *ptr, const UA_DataType &type) noexcept
Assign pointer to scalar/array to variant with custom data type (no copy).
Definition types.hpp:1311
bool isScalar() const noexcept
Check if the variant is a scalar.
Definition types.hpp:1456
void assign(const T &value, const UA_DataType &type)
Assign scalar/array to variant with custom data type (copy).
Definition types.hpp:1351
bool isType() const noexcept
Check if the variant type is equal to the provided template type.
Definition types.hpp:1487
void * data() noexcept
Get pointer to the underlying data.
Definition types.hpp:1533
const T & getScalar() const
Definition types.hpp:1583
Template base class to wrap native objects.
Definition wrapper.hpp:33
constexpr const UA_StatusCode & native() const noexcept
Definition wrapper.hpp:102
constexpr T * handle() noexcept
Return pointer to native object.
Definition wrapper.hpp:82
constexpr Wrapper() noexcept=default
UA_XmlElement wrapper class.
Definition types.hpp:601
std::string_view get() const noexcept
Definition types.hpp:634
XmlElement(std::string_view str)
Definition types.hpp:605
XmlElement & operator=(std::basic_string_view< char, Traits > str)
Assign std::string_view.
Definition types.hpp:621
XmlElement(InputIt first, InputIt last)
Definition types.hpp:609
XmlElement & operator=(const char *str)
Assign null-termined character string.
Definition types.hpp:614
UA_ORDER_LESS
UA_ORDER_MORE
constexpr Namespace namespaceOf(DataTypeId) noexcept
Get namespace of DataTypeId.
Definition nodeids.hpp:448
String toString(const T &object)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition types.hpp:2574
String toString(const T &object, const UA_DataType &type)
Converts an object to its string representation.
Definition types.hpp:2560
static UA_LogCategory const char va_list args
uint16_t NamespaceIndex
Namespace index.
Definition common.hpp:12
NodeIdType
NodeId types.
Definition types.hpp:653
ExtensionObjectEncoding
Extension object encoding.
Definition types.hpp:2073
VariantPolicy
Policies for variant factory methods Variant::fromScalar, Variant::fromArray.
Definition types.hpp:1124
@ Copy
Store copy of scalar/array inside the variant.
@ Reference
Store reference to scalar/array inside the variant.
String toString(const NumericRange &range)
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
const UA_DataType & getDataType() noexcept
UA_NodeId typeId
UA_NumericRangeDimension * dimensions
size_t dimensionsSize
static void toNative(const Type &src, NativeType &dst)
Definition types.hpp:385
static void toNative(Type src, NativeType &dst)
Definition types.hpp:375
std::basic_string< char, Traits, Allocator > Type
Definition types.hpp:358
static void fromNative(const NativeType &src, Type &dst)
Definition types.hpp:361
static void toNative(const Type &src, NativeType &dst)
Definition types.hpp:365
std::basic_string_view< char, Traits > Type
Definition types.hpp:344
static void toNative(Type src, NativeType &dst)
Definition types.hpp:351
static void fromNative(const NativeType &src, Type &dst)
Definition types.hpp:347
std::chrono::time_point< Clock, Duration > Type
Definition types.hpp:472
static void fromNative(const NativeType &src, Type &dst)
Definition types.hpp:475
static void toNative(const Type &src, NativeType &dst)
Definition types.hpp:479
Type conversion from and to native types.
std::size_t operator()(const opcua::ExpandedNodeId &id) const noexcept
Definition types.hpp:2591
std::size_t operator()(const opcua::NodeId &id) const noexcept
Definition types.hpp:2584
UA_NODEIDTYPE_STRING
UA_NODEIDTYPE_GUID
UA_NODEIDTYPE_BYTESTRING
UA_NODEIDTYPE_NUMERIC
UA_UInt32 UA_NodeId_hash(const UA_NodeId *n)
UA_EXTENSIONOBJECT_ENCODED_XML
UA_EXTENSIONOBJECT_ENCODED_BYTESTRING
UA_EXTENSIONOBJECT_ENCODED_NOBODY
UA_EXTENSIONOBJECT_DECODED
UA_EXTENSIONOBJECT_DECODED_NODELETE
UA_Boolean UA_String_equal(const UA_String *s1, const UA_String *s2)
#define UA_EMPTY_ARRAY_SENTINEL
UA_Guid UA_Guid_random(void)
UA_UInt32 UA_ExpandedNodeId_hash(const UA_ExpandedNodeId *n)
UA_Order UA_NodeId_order(const UA_NodeId *n1, const UA_NodeId *n2)
UA_StatusCode UA_print(const void *p, const UA_DataType *type, UA_String *output)
#define UA_DATETIME_UNIX_EPOCH
UA_DateTimeStruct UA_DateTime_toStruct(UA_DateTime t)
UA_Boolean UA_NodeId_isNull(const UA_NodeId *p)
int64_t UA_DateTime
UA_EXPORT const char * UA_StatusCode_name(UA_StatusCode code)
uint32_t UA_StatusCode
UA_VariantStorageType
UA_VARIANT_DATA_NODELETE
UA_Order UA_ExpandedNodeId_order(const UA_ExpandedNodeId *n1, const UA_ExpandedNodeId *n2)
UA_DateTime UA_DateTime_now(void)
UA_Boolean UA_Guid_equal(const UA_Guid *g1, const UA_Guid *g2)
UA_Int64 UA_DateTime_localTimeUtcOffset(void)
#define UA_malloc
UA_StatusCode status