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