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