From a54c3210faa50b123e0d5b1768617c9a6126a756 Mon Sep 17 00:00:00 2001 From: Andreas Ringlstetter Date: Wed, 30 Jul 2025 13:21:37 +0200 Subject: [PATCH 1/6] Make Iterable interface compatible with Container/LegacyIterator requirements --- include/dbcppp/Iterator.h | 99 +++++++++++++++++++++++---------------- 1 file changed, 58 insertions(+), 41 deletions(-) diff --git a/include/dbcppp/Iterator.h b/include/dbcppp/Iterator.h index 916761be..46312dfc 100644 --- a/include/dbcppp/Iterator.h +++ b/include/dbcppp/Iterator.h @@ -2,101 +2,118 @@ #pragma once #include +#include namespace dbcppp { - template - class Iterator + template + class Iterator final { public: - using self_t = Iterator; + using self_t = Iterator; using iterator_category = std::random_access_iterator_tag; using difference_type = std::ptrdiff_t; using value_type = T; - using pointer = const value_type*; - using reference = const value_type&; - using get_t = std::function; + using pointer = value_type*; + using reference = value_type&; - Iterator(get_t get, std::size_t i) - : _get(get) + constexpr Iterator(P parent, F get, std::size_t i) noexcept + : _parent(parent) + , _get(std::move(get)) , _i(i) {} - reference operator*() const + inline reference operator*() const { - return _get(_i); + return (_parent->*_get)(_i); } - pointer operator->() const + inline pointer operator->() const { - return &_get(_i); + return &(_parent->*_get)(_i); } - self_t& operator++() + constexpr self_t& operator++() noexcept { _i++; return *this; } - self_t operator+(std::size_t o) + constexpr self_t operator+(std::size_t o) const noexcept { - return {_get, _i + o}; + return {_parent, _get, _i + o}; } - self_t operator-(std::size_t o) + constexpr self_t operator-(std::size_t o) const noexcept { - return {_get, _i + o}; + return {_parent, _get, _i - o}; } - difference_type operator-(const self_t& rhs) + constexpr difference_type operator-(const self_t& rhs) const noexcept { return _i - rhs._i; } - self_t& operator+=(std::size_t o) + constexpr self_t& operator+=(std::size_t o) noexcept { _i += o; return *this; } - self_t& operator-=(std::size_t o) + constexpr self_t& operator-=(std::size_t o) noexcept { _i -= o; return *this; } - bool operator==(const self_t& rhs) const + constexpr bool operator==(const self_t& rhs) const noexcept { return _i == rhs._i; } - bool operator!=(const self_t& rhs) const + constexpr bool operator!=(const self_t& rhs) const noexcept { return !(*this == rhs); } private: - get_t _get; + P _parent; + F _get; std::size_t _i; }; - template - class Iterable + template + class Iterable final { public: - Iterable(Iterator begin, Iterator end) - : _begin(begin) - , _end(end) + using value_type = const T; + using reference = value_type&; + using const_reference = const value_type&; + using iterator = Iterator; + using const_iterator = Iterator; + using difference_type = std::ptrdiff_t; + using size_type = std::size_t; + + constexpr Iterable(P parent, F get, size_t size) noexcept + : _parent(std::move(parent)) + , _get(std::move(get)) + , _size(std::move(size)) {} - Iterator& begin() + constexpr iterator begin() noexcept + { + return iterator(_parent, _get, 0); + } + constexpr iterator end() noexcept + { + return iterator(_parent, _get, _size); + } + constexpr const_iterator begin() const noexcept { - return _begin; + return const_iterator(_parent, _get, 0); } - Iterator& end() + constexpr const_iterator end() const noexcept { - return _end; + return const_iterator(_parent, _get, _size); } private: - Iterator _begin; - Iterator _end; + P _parent; + F _get; + size_t _size; }; } -#define DBCPPP_MAKE_ITERABLE(ClassName, Name, Type) \ - auto Name() const \ - { \ - auto get = std::bind(&ClassName::Name##_Get, this, std::placeholders::_1); \ - Iterator begin(get, 0); \ - Iterator end(get, Name##_Size()); \ - return Iterable(begin, end); \ +#define DBCPPP_MAKE_ITERABLE(ClassName, Name, Type) \ + inline Iterable Name() const noexcept \ + { \ + return {this, &ClassName::Name##_Get, Name##_Size()}; \ } From d6b632fd5f3499d1d0df1830dcfe3aee34081394 Mon Sep 17 00:00:00 2001 From: Andreas Ringlstetter Date: Wed, 30 Jul 2025 13:30:41 +0200 Subject: [PATCH 2/6] Extend ISignal::EMultiplexer, validation for advanced multiplexing, documentation --- include/dbcppp/Message.h | 3 ++ include/dbcppp/Signal.h | 28 +++++++++-- include/dbcppp/SignalMultiplexerValue.h | 2 +- src/DBCAST2Network.cpp | 18 ++++--- src/MessageImpl.cpp | 66 +++++++++++++++++++++---- src/Network2DBC.cpp | 15 ++++-- src/SignalImpl.cpp | 20 +++++++- 7 files changed, 125 insertions(+), 27 deletions(-) diff --git a/include/dbcppp/Message.h b/include/dbcppp/Message.h index bbdeef01..b1228e9b 100644 --- a/include/dbcppp/Message.h +++ b/include/dbcppp/Message.h @@ -50,6 +50,9 @@ namespace dbcppp virtual const std::string& Comment() const = 0; virtual const ISignalGroup& SignalGroups_Get(std::size_t i) const = 0; virtual uint64_t SignalGroups_Size() const = 0; + /// \brief Optional multiplexor signal when this message is using simple multiplexing with a single switch. + /// + /// For advanced multiplexing support, ISignal::SignalMultiplexerValues needs to be followed instead. virtual const ISignal* MuxSignal() const = 0; DBCPPP_MAKE_ITERABLE(IMessage, MessageTransmitters, std::string); diff --git a/include/dbcppp/Signal.h b/include/dbcppp/Signal.h index 7097a847..d1f6b0da 100644 --- a/include/dbcppp/Signal.h +++ b/include/dbcppp/Signal.h @@ -25,11 +25,17 @@ namespace dbcppp MaschinesFloatEncodingNotSupported = 1, MaschinesDoubleEncodingNotSupported = 2, SignalExceedsMessageSize = 4, - WrongBitSizeForExtendedDataType = 8 + WrongBitSizeForExtendedDataType = 8, + ConflictingMultiplexDefinition = 16, }; enum class EMultiplexer { - NoMux, MuxSwitch, MuxValue + NoMux, + MuxSwitch = 1, + MuxValue = 2, + // For advanced multiplexing with ISignalMultiplexerValue, nesting is possible. + // This results in signals being both a multiplexor switch and a multiplexed value. + MuxSwitchAndValue = MuxSwitch | MuxValue }; enum class EByteOrder { @@ -44,7 +50,7 @@ namespace dbcppp { Integer, Float, Double }; - + static std::unique_ptr Create( uint64_t message_size , std::string&& name @@ -65,13 +71,21 @@ namespace dbcppp , std::string&& comment , EExtendedValueType extended_value_type , std::vector>&& signal_multiplexer_values); - + virtual std::unique_ptr Clone() const = 0; virtual ~ISignal() = default; virtual const std::string& Name() const = 0; virtual EMultiplexer MultiplexerIndicator() const = 0; + /// \brief Single multiplexor value for simple multiplexing. + /// + /// Invalid if ISignal::SignalMultiplexerValues is not empty. virtual uint64_t MultiplexerSwitchValue() const = 0; + /// \brief Least significant bit of the signal. + /// + /// \note DBC uses the least signficant bit as start bit for both big and little endian. + /// This means that for big endian, the StartBit points into the highest byte. + /// This differs from FIBEX/Autosar where big endian signals are instead using the least significant bit in the lowest byte as offset. virtual uint64_t StartBit() const = 0; virtual uint64_t BitSize() const = 0; virtual EByteOrder ByteOrder() const = 0; @@ -121,6 +135,12 @@ namespace dbcppp DBCPPP_MAKE_ITERABLE(ISignal, Receivers, std::string); DBCPPP_MAKE_ITERABLE(ISignal, ValueEncodingDescriptions, IValueEncodingDescription); DBCPPP_MAKE_ITERABLE(ISignal, AttributeValues, IAttribute); + + /// \brief Mapping of this multiplexed signal to specific value ranges of a selected multiplexor switch signal. + /// + /// In a valid DBC, this can only have 0 or 1 entries. + /// Requires EMultiplexer::MuxValue to be set in MultiplexerIndicator in order to be valid. + /// If empty, simple multiplexing rules by ISignal::MultiplexerSwitchValue and IMessage::MuxSignal apply instead. DBCPPP_MAKE_ITERABLE(ISignal, SignalMultiplexerValues, ISignalMultiplexerValue); protected: diff --git a/include/dbcppp/SignalMultiplexerValue.h b/include/dbcppp/SignalMultiplexerValue.h index 27790766..763c8108 100644 --- a/include/dbcppp/SignalMultiplexerValue.h +++ b/include/dbcppp/SignalMultiplexerValue.h @@ -6,12 +6,12 @@ #include #include #include -#include #include "Iterator.h" namespace dbcppp { + // Map from values of the multiplexor signal ISignalMultiplexerValue::SwitchName to the parent multiplexed ISignal. class ISignalMultiplexerValue { public: diff --git a/src/DBCAST2Network.cpp b/src/DBCAST2Network.cpp index b3aed055..33b302e2 100644 --- a/src/DBCAST2Network.cpp +++ b/src/DBCAST2Network.cpp @@ -351,16 +351,20 @@ static auto getSignals(const G_Network& gnet, const G_Message& m, Cache const& c uint64_t multiplexer_switch_value = 0; if (s.multiplexer_indicator) { - auto m = *s.multiplexer_indicator; - if (m.substr(0, 1) == "M") + const auto& m = *s.multiplexer_indicator; + if (auto mux_value_pos = m.find('m', 0); mux_value_pos != std::string::npos) { - multiplexer_indicator = ISignal::EMultiplexer::MuxSwitch; + multiplexer_indicator = ISignal::EMultiplexer( + std::underlying_type_t(multiplexer_indicator) | + std::underlying_type_t(ISignal::EMultiplexer::MuxValue)); + multiplexer_switch_value = std::atoi(m.c_str() + mux_value_pos + 1); } - else + if (m.find('M', 0) != std::string::npos) { - multiplexer_indicator = ISignal::EMultiplexer::MuxValue; - std::string value = m.substr(1, m.size()); - multiplexer_switch_value = std::atoi(value.c_str()); + multiplexer_indicator = + ISignal::EMultiplexer( + std::underlying_type_t(multiplexer_indicator) | + std::underlying_type_t(ISignal::EMultiplexer::MuxSwitch)); } } diff --git a/src/MessageImpl.cpp b/src/MessageImpl.cpp index 8ed225d6..c04e3548 100644 --- a/src/MessageImpl.cpp +++ b/src/MessageImpl.cpp @@ -67,22 +67,68 @@ MessageImpl::MessageImpl( , _mux_signal(nullptr) , _error(EErrorCode::NoError) { - bool have_mux_value = false; + bool have_mux_values = false; + size_t num_mux_switches = 0; + bool have_mux_values_without_advanced_mapping = false; for (const auto& sig : _signals) { - switch (sig.MultiplexerIndicator()) + if (std::underlying_type_t(sig.MultiplexerIndicator()) & + std::underlying_type_t(ISignal::EMultiplexer::MuxValue)) { - case ISignal::EMultiplexer::MuxValue: - have_mux_value = true; - break; - case ISignal::EMultiplexer::MuxSwitch: - _mux_signal = &sig; - break; + have_mux_values = true; + if (sig.SignalMultiplexerValues_Size() == 0) + { + have_mux_values_without_advanced_mapping = true; + } + else + { + for (const auto& multiplexor : sig.SignalMultiplexerValues()) + { + if (!std::any_of(_signals.begin(), _signals.end(), [&multiplexor](const SignalImpl& sig) + { return sig.Name() == multiplexor.SwitchName(); })) + { + _error = EErrorCode::MuxValeWithoutMuxSignal; + } + } + } + } + if (std::underlying_type_t(sig.MultiplexerIndicator()) & + std::underlying_type_t(ISignal::EMultiplexer::MuxSwitch)) + { + ++num_mux_switches; } } - if (have_mux_value && _mux_signal == nullptr) + if (num_mux_switches > 1) { - _error = EErrorCode::MuxValeWithoutMuxSignal; + if (have_mux_values_without_advanced_mapping) + { + _error = EErrorCode::MuxValeWithoutMuxSignal; + } + } + // MuxSignal property can be populated for simple multiplexing only. + else if (num_mux_switches == 1) + { + for (const auto& sig : _signals) + { + if (uint64_t(sig.MultiplexerIndicator()) & uint64_t(ISignal::EMultiplexer::MuxSwitch)) + { + _mux_signal = &sig; + } + } + for (const auto& sig : _signals) + { + if (uint64_t(sig.MultiplexerIndicator()) & uint64_t(ISignal::EMultiplexer::MuxValue)) + { + _mux_signal = &sig; + } + } + } + else if (num_mux_switches == 0) + { + if (have_mux_values) + { + _error = EErrorCode::MuxValeWithoutMuxSignal; + } } } MessageImpl::MessageImpl(const MessageImpl& other) diff --git a/src/Network2DBC.cpp b/src/Network2DBC.cpp index 62f32d82..8f8a8455 100644 --- a/src/Network2DBC.cpp +++ b/src/Network2DBC.cpp @@ -499,10 +499,19 @@ DBCPPP_API std::ostream& dbcppp::Network2DBC::operator<<(std::ostream& os, const DBCPPP_API std::ostream& dbcppp::Network2DBC::operator<<(std::ostream& os, const ISignal& s) { os << "\tSG_ " << s.Name() << " "; - switch (s.MultiplexerIndicator()) + if (s.MultiplexerIndicator() != ISignal::EMultiplexer::NoMux) { - case ISignal::EMultiplexer::MuxSwitch: os << "M "; break; - case ISignal::EMultiplexer::MuxValue: os << "m" << s.MultiplexerSwitchValue() << " "; break; + if (std::underlying_type_t(s.MultiplexerIndicator()) & + std::underlying_type_t(ISignal::EMultiplexer::MuxValue)) + { + os << "m" << s.MultiplexerSwitchValue(); + } + if (std::underlying_type_t(s.MultiplexerIndicator()) & + std::underlying_type_t(ISignal::EMultiplexer::MuxSwitch)) + { + os << "M"; + } + os << " "; } os << ": " << s.StartBit() << "|" << s.BitSize() << "@"; switch (s.ByteOrder()) diff --git a/src/SignalImpl.cpp b/src/SignalImpl.cpp index b7633953..c9670959 100644 --- a/src/SignalImpl.cpp +++ b/src/SignalImpl.cpp @@ -517,6 +517,22 @@ SignalImpl::SignalImpl( _phys_to_raw = ::phys_to_raw; break; } + // Permitted due to faulty interface design in ISignal only - doesn't happen with real DBC files. + if (_signal_multiplexer_values.size() > 1) + { + SetError(EErrorCode::ConflictingMultiplexDefinition); + } + else if (_signal_multiplexer_values.size() == 1) + { + const auto ranges = _signal_multiplexer_values[0].ValueRanges(); + if (!std::any_of(std::begin(ranges), std::end(ranges), [&](const ISignalMultiplexerValue::Range& range) -> bool + { + return range.from <= _multiplexer_switch_value && range.to >= _multiplexer_switch_value; + })) + { + SetError(EErrorCode::ConflictingMultiplexDefinition); + } + } } std::unique_ptr SignalImpl::Clone() const { @@ -612,11 +628,11 @@ uint64_t SignalImpl::SignalMultiplexerValues_Size() const } bool SignalImpl::Error(EErrorCode code) const { - return code == _error || (uint64_t(_error) & uint64_t(code)); + return code == _error || (std::underlying_type_t(_error) & std::underlying_type_t(code)); } void SignalImpl::SetError(EErrorCode code) { - _error = EErrorCode(uint64_t(_error) | uint64_t(code)); + _error = EErrorCode(std::underlying_type_t(_error) | std::underlying_type_t(code)); } bool SignalImpl::operator==(const ISignal& rhs) const { From 038333c609512b33cae1fcd0bedf24bda03f150c Mon Sep 17 00:00:00 2001 From: Andreas Ringlstetter Date: Wed, 30 Jul 2025 13:47:23 +0200 Subject: [PATCH 3/6] Correct includes for Iterator header --- include/dbcppp/Iterator.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/dbcppp/Iterator.h b/include/dbcppp/Iterator.h index 46312dfc..a8b5b24b 100644 --- a/include/dbcppp/Iterator.h +++ b/include/dbcppp/Iterator.h @@ -1,8 +1,6 @@ - #pragma once -#include -#include +#include namespace dbcppp { From 73741f7b93d54466e81434e733a54525555133ee Mon Sep 17 00:00:00 2001 From: Andreas Ringlstetter Date: Wed, 30 Jul 2025 14:40:43 +0200 Subject: [PATCH 4/6] Fixup meaning of having multiple SignalMultiplexerValues --- include/dbcppp/Signal.h | 7 +++---- src/SignalImpl.cpp | 16 ---------------- 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/include/dbcppp/Signal.h b/include/dbcppp/Signal.h index d1f6b0da..514ef18e 100644 --- a/include/dbcppp/Signal.h +++ b/include/dbcppp/Signal.h @@ -25,8 +25,7 @@ namespace dbcppp MaschinesFloatEncodingNotSupported = 1, MaschinesDoubleEncodingNotSupported = 2, SignalExceedsMessageSize = 4, - WrongBitSizeForExtendedDataType = 8, - ConflictingMultiplexDefinition = 16, + WrongBitSizeForExtendedDataType = 8 }; enum class EMultiplexer { @@ -136,11 +135,11 @@ namespace dbcppp DBCPPP_MAKE_ITERABLE(ISignal, ValueEncodingDescriptions, IValueEncodingDescription); DBCPPP_MAKE_ITERABLE(ISignal, AttributeValues, IAttribute); - /// \brief Mapping of this multiplexed signal to specific value ranges of a selected multiplexor switch signal. + /// \brief Mapping of this multiplexed signal to specific value ranges of a selected multiplexor switch signals. /// - /// In a valid DBC, this can only have 0 or 1 entries. /// Requires EMultiplexer::MuxValue to be set in MultiplexerIndicator in order to be valid. /// If empty, simple multiplexing rules by ISignal::MultiplexerSwitchValue and IMessage::MuxSignal apply instead. + /// If not empty, all listed multiplexor signals must match the specified value ranges simultaniously. DBCPPP_MAKE_ITERABLE(ISignal, SignalMultiplexerValues, ISignalMultiplexerValue); protected: diff --git a/src/SignalImpl.cpp b/src/SignalImpl.cpp index c9670959..706962db 100644 --- a/src/SignalImpl.cpp +++ b/src/SignalImpl.cpp @@ -517,22 +517,6 @@ SignalImpl::SignalImpl( _phys_to_raw = ::phys_to_raw; break; } - // Permitted due to faulty interface design in ISignal only - doesn't happen with real DBC files. - if (_signal_multiplexer_values.size() > 1) - { - SetError(EErrorCode::ConflictingMultiplexDefinition); - } - else if (_signal_multiplexer_values.size() == 1) - { - const auto ranges = _signal_multiplexer_values[0].ValueRanges(); - if (!std::any_of(std::begin(ranges), std::end(ranges), [&](const ISignalMultiplexerValue::Range& range) -> bool - { - return range.from <= _multiplexer_switch_value && range.to >= _multiplexer_switch_value; - })) - { - SetError(EErrorCode::ConflictingMultiplexDefinition); - } - } } std::unique_ptr SignalImpl::Clone() const { From c30e675600d5a10c3216b3d71894064ebfddd67c Mon Sep 17 00:00:00 2001 From: Andreas Ringlstetter Date: Wed, 30 Jul 2025 15:27:59 +0200 Subject: [PATCH 5/6] Complete Iterator interface --- include/dbcppp/Iterator.h | 63 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/include/dbcppp/Iterator.h b/include/dbcppp/Iterator.h index a8b5b24b..8ad64689 100644 --- a/include/dbcppp/Iterator.h +++ b/include/dbcppp/Iterator.h @@ -28,11 +28,32 @@ namespace dbcppp { return &(_parent->*_get)(_i); } + inline reference operator[](std::size_t o) const + { + return *(*this + o); + } constexpr self_t& operator++() noexcept { - _i++; + ++_i; + return *this; + } + constexpr self_t& operator--() noexcept + { + --_i; return *this; } + constexpr self_t operator++(int) noexcept + { + self_t old = *this; + ++_i; + return std::move(old); + } + constexpr self_t operator--(int) noexcept + { + self_t old = *this; + --_i; + return std::move(old); + } constexpr self_t operator+(std::size_t o) const noexcept { return {_parent, _get, _i + o}; @@ -64,6 +85,22 @@ namespace dbcppp { return !(*this == rhs); } + constexpr bool operator<(const self_t& rhs) const noexcept + { + return _i < rhs._i; + } + constexpr bool operator>(const self_t& rhs) const noexcept + { + return _i > rhs._i; + } + constexpr bool operator<=(const self_t& rhs) const noexcept + { + return _i <= rhs._i; + } + constexpr bool operator>=(const self_t& rhs) const noexcept + { + return _i >= rhs._i; + } private: P _parent; @@ -87,6 +124,10 @@ namespace dbcppp , _get(std::move(get)) , _size(std::move(size)) {} + inline reference operator[](std::size_t o) const + { + return (_parent->*_get)(o); + } constexpr iterator begin() noexcept { return iterator(_parent, _get, 0); @@ -95,14 +136,30 @@ namespace dbcppp { return iterator(_parent, _get, _size); } - constexpr const_iterator begin() const noexcept + constexpr const_iterator cbegin() const noexcept { return const_iterator(_parent, _get, 0); } - constexpr const_iterator end() const noexcept + constexpr const_iterator cend() const noexcept { return const_iterator(_parent, _get, _size); } + constexpr const_iterator begin() const noexcept + { + return cbegin(); + } + constexpr const_iterator end() const noexcept + { + return cend(); + } + constexpr size_type size() const noexcept + { + return _size; + } + constexpr bool empty() const noexcept + { + return _size == 0; + } private: P _parent; From a45701b64e762872eb705d430dc6128c07aa7457 Mon Sep 17 00:00:00 2001 From: Andreas Ringlstetter Date: Thu, 31 Jul 2025 08:14:19 +0200 Subject: [PATCH 6/6] Fix unsigned type in iterator interface breaking reverse iteraors --- include/dbcppp/Iterator.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/include/dbcppp/Iterator.h b/include/dbcppp/Iterator.h index 8ad64689..6cffc91c 100644 --- a/include/dbcppp/Iterator.h +++ b/include/dbcppp/Iterator.h @@ -15,20 +15,20 @@ namespace dbcppp using pointer = value_type*; using reference = value_type&; - constexpr Iterator(P parent, F get, std::size_t i) noexcept + constexpr Iterator(P parent, F get, difference_type i) noexcept : _parent(parent) , _get(std::move(get)) , _i(i) {} inline reference operator*() const { - return (_parent->*_get)(_i); + return (_parent->*_get)(static_cast(_i)); } inline pointer operator->() const { - return &(_parent->*_get)(_i); + return &(_parent->*_get)(static_cast(_i)); } - inline reference operator[](std::size_t o) const + inline reference operator[](difference_type o) const { return *(*this + o); } @@ -54,11 +54,11 @@ namespace dbcppp --_i; return std::move(old); } - constexpr self_t operator+(std::size_t o) const noexcept + constexpr self_t operator+(difference_type o) const noexcept { return {_parent, _get, _i + o}; } - constexpr self_t operator-(std::size_t o) const noexcept + constexpr self_t operator-(difference_type o) const noexcept { return {_parent, _get, _i - o}; } @@ -66,12 +66,12 @@ namespace dbcppp { return _i - rhs._i; } - constexpr self_t& operator+=(std::size_t o) noexcept + constexpr self_t& operator+=(difference_type o) noexcept { _i += o; return *this; } - constexpr self_t& operator-=(std::size_t o) noexcept + constexpr self_t& operator-=(difference_type o) noexcept { _i -= o; return *this; @@ -105,7 +105,7 @@ namespace dbcppp private: P _parent; F _get; - std::size_t _i; + difference_type _i; }; template class Iterable final @@ -142,7 +142,7 @@ namespace dbcppp } constexpr const_iterator cend() const noexcept { - return const_iterator(_parent, _get, _size); + return const_iterator(_parent, _get, static_cast(_size)); } constexpr const_iterator begin() const noexcept {