...
This commit is contained in:
parent
27dccfe7c0
commit
e0e10395fb
@ -40,6 +40,7 @@ set(ASIBFM700_LIB asibfm700mount)
|
|||||||
add_library(${ASIBFM700_LIB} STATIC ${ASIBFM700_LIB_SRC}
|
add_library(${ASIBFM700_LIB} STATIC ${ASIBFM700_LIB_SRC}
|
||||||
asibfm700_mount.h asibfm700_mount.cpp
|
asibfm700_mount.h asibfm700_mount.cpp
|
||||||
asibfm700_configfile.h)
|
asibfm700_configfile.h)
|
||||||
|
target_include_directories(${ASIBFM700_LIB} PRIVATE mcc spdlog)
|
||||||
target_link_libraries(${ASIBFM700_LIB} PRIVATE mcc spdlog)
|
target_link_libraries(${ASIBFM700_LIB} PRIVATE mcc spdlog)
|
||||||
|
|
||||||
option(WITH_TESTS "Build tests" ON)
|
option(WITH_TESTS "Build tests" ON)
|
||||||
|
|||||||
@ -92,5 +92,9 @@ if (WITH_TESTS)
|
|||||||
target_include_directories(${CTTE_TEST_APP} PRIVATE ${ERFA_INCLUDE_DIR})
|
target_include_directories(${CTTE_TEST_APP} PRIVATE ${ERFA_INCLUDE_DIR})
|
||||||
target_link_libraries(${CTTE_TEST_APP} ERFA_LIB bsplines)
|
target_link_libraries(${CTTE_TEST_APP} ERFA_LIB bsplines)
|
||||||
|
|
||||||
|
set(NETMSG_TESTS_APP netmsg_test)
|
||||||
|
add_executable(${NETMSG_TESTS_APP} tests/netmsg_test.cpp)
|
||||||
|
target_link_libraries(${NETMSG_TESTS_APP} mcc)
|
||||||
|
|
||||||
enable_testing()
|
enable_testing()
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@ -471,124 +471,392 @@ struct MccNetMessageValidKeywords {
|
|||||||
return std::array{mcc::utils::FNV1aHash(NETMSG_VALID_KEYWORDS[Is])...};
|
return std::array{mcc::utils::FNV1aHash(NETMSG_VALID_KEYWORDS[Is])...};
|
||||||
}(std::make_index_sequence<NETMSG_VALID_KEYWORDS.size()>());
|
}(std::make_index_sequence<NETMSG_VALID_KEYWORDS.size()>());
|
||||||
|
|
||||||
static std::expected<size_t, std::error_code> isKeywordValid(std::string_view key)
|
constexpr static const size_t* isKeywordValid(std::string_view key)
|
||||||
{
|
{
|
||||||
const auto hash = utils::FNV1aHash(key);
|
const auto hash = utils::FNV1aHash(key);
|
||||||
|
|
||||||
for (auto const& h : NETMSG_VALID_KEYWORD_HASHES) {
|
for (auto const& h : NETMSG_VALID_KEYWORD_HASHES) {
|
||||||
if (h == hash) {
|
if (h == hash) {
|
||||||
return h;
|
return &h;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::unexpected(std::make_error_code(std::errc::invalid_argument));
|
return nullptr;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(mcc_netmsg_valid_keys_c<MccNetMessageValidKeywords>, "");
|
static_assert(mcc_netmsg_valid_keys_c<MccNetMessageValidKeywords>, "");
|
||||||
|
|
||||||
|
|
||||||
template <mcc_netmsg_valid_keys_c BASE_T, traits::mcc_output_char_range BYTEREPR_T>
|
template <mcc_netmsg_valid_keys_c BASE_T, traits::mcc_char_range BYTEREPR_T>
|
||||||
class MccNetMessage
|
class MccNetMessage
|
||||||
{
|
{
|
||||||
|
static inline utils::MccSimpleDeserializer defaultDeserilizer{MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef BASE_T valid_keys_t;
|
typedef BASE_T valid_keys_t;
|
||||||
typedef BYTEREPR_T byte_repr_t;
|
typedef BYTEREPR_T byte_repr_t;
|
||||||
|
|
||||||
|
enum MccNetMessageError { ERROR_OK, ERROR_EMPTY_MESSAGE, ERROR_INVALID_KEYWORD, ERROR_EMPTY_KEYWORD };
|
||||||
|
|
||||||
|
MccNetMessage() = default;
|
||||||
|
|
||||||
template <traits::mcc_input_char_range KT, typename... PTs>
|
template <traits::mcc_input_char_range KT, typename... PTs>
|
||||||
MccNetMessage(KT&& key, PTs&&... params)
|
MccNetMessage(KT&& key, PTs&&... params)
|
||||||
|
requires traits::mcc_output_char_range<BYTEREPR_T>
|
||||||
{
|
{
|
||||||
construct(std::forward<KT>(key), std::forward<PTs>(params)...);
|
construct(std::forward<KT>(key), std::forward<PTs>(params)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <traits::mcc_input_char_range KT>
|
template <traits::mcc_input_char_range R>
|
||||||
bool withKey(const KT& key)
|
constexpr MccNetMessage(const R& msg)
|
||||||
|
requires traits::mcc_input_char_range<BYTEREPR_T>
|
||||||
{
|
{
|
||||||
|
fromCharRange(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <traits::mcc_input_char_range KT>
|
||||||
|
constexpr bool withKey(const KT& key)
|
||||||
|
{
|
||||||
|
if constexpr (std::is_pointer_v<std::decay_t<KT>>) {
|
||||||
|
return withKey(std::string_view{key});
|
||||||
|
}
|
||||||
|
|
||||||
return utils::FNV1aHash(key) == _keywordHash;
|
return utils::FNV1aHash(key) == _keywordHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename DeserFuncT>
|
|
||||||
std::expected<T, std::error_code> params(DeserFuncT&& deser_func)
|
template <traits::mcc_view_or_output_char_range R>
|
||||||
|
R keyword() const
|
||||||
{
|
{
|
||||||
|
if constexpr (traits::mcc_char_view<R>) {
|
||||||
|
return R{_keyword.begin(), _keyword.end()};
|
||||||
|
} else {
|
||||||
|
R r;
|
||||||
|
std::ranges::copy(_keyword, std::back_inserter(r));
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view keyword() const
|
||||||
|
{
|
||||||
|
return _keyword;
|
||||||
|
}
|
||||||
|
|
||||||
|
// template <traits::mcc_view_or_output_char_range R>
|
||||||
|
// R params(size_t start_idx, size_t Nelemes = std::numeric_limits<size_t>::max())
|
||||||
|
// {
|
||||||
|
// if (start_idx >= _params.size()) {
|
||||||
|
// return R{};
|
||||||
|
// }
|
||||||
|
|
||||||
|
// auto stop_idx = start_idx + Nelemes - 1;
|
||||||
|
// if (stop_idx >= _params.size()) {
|
||||||
|
// stop_idx = _params.size() - 1;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if constexpr (traits::mcc_char_view<R>) {
|
||||||
|
// return R{_params[start_idx].begin(), _params[stop_idx].end()};
|
||||||
|
// } else {
|
||||||
|
// R r;
|
||||||
|
// std::ranges::copy(std::string_view{_params[start_idx].begin(), _params[stop_idx].end()},
|
||||||
|
// std::back_inserter(r));
|
||||||
|
|
||||||
|
// return r;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
template <std::ranges::range R>
|
||||||
|
R params(size_t start_idx, size_t Nelemes = std::numeric_limits<size_t>::max())
|
||||||
|
requires(traits::mcc_view_or_output_char_range<R> || traits::mcc_range_of_char_range<R>)
|
||||||
|
{
|
||||||
|
if (start_idx >= _params.size()) {
|
||||||
|
return R{};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto stop_idx = start_idx + Nelemes - 1;
|
||||||
|
if (stop_idx >= _params.size()) {
|
||||||
|
stop_idx = _params.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (traits::mcc_range_of_char_range<R>) { // returm parameters as array
|
||||||
|
using el_t = std::ranges::range_value_t<R>;
|
||||||
|
|
||||||
|
R r;
|
||||||
|
if constexpr (traits::mcc_char_view<el_t> || traits::mcc_output_char_range<el_t>) {
|
||||||
|
for (size_t i = start_idx; i <= stop_idx; ++i) {
|
||||||
|
auto& el = _params[i];
|
||||||
|
std::back_inserter(r) = el_t{el.begin(), el.end()};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
static_assert(false, "UNSUPPORTED RANGE ELEMENT TYPE!!!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
} else {
|
||||||
|
if constexpr (traits::mcc_char_view<R>) { // return joined parameters as a single char-range
|
||||||
|
return R{_params[start_idx].begin(), _params[stop_idx].end()};
|
||||||
|
} else {
|
||||||
|
R r;
|
||||||
|
std::ranges::copy(std::string_view{_params[start_idx].begin(), _params[stop_idx].end()},
|
||||||
|
std::back_inserter(r));
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view params(size_t start_idx, size_t Nelemes = std::numeric_limits<size_t>::max())
|
||||||
|
{
|
||||||
|
return params<std::string_view>(start_idx, Nelemes);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <traits::mcc_view_or_output_char_range R>
|
||||||
|
R param(size_t idx)
|
||||||
|
{
|
||||||
|
if (idx >= _params.size()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (traits::mcc_char_view<R>) {
|
||||||
|
return R{_params[idx].begin(), _params[idx].end()};
|
||||||
|
} else {
|
||||||
|
R r;
|
||||||
|
std::ranges::copy(_params[idx], std::back_inserter(r));
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view param(size_t idx)
|
||||||
|
{
|
||||||
|
if (idx >= _params.size()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return _params[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename DeserFuncT>
|
||||||
|
std::expected<T, std::error_code> paramValue(size_t idx, DeserFuncT&& deser_func)
|
||||||
|
{
|
||||||
|
if (idx >= _params.size()) {
|
||||||
|
return std::unexpected{std::make_error_code(std::errc::argument_out_of_domain)};
|
||||||
|
}
|
||||||
|
|
||||||
T val;
|
T val;
|
||||||
|
|
||||||
auto ec = deser_func(_params, val);
|
auto ec = std::forward<DeserFuncT>(deser_func)(_params[idx], val);
|
||||||
if (ec) {
|
if (ec) {
|
||||||
return std::unexpect(ec);
|
return std::unexpected(ec);
|
||||||
} else {
|
} else {
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::expected<T, std::error_code> params()
|
std::expected<T, std::error_code> paramValue(size_t idx)
|
||||||
{
|
{
|
||||||
return params<T>(utils::MccSimpleDeserializer{}.setRangeDelim(MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ));
|
return paramValue<T>(idx, defaultDeserilizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <traits::mcc_view_or_output_char_range R>
|
||||||
|
R byteRepr() const
|
||||||
|
{
|
||||||
|
if constexpr (traits::mcc_char_view<R>) {
|
||||||
|
return R{_msgBuffer.begin(), _msgBuffer.end()};
|
||||||
|
} else {
|
||||||
|
R r;
|
||||||
|
std::ranges::copy(_msgBuffer, std::back_inserter(r));
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view byteRepr() const
|
||||||
|
{
|
||||||
|
return byteRepr<std::string_view>();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <traits::mcc_input_char_range KT, typename... PTs>
|
template <traits::mcc_input_char_range KT, typename... PTs>
|
||||||
std::error_code construct(KT&& key, PTs&&... params)
|
std::error_code construct(KT&& key, PTs&&... params)
|
||||||
|
requires traits::mcc_output_char_range<BYTEREPR_T>
|
||||||
{
|
{
|
||||||
|
if constexpr (std::is_pointer_v<std::decay_t<KT>>) {
|
||||||
|
return construct(std::string_view(key), std::forward<PTs>(params)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!std::ranges::size(key)) {
|
if (!std::ranges::size(key)) {
|
||||||
return std::make_error_code(std::errc::invalid_argument);
|
return std::make_error_code(std::errc::invalid_argument);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto r = valid_keys_t::isKeywordValid(key);
|
auto r = valid_keys_t::isKeywordValid(key);
|
||||||
if (!r) {
|
if (!r) {
|
||||||
return r.error();
|
return std::make_error_code(std::errc::argument_out_of_domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
_keywordHash = r.value();
|
_keywordHash = *r;
|
||||||
|
|
||||||
_msgBuffer = BYTEREPR_T{};
|
_msgBuffer = BYTEREPR_T{};
|
||||||
|
|
||||||
std::ranges::copy(key, std::back_inserter(_msgBuffer));
|
std::ranges::copy(std::forward<KT>(key), std::back_inserter(_msgBuffer));
|
||||||
_keyword = {_msgBuffer.begin(), _msgBuffer.end()};
|
// _keyword = {_msgBuffer.begin(), _msgBuffer.end()};
|
||||||
|
size_t key_idx = std::distance(_msgBuffer.begin(), _msgBuffer.end());
|
||||||
|
std::vector<size_t> par_idx;
|
||||||
|
|
||||||
_params = std::string_view{};
|
_params.clear();
|
||||||
|
|
||||||
if constexpr (sizeof...(PTs)) {
|
if constexpr (sizeof...(PTs)) {
|
||||||
std::ranges::copy(MCC_COMMPROTO_KEYPARAM_DELIM_SEQ, std::back_inserter(_msgBuffer));
|
std::ranges::copy(MCC_COMMPROTO_KEYPARAM_DELIM_SEQ, std::back_inserter(_msgBuffer));
|
||||||
|
|
||||||
[this]<typename T, typename... Ts>(this auto&& self, const T& par, const Ts&... pars) {
|
convertFunc(par_idx, std::forward<PTs>(params)...);
|
||||||
if constexpr (std::is_arithmetic_v<T>) {
|
|
||||||
std::ranges::copy(std::to_string(par), std::back_inserter(_msgBuffer));
|
|
||||||
} else if constexpr (std::convertible_to<T, std::string>) {
|
|
||||||
std::ranges::copy(static_cast<std::string>(par), std::back_inserter(_msgBuffer));
|
|
||||||
} else if constexpr (std::constructible_from<std::string, T>) {
|
|
||||||
std::ranges::copy(std::string(par), std::back_inserter(_msgBuffer));
|
|
||||||
} else if constexpr (traits::mcc_char_range<T>) {
|
|
||||||
std::ranges::copy(std::string(par.begin(), par.end()), std::back_inserter(_msgBuffer));
|
|
||||||
} else if constexpr (std::same_as<T, MccCoordPairKind>) {
|
|
||||||
std::ranges::copy(mcc_pairkind2str(par), std::back_inserter(_msgBuffer));
|
|
||||||
} else if constexpr (traits::mcc_time_duration_c<T>) {
|
|
||||||
std::forward<decltype(self)>(self)(par.count());
|
|
||||||
} else {
|
|
||||||
static_assert(false, "UNSUPPORTED TYPE!!!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if constexpr (sizeof...(Ts)) {
|
for (size_t i = 0; i < par_idx.size(); i += 2) {
|
||||||
std::ranges::copy(MCC_COMMPROTO_PARAMPARAM_DELIM_SEQ, std::back_inserter(_msgBuffer));
|
_params.emplace_back(_msgBuffer.begin() + par_idx[i], _msgBuffer.begin() + par_idx[i + 1]);
|
||||||
|
}
|
||||||
std::forward<decltype(self)>(self)(pars...);
|
|
||||||
}
|
|
||||||
}(std::forward<PTs>(params)...);
|
|
||||||
|
|
||||||
_params = {_msgBuffer.begin() + _keyword.size() + MCC_COMMPROTO_KEYPARAM_DELIM_SEQ.size(),
|
|
||||||
_msgBuffer.end()};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_keyword = std::string_view{_msgBuffer.begin(), _msgBuffer.begin() + key_idx};
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <traits::mcc_input_char_range R>
|
||||||
|
constexpr MccNetMessageError fromCharRange(const R& r)
|
||||||
|
{
|
||||||
|
if constexpr (std::is_pointer_v<std::decay_t<R>>) {
|
||||||
|
return fromCharRange(std::string_view(r));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (std::ranges::size(r) == 0) {
|
||||||
|
return ERROR_EMPTY_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view key, val;
|
||||||
|
|
||||||
|
auto prev_msg_buff = _msgBuffer;
|
||||||
|
|
||||||
|
if constexpr (traits::mcc_output_char_range<BYTEREPR_T>) {
|
||||||
|
_msgBuffer = BYTEREPR_T{};
|
||||||
|
std::ranges::copy(r, std::back_inserter(_msgBuffer));
|
||||||
|
} else {
|
||||||
|
_msgBuffer = {std::begin(r), std::end(r)};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto found = std::ranges::search(_msgBuffer, MCC_COMMPROTO_KEYPARAM_DELIM_SEQ);
|
||||||
|
|
||||||
|
if (found.empty()) { // only keyword
|
||||||
|
key = utils::trimSpaces(std::string_view{_msgBuffer.begin(), _msgBuffer.end()});
|
||||||
|
} else {
|
||||||
|
key = utils::trimSpaces(std::string_view{_msgBuffer.begin(), found.begin()});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto kv = valid_keys_t::isKeywordValid(key);
|
||||||
|
if (!kv) {
|
||||||
|
// _msgBuffer = prev_msg_buff; // restore previous netmessage state
|
||||||
|
return ERROR_INVALID_KEYWORD;
|
||||||
|
}
|
||||||
|
|
||||||
|
_keywordHash = *kv;
|
||||||
|
_keyword = key;
|
||||||
|
|
||||||
|
if (!found.empty()) { // params ...
|
||||||
|
_params.clear();
|
||||||
|
auto pr =
|
||||||
|
std::views::split(std::string_view{found.end(), _msgBuffer.end()}, MCC_COMMPROTO_PARAMPARAM_DELIM_SEQ);
|
||||||
|
for (auto const& p : pr) {
|
||||||
|
_params.emplace_back(p.begin(), p.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
size_t _keywordHash{};
|
size_t _keywordHash{};
|
||||||
std::string_view _keyword{};
|
std::string_view _keyword{};
|
||||||
std::string_view _params{};
|
std::vector<std::string_view> _params{};
|
||||||
|
|
||||||
BYTEREPR_T _msgBuffer{};
|
BYTEREPR_T _msgBuffer{};
|
||||||
|
|
||||||
|
template <typename T, typename... Ts>
|
||||||
|
void convertFunc(std::vector<size_t>& idx, const T& par, const Ts&... pars)
|
||||||
|
{
|
||||||
|
idx.emplace_back(std::distance(_msgBuffer.begin(), _msgBuffer.end()));
|
||||||
|
|
||||||
|
// if constexpr (std::is_arithmetic_v<T>) {
|
||||||
|
// std::format_to(std::back_inserter(_msgBuffer), "{}", par);
|
||||||
|
// } else if constexpr (std::convertible_to<T, std::string>) {
|
||||||
|
// std::ranges::copy(static_cast<std::string>(par), std::back_inserter(_msgBuffer));
|
||||||
|
// } else if constexpr (std::constructible_from<std::string, T>) {
|
||||||
|
// std::ranges::copy(std::string(par), std::back_inserter(_msgBuffer));
|
||||||
|
// } else if constexpr (traits::mcc_char_range<T>) {
|
||||||
|
// std::ranges::copy(std::string(par.begin(), par.end()), std::back_inserter(_msgBuffer));
|
||||||
|
// } else if constexpr (std::same_as<T, MccCoordPairKind>) {
|
||||||
|
// std::ranges::copy(mcc_pairkind2str(par), std::back_inserter(_msgBuffer));
|
||||||
|
// } else if constexpr (traits::mcc_time_duration_c<T>) {
|
||||||
|
// idx.resize(idx.size() - 1);
|
||||||
|
// convertFunc(idx, par.count());
|
||||||
|
// idx.resize(idx.size() - 1);
|
||||||
|
// } else if constexpr (std::formattable<T, char>) {
|
||||||
|
// std::format_to(std::back_inserter(_msgBuffer), "{}", par);
|
||||||
|
// } else {
|
||||||
|
// static_assert(false, "UNSUPPORTED TYPE!!!");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// use the lambda here to enable recursive calling with no special handling of 'idx'
|
||||||
|
[](this auto const& self, auto& buff, auto const& p) {
|
||||||
|
using p_t = std::decay_t<decltype(p)>;
|
||||||
|
|
||||||
|
if constexpr (std::is_arithmetic_v<p_t>) {
|
||||||
|
std::format_to(std::back_inserter(buff), "{}", p);
|
||||||
|
} else if constexpr (std::convertible_to<p_t, std::string>) {
|
||||||
|
std::ranges::copy(static_cast<std::string>(p), std::back_inserter(buff));
|
||||||
|
} else if constexpr (std::constructible_from<std::string, p_t>) {
|
||||||
|
std::ranges::copy(std::string(p), std::back_inserter(buff));
|
||||||
|
} else if constexpr (traits::mcc_char_range<p_t>) {
|
||||||
|
std::ranges::copy(std::string(p.begin(), p.end()), std::back_inserter(buff));
|
||||||
|
} else if constexpr (std::same_as<p_t, MccCoordPairKind>) {
|
||||||
|
std::ranges::copy(mcc_pairkind2str(p), std::back_inserter(buff));
|
||||||
|
} else if constexpr (traits::mcc_time_duration_c<p_t>) {
|
||||||
|
self(buff, p.count());
|
||||||
|
} else if constexpr (std::ranges::range<p_t>) {
|
||||||
|
auto sz = std::ranges::size(p);
|
||||||
|
if (sz == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self(buff, *p.begin()); // the first element
|
||||||
|
|
||||||
|
if (sz > 1) {
|
||||||
|
for (auto const& el : p | std::views::drop(1)) {
|
||||||
|
std::ranges::copy(MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ, std::back_inserter(buff));
|
||||||
|
self(buff, el);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if constexpr (std::formattable<p_t, char>) {
|
||||||
|
std::format_to(std::back_inserter(buff), "{}", p);
|
||||||
|
} else {
|
||||||
|
static_assert(false, "UNSUPPORTED TYPE!!!");
|
||||||
|
}
|
||||||
|
}(_msgBuffer, par);
|
||||||
|
|
||||||
|
idx.emplace_back(std::distance(_msgBuffer.begin(), _msgBuffer.end()));
|
||||||
|
|
||||||
|
if constexpr (sizeof...(Ts)) {
|
||||||
|
std::ranges::copy(MCC_COMMPROTO_PARAMPARAM_DELIM_SEQ, std::back_inserter(_msgBuffer));
|
||||||
|
|
||||||
|
convertFunc(idx, pars...);
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static_assert(MccNetMessage<MccNetMessageValidKeywords, std::string_view>{"ACK"}.withKey("ACK"));
|
||||||
|
|
||||||
} // namespace mcc::network
|
} // namespace mcc::network
|
||||||
|
|||||||
@ -16,7 +16,7 @@ namespace mcc::utils
|
|||||||
static const std::regex decimalNumberRx{" *[-+]?([0-9]*[.])?[0-9]+([eE][-+]?[0-9]+)? *"};
|
static const std::regex decimalNumberRx{" *[-+]?([0-9]*[.])?[0-9]+([eE][-+]?[0-9]+)? *"};
|
||||||
static const std::regex sexagesimalReprRx{" *[-+]?[0-9]{1,2}:[0-9]{1,2}:([0-9]{0,2}[.])?[0-9]+ *"};
|
static const std::regex sexagesimalReprRx{" *[-+]?[0-9]{1,2}:[0-9]{1,2}:([0-9]{0,2}[.])?[0-9]+ *"};
|
||||||
|
|
||||||
static bool isEqual(std::floating_point auto const& v1, std::floating_point auto const& v2)
|
constexpr static bool isEqual(std::floating_point auto const& v1, std::floating_point auto const& v2)
|
||||||
{
|
{
|
||||||
constexpr auto eps = std::numeric_limits<std::common_type_t<decltype(v1), decltype(v2)>>::epsilon();
|
constexpr auto eps = std::numeric_limits<std::common_type_t<decltype(v1), decltype(v2)>>::epsilon();
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ static bool isEqual(std::floating_point auto const& v1, std::floating_point auto
|
|||||||
enum class TrimType { TRIM_LEFT, TRIM_RIGHT, TRIM_BOTH };
|
enum class TrimType { TRIM_LEFT, TRIM_RIGHT, TRIM_BOTH };
|
||||||
|
|
||||||
template <std::ranges::contiguous_range R>
|
template <std::ranges::contiguous_range R>
|
||||||
static std::string_view trimSpaces(R&& r, TrimType type = TrimType::TRIM_BOTH)
|
constexpr static std::string_view trimSpaces(R&& r, TrimType type = TrimType::TRIM_BOTH)
|
||||||
requires std::same_as<char, std::remove_cvref_t<std::ranges::range_value_t<R>>>
|
requires std::same_as<char, std::remove_cvref_t<std::ranges::range_value_t<R>>>
|
||||||
{
|
{
|
||||||
auto is_space = [](const auto& ch) { return ch == ' '; };
|
auto is_space = [](const auto& ch) { return ch == ' '; };
|
||||||
@ -59,7 +59,7 @@ static std::string_view trimSpaces(R&& r, TrimType type = TrimType::TRIM_BOTH)
|
|||||||
return std::string_view(f1, f2);
|
return std::string_view(f1, f2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string_view trimSpaces(const char* r, TrimType type = TrimType::TRIM_BOTH)
|
constexpr static std::string_view trimSpaces(const char* r, TrimType type = TrimType::TRIM_BOTH)
|
||||||
{
|
{
|
||||||
return trimSpaces(std::string_view(r), type);
|
return trimSpaces(std::string_view(r), type);
|
||||||
}
|
}
|
||||||
@ -404,7 +404,15 @@ static constexpr size_t FNV1aHash(std::forward_iterator auto begin, std::sentine
|
|||||||
class MccSimpleDeserializer
|
class MccSimpleDeserializer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static constexpr std::string RANGE_DELIM_SEQ = ",";
|
static constexpr std::string_view RANGE_DELIM_SEQ = ",";
|
||||||
|
|
||||||
|
MccSimpleDeserializer() : _rangeDelim(RANGE_DELIM_SEQ) {}
|
||||||
|
|
||||||
|
template <traits::mcc_input_char_range R>
|
||||||
|
MccSimpleDeserializer(R&& r) : MccSimpleDeserializer()
|
||||||
|
{
|
||||||
|
setRangeDelim(std::forward<R>(r));
|
||||||
|
}
|
||||||
|
|
||||||
template <traits::mcc_input_char_range R>
|
template <traits::mcc_input_char_range R>
|
||||||
MccSimpleDeserializer& setRangeDelim(R&& r)
|
MccSimpleDeserializer& setRangeDelim(R&& r)
|
||||||
@ -485,7 +493,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::string _rangeDelim{RANGE_DELIM_SEQ};
|
std::string _rangeDelim;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
54
mcc/tests/netmsg_test.cpp
Normal file
54
mcc/tests/netmsg_test.cpp
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
|
||||||
|
#include "../mcc_netserver_proto.h"
|
||||||
|
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
using msg1_t = mcc::network::MccNetMessage<mcc::network::MccNetMessageValidKeywords, std::string_view>;
|
||||||
|
using msg2_t = mcc::network::MccNetMessage<mcc::network::MccNetMessageValidKeywords, std::string>;
|
||||||
|
|
||||||
|
std::string_view sv{"TARGET 11:22:33.212; -0.23876984; RADEC "};
|
||||||
|
std::vector<char> arr{sv.begin(), sv.end()};
|
||||||
|
|
||||||
|
msg1_t msg1;
|
||||||
|
|
||||||
|
msg1.fromCharRange(arr);
|
||||||
|
|
||||||
|
std::cout << "msg.key = <" << msg1.keyword() << ">\n";
|
||||||
|
std::cout << "msg.par[1] = <" << msg1.param(1) << ">\n";
|
||||||
|
|
||||||
|
std::vector<double> vd{1.1, 2.2, 3.3};
|
||||||
|
msg2_t msg2("ACK", 12.43298042, "EEE", std::chrono::seconds(10), vd);
|
||||||
|
|
||||||
|
std::cout << "msg.bytes = <" << msg2.byteRepr() << ">\n";
|
||||||
|
std::cout << "msg.key = <" << msg2.keyword() << ">\n";
|
||||||
|
std::cout << "msg.par[1] = <" << msg2.param(1) << ">\n";
|
||||||
|
std::cout << "msg.par[2] = <" << msg2.param(2) << ">\n";
|
||||||
|
|
||||||
|
auto p2 = msg2.paramValue<std::chrono::seconds>(2);
|
||||||
|
std::cout << "msg.par[2] = <" << p2.value_or({}) << ">\n";
|
||||||
|
|
||||||
|
std::cout << "msg.par[3] = <";
|
||||||
|
auto p3 = msg2.paramValue<decltype(vd)>(3);
|
||||||
|
if (p3.has_value()) {
|
||||||
|
for (auto const& el : p3.value()) {
|
||||||
|
std::cout << el << " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::cout << ">\n";
|
||||||
|
|
||||||
|
std::cout << "msg.par[1-2] joined = <" << msg2.params(1, 2) << ">\n";
|
||||||
|
|
||||||
|
auto vv = msg2.params<std::list<std::string_view>>(1, 3);
|
||||||
|
std::cout << "msg.par[1-3] array = <";
|
||||||
|
for (auto const& el : vv) {
|
||||||
|
std::cout << el << " ";
|
||||||
|
}
|
||||||
|
std::cout << ">\n";
|
||||||
|
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user