This commit is contained in:
Timur A. Fatkhullin 2024-06-03 18:02:46 +03:00
parent 65cd65b2d8
commit c0316937e3
4 changed files with 239 additions and 18 deletions

View File

@ -46,6 +46,10 @@ template <typename R>
concept adc_char_view = std::ranges::view<R> && std::same_as<std::ranges::range_value_t<R>, char>; concept adc_char_view = std::ranges::view<R> && std::same_as<std::ranges::range_value_t<R>, char>;
template <typename R>
concept adc_range_of_view_char_range = std::ranges::range<R> && std::ranges::view<std::ranges::range_value_t<R>> &&
std::same_as<std::ranges::range_value_t<std::ranges::range_value_t<R>>, char>;
// deduce returned type of callable // deduce returned type of callable
// template <typename T> // template <typename T>
// using adc_retval_t = std::invoke_result_t<std::remove_cvref_t<T>>; // using adc_retval_t = std::invoke_result_t<std::remove_cvref_t<T>>;

View File

@ -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 <std::ranges::input_range R,
std::ranges::input_range RD,
std::ranges::output_range<std::ranges::range_value_t<RD>> RO>
static size_t AdcJoinRange(R& r, const RD& delim, RO& ro)
requires std::ranges::range<std::ranges::range_value_t<R>> && // input R is range of ranges
std::same_as<std::ranges::range_value_t<std::ranges::range_value_t<R>>, std::ranges::range_value_t<RD>>
{
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 <std::ranges::input_range R, std::ranges::input_range RD, std::ranges::range RO>
static size_t AdcReturnRangeElementsView(const R& r, const RD& delim, RO& ro)
requires std::ranges::range<std::ranges::range_value_t<R>> && // input R is range of ranges
std::same_as<std::ranges::range_value_t<std::ranges::range_value_t<R>>, std::ranges::range_value_t<RD>> &&
std::ranges::view<std::ranges::range_value_t<RO>> && // output RO is range of views
std::same_as<std::ranges::range_value_t<std::ranges::range_value_t<RO>>, std::ranges::range_value_t<RD>>
{
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 } // namespace adc::utils

View File

@ -8,6 +8,7 @@
#include "../common/adc_traits.h" #include "../common/adc_traits.h"
#include "../common/adc_utils.h"
namespace adc namespace adc
{ {
@ -15,6 +16,9 @@ namespace adc
template <traits::adc_output_char_range ByteStorageT = std::string, traits::adc_char_view ByteViewT = std::string_view> template <traits::adc_output_char_range ByteStorageT = std::string, traits::adc_char_view ByteViewT = std::string_view>
class AdcGenericNetMessage class AdcGenericNetMessage
{ {
virtual bool empty() const { return std::ranges::distance(_bytes.begin(), _bytes.end()) == 0; }
// get a copy of message bytes // get a copy of message bytes
template <traits::adc_output_char_range R> template <traits::adc_output_char_range R>
R bytes() const R bytes() const
@ -29,13 +33,19 @@ class AdcGenericNetMessage
virtual ByteStorageT bytes() const { return bytes<ByteStorageT>(); } virtual ByteStorageT bytes() const { return bytes<ByteStorageT>(); }
// get a view of message bytes // get a view of message bytes
template <traits::adc_char_view R> // template <std::ranges::range R>
// requires std::ranges::view<std::ranges::range_value_t<R>>
template<traits::adc_range_of_view_char_range R>
R bytesView() const 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<ByteViewT>(); } virtual std::vector<ByteViewT> bytesView() const { return bytesView<std::vector<ByteViewT>>(); }
protected: protected:
ByteStorageT _bytes; ByteStorageT _bytes;
@ -47,8 +57,7 @@ namespace constants
{ {
static constexpr char ADC_DEFAULT_TOKEN_DELIMITER[] = " "; static constexpr char ADC_DEFAULT_TOKEN_DELIMITER[] = " ";
static constexpr char ADC_DEFAULT_KEY_PARAM_DELIMITER[] = " "; static constexpr char ADC_DEFAULT_KEY_TOKEN_DELIMITER[] = " ";
static constexpr char ADC_DEFAULT_PARAM_PARAM_DELIMITER[] = " ";
} // namespace constants } // namespace constants
@ -63,6 +72,9 @@ class AdcTokenNetMessage : public AdcGenericNetMessage<ByteStorageT, ByteViewT>
public: public:
static constexpr std::string_view tokenDelimiter{TOKEN_DELIM}; static constexpr std::string_view tokenDelimiter{TOKEN_DELIM};
virtual bool empty() const { return _tokens.empty(); }
template <traits::adc_output_char_range R> template <traits::adc_output_char_range R>
R bytes() const R bytes() const
{ {
@ -73,37 +85,166 @@ public:
} }
std::ranges::for_each(_tokens | std::views::take(_tokens.size()), [&r](const auto& el) { // 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(el, std::back_inserter(r));
std::ranges::copy(tokenDelimiter, 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; return r;
} }
template <std::ranges::range R> // template <std::ranges::range R>
requires std::ranges::view<std::ranges::range_value_t<R>> // requires std::ranges::view<std::ranges::range_value_t<R>>
template<traits::adc_range_of_view_char_range R>
R bytesView() const R bytesView() const
{ {
R r; R r;
std::ranges::for_each(_tokens | std::views::take(_tokens.size()), [&r](const auto& el) { if (_tokens.empty()) {
std::ranges::range_value_t<R> v{el.begin(), el.end()}; return r;
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()}; // std::ranges::for_each(_tokens | std::views::take(_tokens.size()-1), [&r](const auto& el) {
// // std::ranges::range_value_t<R> 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; return r;
} }
template<std::ranges::output_range<ByteStorageT> R>
R tokens() const
{
R r;
if (_tokens.empty()) {
return r;
}
std::ranges::copy(_tokens, std::back_inserter(r));
return r;
}
std::vector<ByteStorageT> tokens() const
{
//
return tokens<std::vector<ByteStorageT>>();
}
protected: protected:
std::vector<ByteStorageT> _tokens; std::vector<ByteStorageT> _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<const char *KEY_TOKEN_DELIM = constants::ADC_DEFAULT_KEY_TOKEN_DELIMITER,
const char *TOKEN_DELIM = constants::ADC_DEFAULT_TOKEN_DELIMITER,
traits::adc_output_char_range ByteStorageT = std::string,
traits::adc_char_view ByteViewT = std::string_view>
class AdcKeyTokenNetMessage : public AdcTokenNetMessage<TOKEN_DELIM, ByteStorageT, ByteViewT>
{
using base_t = AdcTokenNetMessage<TOKEN_DELIM, ByteStorageT, ByteViewT>;
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<traits::adc_output_char_range R>
R keyBytes() const
{
R r;
std::ranges::copy(this->_bytes, r);
return r;
}
ByteStorageT keyBytes() const
{
//
return this->_bytes;
}
template<traits::adc_char_view R>
R keyView() const
{
//
return R{this->_bytes.begin(), this->_bytes.end()};
}
ByteViewT keyView() const {
//
return ByteViewT{this->_bytes.begin(), this->_bytes.end()};
}
template<traits::adc_output_char_range R>
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<traits::adc_range_of_view_char_range R>
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 } // namespace adc

View File

@ -3,6 +3,7 @@
#include <doctest/doctest.h> #include <doctest/doctest.h>
#include <iostream> #include <iostream>
#include "../common/adc_utils.h"
#include "../net/adc_netmessage.h" #include "../net/adc_netmessage.h"
using namespace adc; using namespace adc;
@ -54,4 +55,21 @@ TEST_CASE("[ADC NET MESSAGE]")
for (auto& el : ls) { for (auto& el : ls) {
std::cout << "PAR: [" << el << "]\n"; std::cout << "PAR: [" << el << "]\n";
} }
ls = {"AAA", "BBB", "CCC"};
std::string sr{"RES: "};
std::vector<std::string_view> 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";
} }