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