diff --git a/common/adc_traits.h b/common/adc_traits.h index a2c67fd..57ec882 100644 --- a/common/adc_traits.h +++ b/common/adc_traits.h @@ -46,6 +46,10 @@ template concept adc_char_view = std::ranges::view && std::same_as, char>; +template +concept adc_range_of_view_char_range = std::ranges::range && std::ranges::view> && + std::same_as>, char>; + // deduce returned type of callable // template // using adc_retval_t = std::invoke_result_t>; diff --git a/common/adc_utils.h b/common/adc_utils.h index c9585be..5ede0a2 100644 --- a/common/adc_utils.h +++ b/common/adc_utils.h @@ -441,4 +441,62 @@ public: } }; + +/* join range elements using delimiter between its elements */ + +// NOTE: C++23 has std::views::join_with adapter but here I use upto C++20!!! +template > RO> +static size_t AdcJoinRange(R& r, const RD& delim, RO& ro) + requires std::ranges::range> && // input R is range of ranges + std::same_as>, std::ranges::range_value_t> +{ + auto N = std::ranges::distance(r.begin(), r.end()); + + if (!N) { + return 0; + } + + size_t i = 0; + + std::ranges::for_each(r, [&](const auto& el) { + std::ranges::copy(el, std::back_inserter(ro)); + if (++i < N) { + std::ranges::copy(delim, std::back_inserter(ro)); + } + }); + + + return N; +} + + +// create a range with views of elements of input range and insert a view of input delimiter between them + +template +static size_t AdcReturnRangeElementsView(const R& r, const RD& delim, RO& ro) + requires std::ranges::range> && // input R is range of ranges + std::same_as>, std::ranges::range_value_t> && + std::ranges::view> && // output RO is range of views + std::same_as>, std::ranges::range_value_t> +{ + auto N = std::ranges::distance(r.begin(), r.end()); + + if (!N) { + return 0; + } + + size_t i = 0; + + std::ranges::for_each(r, [&](const auto& el) { + std::back_inserter(ro) = {el.begin(), el.end()}; + if (++i < N) { + std::back_inserter(ro) = {delim.begin(), delim.end()}; + } + }); + + return N; +} + } // namespace adc::utils diff --git a/net/adc_netmsg.h b/net/adc_netmsg.h index df87744..37564e6 100644 --- a/net/adc_netmsg.h +++ b/net/adc_netmsg.h @@ -8,6 +8,7 @@ #include "../common/adc_traits.h" +#include "../common/adc_utils.h" namespace adc { @@ -15,6 +16,9 @@ namespace adc template class AdcGenericNetMessage { + virtual bool empty() const { return std::ranges::distance(_bytes.begin(), _bytes.end()) == 0; } + + // get a copy of message bytes template R bytes() const @@ -29,13 +33,19 @@ class AdcGenericNetMessage virtual ByteStorageT bytes() const { return bytes(); } // get a view of message bytes - template + // template + // requires std::ranges::view> + template R bytesView() const { - return R{_bytes.begin(), _bytes.end()}; + R r; + + r.emplace_back(_bytes.begin(), _bytes.end()); + + return r; } - virtual ByteViewT bytesView() const { return bytesView(); } + virtual std::vector bytesView() const { return bytesView>(); } protected: ByteStorageT _bytes; @@ -47,8 +57,7 @@ namespace constants { static constexpr char ADC_DEFAULT_TOKEN_DELIMITER[] = " "; -static constexpr char ADC_DEFAULT_KEY_PARAM_DELIMITER[] = " "; -static constexpr char ADC_DEFAULT_PARAM_PARAM_DELIMITER[] = " "; +static constexpr char ADC_DEFAULT_KEY_TOKEN_DELIMITER[] = " "; } // namespace constants @@ -63,6 +72,9 @@ class AdcTokenNetMessage : public AdcGenericNetMessage public: static constexpr std::string_view tokenDelimiter{TOKEN_DELIM}; + + virtual bool empty() const { return _tokens.empty(); } + template R bytes() const { @@ -73,37 +85,166 @@ public: } - std::ranges::for_each(_tokens | std::views::take(_tokens.size()), [&r](const auto& el) { - std::ranges::copy(el, std::back_inserter(r)); - std::ranges::copy(tokenDelimiter, std::back_inserter(r)); - }); + // std::ranges::for_each(_tokens | std::views::take(_tokens.size()-1), [&r](const auto& el) { + // std::ranges::copy(el, std::back_inserter(r)); + // std::ranges::copy(tokenDelimiter, std::back_inserter(r)); + // }); - std::ranges::copy(_tokens.back(), std::back_inserter(r)); + // std::ranges::copy(_tokens.back(), std::back_inserter(r)); + + utils::AdcJoinRange(_tokens, tokenDelimiter, r); return r; } - template - requires std::ranges::view> + // template + // requires std::ranges::view> + template R bytesView() const { R r; - std::ranges::for_each(_tokens | std::views::take(_tokens.size()), [&r](const auto& el) { - std::ranges::range_value_t v{el.begin(), el.end()}; - std::back_inserter(r) = {el.begin(), el.end()}; - std::back_inserter(r) = {tokenDelimiter.begin(), tokenDelimiter.end()}; - }); + if (_tokens.empty()) { + return r; + } - std::back_inserter(r) = {_tokens.back().begin(), _tokens.back().end()}; + // std::ranges::for_each(_tokens | std::views::take(_tokens.size()-1), [&r](const auto& el) { + // // std::ranges::range_value_t v{el.begin(), el.end()}; + // std::back_inserter(r) = {el.begin(), el.end()}; + // std::back_inserter(r) = {tokenDelimiter.begin(), tokenDelimiter.end()}; + // }); + + // std::back_inserter(r) = {_tokens.back().begin(), _tokens.back().end()}; + + utils::AdcReturnRangeElementsView(_tokens, tokenDelimiter, r); return r; } + + template R> + R tokens() const + { + R r; + + if (_tokens.empty()) { + return r; + } + + std::ranges::copy(_tokens, std::back_inserter(r)); + + return r; + } + + std::vector tokens() const + { + // + return tokens>(); + } + protected: std::vector _tokens; }; +// special type of tokenized message: +// the first token is keyword +// the other - just tokens +// delimiter between keyword and other tokens may differ from token-to-token one +template +class AdcKeyTokenNetMessage : public AdcTokenNetMessage +{ + using base_t = AdcTokenNetMessage; + +public: + static constexpr std::string_view keytokenDelim{KEY_TOKEN_DELIM}; + + using base_t::tokens; + + virtual bool empty() const + { + auto N = std::distance(this->_bytes.begin(), this->_bytes.end()); + + return (N == 0) && this->_tokens.empty(); + } + + template + R keyBytes() const + { + R r; + + std::ranges::copy(this->_bytes, r); + + return r; + } + + + ByteStorageT keyBytes() const + { + // + return this->_bytes; + } + + + template + R keyView() const + { + // + return R{this->_bytes.begin(), this->_bytes.end()}; + } + + + ByteViewT keyView() const { + // + return ByteViewT{this->_bytes.begin(), this->_bytes.end()}; + } + + + template + R bytes() const + { + R r; + + if (this->_bytes.empty() && this->_tokens.empty()) { + return r; + } + + std::ranges::copy(this->_bytes, std::back_inserter(r)); // keyword + + if (!this->_tokens.empty()) { + std::ranges::copy(keytokenDelim, std::back_inserter(r)); // keyword-to-tokens delimiter + utils::AdcJoinRange(this->_tokens, base_t::tokenDelimiter, r); + } + + return r; + } + + template + R bytesView() const + { + R r; + + if (this->_bytes.empty() && this->_tokens.empty()) { + return r; + } + + std::back_inserter(r) = {this->_bytes.begin(), this->_bytes.end()}; // keyword + + if (!this->_tokens.empty()) { + std::back_inserter(r) = {keytokenDelim.begin(), keytokenDelim.end()}; // keyword-to-tokens delimiter + utils::AdcReturnRangeElementsView(this->_tokens, base_t::tokenDelimiter, r); + } + + return r; + } + +protected: + // _bytes: keyword + // _tokens: tokens +}; + } // namespace adc diff --git a/tests/adc_netmsg_test.cpp b/tests/adc_netmsg_test.cpp index 0d4be1b..d6e36ea 100644 --- a/tests/adc_netmsg_test.cpp +++ b/tests/adc_netmsg_test.cpp @@ -3,6 +3,7 @@ #include #include +#include "../common/adc_utils.h" #include "../net/adc_netmessage.h" using namespace adc; @@ -54,4 +55,21 @@ TEST_CASE("[ADC NET MESSAGE]") for (auto& el : ls) { std::cout << "PAR: [" << el << "]\n"; } + + + ls = {"AAA", "BBB", "CCC"}; + std::string sr{"RES: "}; + std::vector vsv; + + + auto N = utils::AdcReturnRangeElementsView(ls, std::string_view("<>"), vsv); + + std::cout << "\n\nN = " << N << "\n"; + for (auto& el : vsv) { + std::cout << el << "\n"; + } + + N = utils::AdcJoinRange(ls, std::string_view("*"), sr); + std::cout << "\n\nN = " << N << "\n"; + std::cout << sr << "\n"; }