#pragma once #include #include #include #include "../common/adc_traits.h" #include "../common/adc_utils.h" namespace adc { template class AdcNetMessageCommonInterface { virtual bool empty() const { return std::ranges::distance(_bytes.begin(), _bytes.end()) == 0; } // get a copy of message bytes template R bytes() const { R r; std::ranges::copy(_bytes, std::back_inserter(r)); return r; } virtual ByteStorageT bytes() const { return bytes(); } // get a view of message bytes template R bytesView() const { R r; r.emplace_back(_bytes.begin(), _bytes.end()); return r; } virtual std::vector bytesView() const { return bytesView>(); } protected: ByteStorageT _bytes; AdcNetMessageCommonInterface() = default; virtual ~AdcNetMessageCommonInterface() = default; AdcNetMessageCommonInterface(const AdcNetMessageCommonInterface&) = default; AdcNetMessageCommonInterface(AdcNetMessageCommonInterface&&) = default; AdcNetMessageCommonInterface& operator=(const AdcNetMessageCommonInterface&) = default; AdcNetMessageCommonInterface& operator=(AdcNetMessageCommonInterface&&) = default; template void convertToBytes(ByteViewT& res, const T& v, const Ts&... vs) { if constexpr (std::is_array_v>) { // to handle with trailing '\0' in plain char array convertToBytes(std::string_view(v), vs...); } else { if constexpr (traits::adc_input_char_range) { std::ranges::copy(v, std::back_inserter(res)); } else if constexpr (std::convertible_to) { convertToBytes(static_cast(v), vs...); } else if constexpr (traits::formattable) { std::format_to(std::back_inserter(res), "{}", v); } else { static_assert(false, "UNSUPPORTED TYPE!!!"); } } if constexpr (sizeof...(Ts)) { convertToBytes(vs...); } } }; // Generic message class template class AdcGenericNetMessage : public AdcNetMessageCommonInterface { using base_t = AdcNetMessageCommonInterface; public: using base_t::base_t; using base_t::bytes; using base_t::byteView; template void appendBytes(const T& v, const Ts&... vs) { this->convertToBytes(this->_bytes, v, vs...); } template void setBytes(const T& v, const Ts&... vs) { this->_bytes = ByteStorageT(); appendBytes(v, vs...); } template void appendFromBytes(IT begin, IT end) requires std::same_as, char> { std::copy(begin, end, std::back_inserter(this->_bytes)); } template void setFromBytes(IT begin, IT end) requires std::same_as, char> { this->_bytes = ByteStorageT(); appendFromBytes(begin, end); } }; namespace constants { static constexpr char ADC_DEFAULT_TOKEN_DELIMITER[] = " "; static constexpr char ADC_DEFAULT_KEY_TOKEN_DELIMITER[] = " "; } // namespace constants template class AdcTokenNetMessage : public AdcNetMessageCommonInterface { using base_t = AdcNetMessageCommonInterface; public: static constexpr std::string_view tokenDelimiter{TOKEN_DELIM}; AdcTokenNetMessage() = default; template AdcTokenNetMessage(const T& v, const Ts&... vs) : AdcTokenNetMessage() { setTokens(v, vs...); } virtual ~AdcTokenNetMessage() = default; virtual bool empty() const { // return _tokens.empty(); } template R bytes() const { R r; if (_tokens.empty()) { return r; } utils::AdcJoinRange(_tokens, tokenDelimiter, r); return r; } template R bytesView() const { R r; if (_tokens.empty()) { return r; } 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>(); } template void appendTokens(const T& v, const Ts&... vs) { if constexpr (traits::adc_input_char_range || traits::formattable) { this->convertToBytes(_tokens.emplace_back(), v); } else if constexpr (std::ranges::input_range) { using el_t = std::ranges::range_value_t; static_assert(traits::adc_input_char_range || traits::formattable, "INVALID TYPE OF INPUT TOKENS!!!"); if (std::ranges::distance(v.begin(), v.end())) { for (const auto& el : v) { this->convertToBytes(_tokens.emplace_back(), el); } } } else { static_assert(false, "UNSUPPORTED (CANNOT BE SERIALIZED TO BYTES) TYPE OF INPUT TOKENS!!!"); } if constexpr (sizeof...(Ts)) { appendTokens(vs...); } } template void setTokens(const T& v, const Ts&... vs) { _tokens.clear(); appendTokens(v, vs...); } template void appendFromBytes(IT begin, IT end) requires std::same_as, char> { auto it = begin, start_it = begin; do { it = std::search(start_it, end, tokenDelimiter.begin(), tokenDelimiter.end()); std::copy(start_it, it, std::back_inserter(_tokens.emplace_back())); start_it = it + tokenDelimiter.size(); } while (it != end); } template void setFromBytes(IT begin, IT end) requires std::same_as, char> { _tokens.clear(); appendFromBytes(begin, end); } 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::appendTokens; using base_t::setTokens; 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