COMMS
Template library intended to help with implementation of communication protocols.
Loading...
Searching...
No Matches
ArrayList.h
1//
2// Copyright 2015 - 2025 (C). Alex Robenko. All rights reserved.
3//
4// This Source Code Form is subject to the terms of the Mozilla Public
5// License, v. 2.0. If a copy of the MPL was not distributed with this
6// file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
8#pragma once
9
10#include "comms/Assert.h"
12#include "comms/details/detect.h"
13#include "comms/details/tag.h"
14#include "comms/ErrorStatus.h"
15#include "comms/field/basic/CommonFuncs.h"
16#include "comms/field/details/VersionStorage.h"
17#include "comms/field/tag.h"
18#include "comms/util/access.h"
19#include "comms/util/assign.h"
20#include "comms/util/detect.h"
21#include "comms/util/MaxSizeOf.h"
25
26#include <algorithm>
27#include <limits>
28#include <numeric>
29#include <type_traits>
30#include <utility>
31
32namespace comms
33{
34
35namespace field
36{
37
38namespace basic
39{
40
41namespace details
42{
43
44template <typename TStorage>
45struct ArrayListMaxLengthRetrieveHelper
46{
47 static const std::size_t Value = CommonFuncs::maxSupportedLength();
48};
49
50template <typename T, std::size_t TSize>
51struct ArrayListMaxLengthRetrieveHelper<comms::util::StaticVector<T, TSize> >
52{
53 static const std::size_t Value = TSize;
54};
55
56template <std::size_t TSize>
57struct ArrayListMaxLengthRetrieveHelper<comms::util::StaticString<TSize> >
58{
59 static const std::size_t Value = TSize - 1;
60};
61
62template <typename TElem>
63using ArrayListFieldHasVarLengthBoolType =
64 typename comms::util::LazyDeepConditional<
65 std::is_integral<TElem>::value
66 >::template Type<
67 comms::util::FalseType,
68 comms::util::FieldCheckVarLength,
69 TElem
70 >;
71
72template <typename TElem>
73using HasArrayListElemNonDefaultRefreshBoolType =
74 typename comms::util::LazyDeepConditional<
75 std::is_integral<TElem>::value
76 >::template Type<
77 comms::util::FalseType,
78 comms::util::FieldCheckNonDefaultRefresh,
79 TElem
80 >;
81
82template <typename TElem>
83using IsArrayListElemVersionDependentBoolType =
84 typename comms::util::LazyDeepConditional<
85 std::is_integral<TElem>::value
86 >::template Type<
87 comms::util::FalseType,
88 comms::util::FieldCheckVersionDependent,
89 TElem
90 >;
91
92template <typename TFieldBase, typename TStorage>
93using ArrayListVersionStorageBase =
94 typename comms::util::LazyShallowConditional<
95 IsArrayListElemVersionDependentBoolType<typename TStorage::value_type>::value
96 >::template Type<
97 comms::field::details::VersionStorage,
98 comms::util::EmptyStruct,
99 typename TFieldBase::VersionType
100 >;
101
102} // namespace details
103
104template <typename TFieldBase, typename TStorage>
105class ArrayList :
106 public TFieldBase,
107 public details::ArrayListVersionStorageBase<TFieldBase, TStorage>
108{
109 using BaseImpl = TFieldBase;
110 using VersionBaseImpl = details::ArrayListVersionStorageBase<TFieldBase, TStorage>;
111
112public:
113 using Endian = typename BaseImpl::Endian;
114 using VersionType = typename BaseImpl::VersionType;
115 using ElementType = typename TStorage::value_type;
116 using ValueType = TStorage;
117 using CommsTag =
119 std::is_integral<ElementType>::value
120 >::template Type<
121 comms::field::tag::RawArrayList,
122 comms::field::tag::ArrayList
123 >;
124
125 ArrayList() = default;
126
127 explicit ArrayList(const ValueType& val)
128 : m_value(val)
129 {
130 }
131
132 explicit ArrayList(ValueType&& val)
133 : m_value(std::move(val))
134 {
135 }
136
137 ArrayList(const ArrayList&) = default;
138 ArrayList(ArrayList&&) = default;
139 ArrayList& operator=(const ArrayList&) = default;
140 ArrayList& operator=(ArrayList&&) = default;
141 ~ArrayList() noexcept = default;
142
143 const ValueType& value() const
144 {
145 return m_value;
146 }
147
148 ValueType& value()
149 {
150 return m_value;
151 }
152
153 const ValueType& getValue() const
154 {
155 return value();
156 }
157
158 template <typename T>
159 void setValue(T&& val)
160 {
161 value() = std::forward<T>(val);
162 }
163
164 ElementType& createBack()
165 {
166 COMMS_ASSERT(m_value.size() < m_value.max_size());
167 m_value.emplace_back();
168 updateElemVersion(m_value.back(), VersionTag<>());
169 return m_value.back();
170 }
171
172 void clear()
173 {
174 static_assert(comms::util::detect::hasClearFunc<ValueType>(),
175 "The used storage type for ArrayList must have clear() member function");
176
177 m_value.clear();
178 }
179
180 constexpr std::size_t length() const
181 {
182 return lengthInternal(ElemTag<>());
183 }
184
185 static constexpr std::size_t minLength()
186 {
187 return 0U;
188 }
189
190 static constexpr std::size_t maxLength()
191 {
192 return
193 details::ArrayListMaxLengthRetrieveHelper<TStorage>::Value *
194 maxLengthInternal(ElemTag<>());
195 }
196
197 constexpr bool valid() const
198 {
199 return validInternal(ElemTag<>());
200 }
201
202 bool refresh()
203 {
204 return refreshInternal(ElemTag<>());
205 }
206
207 static constexpr std::size_t minElementLength()
208 {
209 return minElemLengthInternal(ElemTag<>());
210 }
211
212 static constexpr std::size_t maxElementLength()
213 {
214 return maxElemLengthInternal(ElemTag<>());
215 }
216
217 static constexpr std::size_t elementLength(const ElementType& elem)
218 {
219 return elementLengthInternal(elem, ElemTag<>());
220 }
221
222 template <typename TIter>
223 static ErrorStatus readElement(ElementType& elem, TIter& iter, std::size_t& len)
224 {
225 return readElementInternal(elem, iter, len, ElemTag<>());
226 }
227
228 template <typename TIter>
229 static void readElementNoStatus(ElementType& elem, TIter& iter)
230 {
231 return readElementNoStatusInternal(elem, iter, ElemTag<>());
232 }
233
234 template <typename TIter>
235 ErrorStatus read(TIter& iter, std::size_t len)
236 {
237 using IterType = typename std::decay<decltype(iter)>::type;
238 using IterCategory =
239 typename std::iterator_traits<IterType>::iterator_category;
240 static const bool IsRandomAccessIter =
241 std::is_base_of<std::random_access_iterator_tag, IterCategory>::value;
242 static const bool IsRawData =
243 std::is_integral<ElementType>::value && (sizeof(ElementType) == sizeof(std::uint8_t));
244
245 using Tag =
246 typename comms::util::LazyShallowConditional<
247 IsRandomAccessIter && IsRawData
248 >::template Type<
249 RawDataTag,
250 FieldElemTag
251 >;
252 return readInternal(iter, len, Tag());
253 }
254
255 static constexpr bool hasReadNoStatus()
256 {
257 return false;
258 }
259
260 template <typename TIter>
261 void readNoStatus(TIter& iter) = delete;
262
263 template <typename TIter>
264 ErrorStatus readN(std::size_t count, TIter& iter, std::size_t& len)
265 {
266 using IterType = typename std::decay<decltype(iter)>::type;
267 using IterCategory =
268 typename std::iterator_traits<IterType>::iterator_category;
269 static const bool IsRandomAccessIter =
270 std::is_base_of<std::random_access_iterator_tag, IterCategory>::value;
271 static const bool IsRawData =
272 std::is_integral<ElementType>::value && (sizeof(ElementType) == sizeof(std::uint8_t));
273
274 using Tag =
275 typename comms::util::LazyShallowConditional<
276 IsRandomAccessIter && IsRawData
277 >::template Type<
278 RawDataTag,
279 FieldElemTag
280 >;
281
282 return readInternalN(count, iter, len, Tag());
283 }
284
285 template <typename TIter>
286 void readNoStatusN(std::size_t count, TIter& iter)
287 {
288 using IterType = typename std::decay<decltype(iter)>::type;
289 using IterCategory =
290 typename std::iterator_traits<IterType>::iterator_category;
291 static const bool IsRandomAccessIter =
292 std::is_base_of<std::random_access_iterator_tag, IterCategory>::value;
293 static const bool IsRawData =
294 std::is_integral<ElementType>::value && (sizeof(ElementType) == sizeof(std::uint8_t));
295
296 using Tag =
297 typename comms::util::LazyShallowConditional<
298 IsRandomAccessIter && IsRawData
299 >::template Type<
300 RawDataTag,
301 FieldElemTag
302 >;
303
304 return readNoStatusInternalN(count, iter, Tag());
305 }
306
307 static bool canWriteElement(const ElementType& elem)
308 {
309 return canWriteElementInternal(elem, ElemTag<>());
310 }
311
312 template <typename TIter>
313 static ErrorStatus writeElement(const ElementType& elem, TIter& iter, std::size_t& len)
314 {
315 return writeElementInternal(elem, iter, len, ElemTag<>());
316 }
317
318 template <typename TIter>
319 static void writeElementNoStatus(const ElementType& elem, TIter& iter)
320 {
321 return writeElementNoStatusInternal(elem, iter, ElemTag<>());
322 }
323
324 bool canWrite() const
325 {
326 return CommonFuncs::canWriteSequence(*this);
327 }
328
329 template <typename TIter>
330 ErrorStatus write(TIter& iter, std::size_t len) const
331 {
332 return CommonFuncs::writeSequence(*this, iter, len);
333 }
334
335 static constexpr bool hasWriteNoStatus()
336 {
337 return hasWriteNoStatusInternal(ElemTag<>());
338 }
339
340 template <typename TIter>
341 void writeNoStatus(TIter& iter) const
342 {
343 CommonFuncs::writeSequenceNoStatus(*this, iter);
344 }
345
346 template <typename TIter>
347 ErrorStatus writeN(std::size_t count, TIter& iter, std::size_t& len) const
348 {
349 return CommonFuncs::writeSequenceN(*this, count, iter, len);
350 }
351
352 template <typename TIter>
353 void writeNoStatusN(std::size_t count, TIter& iter) const
354 {
355 CommonFuncs::writeSequenceNoStatusN(*this, count, iter);
356 }
357
358 static constexpr bool isVersionDependent()
359 {
360 return details::IsArrayListElemVersionDependentBoolType<ElementType>::value;
361 }
362
363 static constexpr bool hasNonDefaultRefresh()
364 {
365 return details::HasArrayListElemNonDefaultRefreshBoolType<ElementType>::value;
366 }
367
368 bool setVersion(VersionType version)
369 {
370 return setVersionInternal(version, VersionTag<>());
371 }
372
373private:
374 template <typename... TParams>
375 using FieldElemTag = comms::details::tag::Tag1<>;
376
377 template <typename... TParams>
378 using IntegralElemTag = comms::details::tag::Tag2<>;
379
380 template <typename... TParams>
381 using FixedLengthTag = comms::details::tag::Tag3<>;
382
383 template <typename... TParams>
384 using VarLengthTag = comms::details::tag::Tag4<>;
385
386 template <typename... TParams>
387 using RawDataTag = comms::details::tag::Tag5<>;
388
389 template <typename... TParams>
390 using VersionDependentTag = comms::details::tag::Tag6<>;
391
392 template <typename... TParams>
393 using NoVersionDependencyTag = comms::details::tag::Tag7<>;
394
395 template <typename... TParams>
396 using ElemTag =
398 std::is_integral<ElementType>::value
399 >::template Type<
400 IntegralElemTag<TParams...>,
401 FieldElemTag<TParams...>
402 >;
403
404 template <typename... TParams>
405 using VersionTag =
407 details::IsArrayListElemVersionDependentBoolType<ElementType>::value
408 >::template Type<
409 VersionDependentTag<TParams...>,
410 NoVersionDependencyTag<TParams...>
411 >;
412
413 template <typename... TParams>
414 constexpr std::size_t lengthInternal(FieldElemTag<TParams...>) const
415 {
416 using Tag =
417 typename comms::util::LazyShallowConditional<
418 details::ArrayListFieldHasVarLengthBoolType<ElementType>::value
419 >::template Type<
420 VarLengthTag,
421 FixedLengthTag
422 >;
423
424 return fieldLength(Tag());
425 }
426
427 template <typename... TParams>
428 constexpr std::size_t lengthInternal(IntegralElemTag<TParams...>) const
429 {
430 return m_value.size() * sizeof(ElementType);
431 }
432
433 template <typename... TParams>
434 constexpr std::size_t fieldLength(FixedLengthTag<TParams...>) const
435 {
436 return ElementType().length() * m_value.size();
437 }
438
439 template <typename... TParams>
440 std::size_t fieldLength(VarLengthTag<TParams...>) const
441 {
442 return
443 std::accumulate(m_value.begin(), m_value.end(), std::size_t(0),
444 [](std::size_t sum, typename ValueType::const_reference e) -> std::size_t
445 {
446 return sum + e.length();
447 });
448 }
449
450 template <typename... TParams>
451 static constexpr std::size_t maxLengthInternal(FieldElemTag<TParams...>)
452 {
453 return ElementType::maxLength();
454 }
455
456 template <typename... TParams>
457 static constexpr std::size_t maxLengthInternal(IntegralElemTag<TParams...>)
458 {
459 return sizeof(ElementType);
460 }
461
462 template <typename TIter>
463 static ErrorStatus readFieldElement(ElementType& elem, TIter& iter, std::size_t& len)
464 {
465 auto fromIter = iter;
466 auto es = elem.read(iter, len);
467 if (es == ErrorStatus::Success) {
468 auto diff = static_cast<std::size_t>(std::distance(fromIter, iter));
469 COMMS_ASSERT(diff <= len);
470 len -= diff;
471 }
472 return es;
473 }
474
475 template <typename TIter>
476 static ErrorStatus readIntegralElement(ElementType& elem, TIter& iter, std::size_t& len)
477 {
478 if (len < sizeof(ElementType)) {
480 }
481
482 elem = comms::util::readData<ElementType>(iter, Endian());
483 len -= sizeof(ElementType);
485 }
486
487 template <typename TIter, typename... TParams>
488 static ErrorStatus readElementInternal(ElementType& elem, TIter& iter, std::size_t& len, FieldElemTag<TParams...>)
489 {
490 return readFieldElement(elem, iter, len);
491 }
492
493 template <typename TIter, typename... TParams>
494 static ErrorStatus readElementInternal(ElementType& elem, TIter& iter, std::size_t& len, IntegralElemTag<TParams...>)
495 {
496 return readIntegralElement(elem, iter, len);
497 }
498
499 template <typename TIter>
500 static void readNoStatusFieldElement(ElementType& elem, TIter& iter)
501 {
502 elem.readNoStatus(iter);
503 }
504
505 template <typename TIter>
506 static void readNoStatusIntegralElement(ElementType& elem, TIter& iter)
507 {
508 elem = comms::util::readData<ElementType>(iter, Endian());
509 }
510
511 template <typename TIter, typename... TParams>
512 static void readElementNoStatusInternal(ElementType& elem, TIter& iter, FieldElemTag<TParams...>)
513 {
514 readNoStatusFieldElement(elem, iter);
515 }
516
517 template <typename TIter, typename... TParams>
518 static void readElementNoStatusInternal(ElementType& elem, TIter& iter, IntegralElemTag<TParams...>)
519 {
520 readElementNoStatusInternal(elem, iter);
521 }
522
523 template <typename TIter>
524 static ErrorStatus writeFieldElement(const ElementType& elem, TIter& iter, std::size_t& len)
525 {
526 auto es = elem.write(iter, len);
527 if (es == ErrorStatus::Success) {
528 len -= elem.length();
529 }
530 return es;
531 }
532
533 template <typename TIter>
534 static ErrorStatus writeIntegralElement(const ElementType& elem, TIter& iter, std::size_t& len)
535 {
536 if (len < sizeof(ElementType)) {
538 }
539
540 BaseImpl::writeData(elem, iter);
541 len -= sizeof(ElementType);
543 }
544
545 template <typename TIter, typename... TParams>
546 static ErrorStatus writeElementInternal(const ElementType& elem, TIter& iter, std::size_t& len, FieldElemTag<TParams...>)
547 {
548 return writeFieldElement(elem, iter, len);
549 }
550
551 template <typename TIter, typename... TParams>
552 static ErrorStatus writeElementInternal(const ElementType& elem, TIter& iter, std::size_t& len, IntegralElemTag<TParams...>)
553 {
554 return writeIntegralElement(elem, iter, len);
555 }
556
557 template <typename TIter>
558 static void writeNoStatusFieldElement(const ElementType& elem, TIter& iter)
559 {
560 elem.writeNoStatus(iter);
561 }
562
563 template <typename TIter>
564 static void writeNoStatusIntegralElement(const ElementType& elem, TIter& iter)
565 {
566 BaseImpl::writeData(elem, iter);
567 }
568
569 template <typename TIter, typename... TParams>
570 static void writeElementNoStatusInternal(const ElementType& elem, TIter& iter, FieldElemTag<TParams...>)
571 {
572 return writeNoStatusFieldElement(elem, iter);
573 }
574
575 template <typename TIter, typename... TParams>
576 static void writeElementNoStatusInternal(const ElementType& elem, TIter& iter, IntegralElemTag<TParams...>)
577 {
578 return writeNoStatusIntegralElement(elem, iter);
579 }
580
581 template <typename... TParams>
582 constexpr bool validInternal(FieldElemTag<TParams...>) const
583 {
584 return std::all_of(
585 m_value.begin(), m_value.end(),
586 [](const ElementType& e) -> bool
587 {
588 return e.valid();
589 });
590 }
591
592 template <typename... TParams>
593 static constexpr bool validInternal(IntegralElemTag<TParams...>)
594 {
595 return true;
596 }
597
598 template <typename... TParams>
599 bool refreshInternal(FieldElemTag<TParams...>)
600 {
601 return
602 std::accumulate(
603 m_value.begin(), m_value.end(), false,
604 [](bool prev, typename ValueType::reference elem) -> bool
605 {
606 return elem.refresh() || prev;
607 });
608 }
609
610 template <typename... TParams>
611 static constexpr bool refreshInternal(IntegralElemTag<TParams...>)
612 {
613 return false;
614 }
615
616 template <typename... TParams>
617 static constexpr std::size_t minElemLengthInternal(IntegralElemTag<TParams...>)
618 {
619 return sizeof(ElementType);
620 }
621
622 template <typename... TParams>
623 static constexpr std::size_t minElemLengthInternal(FieldElemTag<TParams...>)
624 {
625 return ElementType::minLength();
626 }
627
628 template <typename... TParams>
629 static constexpr std::size_t maxElemLengthInternal(IntegralElemTag<TParams...>)
630 {
631 return sizeof(ElementType);
632 }
633
634 template <typename... TParams>
635 static constexpr std::size_t maxElemLengthInternal(FieldElemTag<TParams...>)
636 {
637 return ElementType::maxLength();
638 }
639
640 template <typename... TParams>
641 static constexpr std::size_t elementLengthInternal(const ElementType&, IntegralElemTag<TParams...>)
642 {
643 return sizeof(ElementType);
644 }
645
646 template <typename... TParams>
647 static constexpr std::size_t elementLengthInternal(const ElementType& elem, FieldElemTag<TParams...>)
648 {
649 return elem.length();
650 }
651
652 template <typename TIter, typename... TParams>
653 ErrorStatus readInternal(TIter& iter, std::size_t len, FieldElemTag<TParams...>)
654 {
655 static_assert(comms::util::detect::hasClearFunc<ValueType>(),
656 "The used storage type for ArrayList must have clear() member function");
657 m_value.clear();
658 auto remLen = len;
659 while (0 < remLen) {
660 auto es = createAndReadNextElementInternal(iter, remLen);
661 if (es != ErrorStatus::Success) {
662 return es;
663 }
664 }
665
667 }
668
669 template <typename TIter, typename... TParams>
670 ErrorStatus readInternal(TIter& iter, std::size_t len, RawDataTag<TParams...>)
671 {
672 comms::util::assign(value(), iter, iter + std::min(len, comms::util::maxSizeOf(value())));
673 std::advance(iter, len);
675 }
676
677 template <typename TIter, typename... TParams>
678 ErrorStatus readInternalN(std::size_t count, TIter& iter, std::size_t len, FieldElemTag<TParams...>)
679 {
680 clear();
681 while (0 < count) {
682 auto es = createAndReadNextElementInternal(iter, len);
683 if (es != ErrorStatus::Success) {
684 return es;
685 }
686
687 --count;
688 }
689
691 }
692
693 template <typename TIter, typename... TParams>
694 ErrorStatus readInternalN(std::size_t count, TIter& iter, std::size_t len, RawDataTag<TParams...>)
695 {
696 if (len < count) {
698 }
699
700 return readInternal(iter, count, RawDataTag<>());
701 }
702
703 template <typename TIter, typename... TParams>
704 void readNoStatusInternalN(std::size_t count, TIter& iter, FieldElemTag<TParams...>)
705 {
706 clear();
707 while (0 < count) {
708 if (m_value.size() < m_value.max_size()) {
709 auto& elem = createBack();
710 readElementNoStatus(elem, iter);
711 }
712 else {
713 ElementType elem;
714 readElementNoStatus(elem, iter);
715 }
716 --count;
717 }
718 }
719
720 template <typename TIter, typename... TParams>
721 void readNoStatusInternalN(std::size_t count, TIter& iter, RawDataTag<TParams...>)
722 {
723 readInternal(iter, count, RawDataTag<>());
724 }
725
726 template <typename... TParams>
727 bool updateElemVersion(ElementType& elem, VersionDependentTag<TParams...>)
728 {
729 return elem.setVersion(VersionBaseImpl::m_version);
730 }
731
732 template <typename... TParams>
733 static constexpr bool updateElemVersion(ElementType&, NoVersionDependencyTag<TParams...>)
734 {
735 return false;
736 }
737
738 template <typename... TParams>
739 bool setVersionInternal(VersionType version, VersionDependentTag<TParams...>)
740 {
741 VersionBaseImpl::m_version = version;
742 bool updated = false;
743 for (auto& elem : value()) {
744 updated = elem.setVersion(version) || updated;
745 }
746
747 return updated;
748 }
749
750 template <typename... TParams>
751 static constexpr bool setVersionInternal(VersionType, NoVersionDependencyTag<TParams...>)
752 {
753 return false;
754 }
755
756 template <typename... TParams>
757 static bool canWriteElementInternal(const ElementType& elem, FieldElemTag<TParams...>)
758 {
759 return elem.canWrite();
760 }
761
762 template <typename... TParams>
763 static bool canWriteElementInternal(const ElementType& elem, IntegralElemTag<TParams...>)
764 {
765 static_cast<void>(elem);
766 return true;
767 }
768
769 template <typename... TParams>
770 static constexpr bool hasWriteNoStatusInternal(FieldElemTag<TParams...>)
771 {
772 return ElementType::hasWriteNoStatus();
773 }
774
775 template <typename... TParams>
776 static constexpr bool hasWriteNoStatusInternal(IntegralElemTag<TParams...>)
777 {
778 return true;
779 }
780
781 template <typename TIter>
782 comms::ErrorStatus createAndReadNextElementInternal(TIter& iter, std::size_t& len)
783 {
784 if (m_value.size() < m_value.max_size()) {
785 auto& elem = createBack();
786 auto es = readElement(elem, iter, len);
787 if (es != ErrorStatus::Success) {
788 m_value.pop_back();
789 }
790
791 return es;
792 }
793
794 ElementType elem;
795 return readElement(elem, iter, len);
796 }
797
798 ValueType m_value;
799};
800
801} // namespace basic
802
803} // namespace field
804
805} // namespace comms
806
807
This file contains classes required for generic custom assertion functionality.
#define COMMS_ASSERT(expr)
Generic assert macro.
Definition Assert.h:170
Contains various compiler related definitions.
This file contain definition of error statuses used by comms module.
Contains comms::util::StaticString class.
Contains comms::util::StaticVector class.
Contains functions for raw data access / (de)serialization.
Provides helper assign() function to allow easy assignment of values to collections or views.
Contains definition of various tag classes.
comms::option::def::VersionType< T > VersionType
Same as comms::option::def::VersionType.
Definition options.h:1917
comms::option::def::Endian< TEndian > Endian
Same as comms::option::def::Endian.
Definition options.h:1460
void assign(T &obj, TIter from, TIter to)
Assigns a new value to provided object.
Definition assign.h:39
Main namespace for all classes / functions of COMMS library.
ErrorStatus
Error statuses reported by the Communication module.
Definition ErrorStatus.h:17
@ Success
Used to indicate successful outcome of the operation.
constexpr unsigned version()
Version of the COMMS library as single numeric value.
Definition version.h:64
STL namespace.
Replacement to std::conditional.
Definition type_traits.h:29
Replacement to some types from standard type_traits.
Various compile-time detection functions of whether specific member functions and/or types exist.