diff --git a/CMakeLists.txt b/CMakeLists.txt index 6eb2d25..862f3ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,8 @@ set(ADC_DEVICE_HEADERS include(GNUInstallDirs) add_library(${PROJECT_NAME} INTERFACE ${ADC_COMMON_HEADERS} ${ADC_DEVICE_HEADERS} - common/adc_traits.h) + common/adc_traits.h + common/adc_utils.h) target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_20) # target_link_libraries(${PROJECT_NAME} INTERFACE ASIO::ASIO) target_include_directories( diff --git a/common/adc_traits.h b/common/adc_traits.h index 2ba4110..7411f86 100644 --- a/common/adc_traits.h +++ b/common/adc_traits.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -14,6 +15,10 @@ namespace adc::traits { +// range of char/const char +template +concept adc_char_range = std::ranges::range && std::is_same_v>, char>; + // deduce returned type of callable template using adc_retval_t = std::invoke_result_t>; @@ -74,7 +79,7 @@ using adc_deduced_type = // perfect-forwarding wrapper template -auto adc_pf_wrapper(T&& v, Ts&&... vs) +constexpr static auto adc_pf_wrapper(T&& v, Ts&&... vs) { return std::tuple, adc_deduced_type...>(std::forward(v), std::forward(vs)...); } diff --git a/common/adc_utils.h b/common/adc_utils.h new file mode 100644 index 0000000..756beeb --- /dev/null +++ b/common/adc_utils.h @@ -0,0 +1,129 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "../common/adc_traits.h" + + +namespace adc::utils +{ + +bool AdcIsSpace(char in) noexcept +{ + static auto ws = {' ', '\t', '\n', '\v', '\r', '\f'}; + return std::ranges::any_of(ws, [in](auto p) { return p == in; }); +}; + + +template +auto AdcTrimSpaces(R&& r) +{ + return r | std::views::drop_while(AdcIsSpace) | std::views::reverse | std::views::drop_while(AdcIsSpace) | + std::views::reverse; +} + + +template +ValueT AdcFromChars(R&& range) +{ + if constexpr (std::is_arithmetic_v) { + ValueT v; + + auto res = std::from_chars(&*range.begin(), &*range.end(), v); + + if (res.ec == std::errc()) { + if (res.ptr != &*range.end()) { + throw std::invalid_argument( + "adc_from_chars: cannot convert char-range to user-type value (invalid argument)"); + } + + return v; + } else if (res.ec == std::errc::invalid_argument) { + throw std::invalid_argument( + "adc_from_chars: cannot convert char-range to user-type value (invalid argument)"); + } else if (res.ec == std::errc::result_out_of_range) { + throw std::invalid_argument( + "adc_from_chars: cannot convert char-range to user-type value (result out of range)"); + } + } else if (std::is_constructible_v, std::ranges::iterator_t>) { + return ValueT(range.begin(), range.end()); + } else { + throw std::invalid_argument( + "adc_from_chars: cannot convert char-range to user-type value (unsupported user-value type)"); + } +} + + +/* TRIVIAL SERIALIZER/DESERIALIZER */ + +static const std::regex AdcIntegerRegex("^ *[+-]?\\d+\\d* *$", std::regex::ECMAScript); +static const std::regex AdcRealRegex("^ *[-+]?\\d+\\d*\\.?\\d*([Ee][-+]?\\d+)? *$", std::regex::ECMAScript); + +template +static SerializedT AdcTrivialSerializer(VT&& value) +{ + using v_t = std::decay_t; + + if constexpr (std::is_convertible_v) { + return static_cast(std::forward(value)); + } else if (std::is_convertible_v) { + return static_cast(std::to_string(std::forward(value))); + } else if (traits::adc_char_range) { + auto s = std::to_string(std::forward(value)); + return SerializedT(s.begin(), s.end()); + } else { + // throw std::invalid_argument("trivial serializer: cannot serialize value"); + static_assert(false, "TRIVIAL SERIALIZER: UNSUPPORTED SERIALIZING TYPE!!!"); + } +} + + +template +static VT AdcTrivialDeserializer(SerializedT&& s_value) +{ + static_assert(std::is_convertible_v || traits::adc_char_range, + "TRIVIAL DESERIALIZER: UNSUPPORTED TYPE OF SERIALIZED VALUE!!!"); + + if constexpr (std::is_arithmetic_v) { + if constexpr (traits::adc_char_range) { + if (std::regex_match(s_value.begin(), s_value.end(), AdcIntegerRegex)) { // integer value + return std::stoll({s_value.begin(), s_value.end()}); + } + + if (std::regex_match(s_value.begin(), s_value.end(), AdcRealRegex)) { // floating-point value + return std::stod({s_value.begin(), s_value.end()}); + } + } else { // SerializedT may be converted to std::string + if (std::regex_match(s_value, AdcIntegerRegex)) { // integer value + return std::stoll(std::forward(s_value)); + } + + if (std::regex_match(s_value, AdcRealRegex)) { // floating-point value + return std::stod(std::forward(s_value)); + } + } + + + throw std::invalid_argument("trivial deserializer: unsupported serialized value"); + + } else if (std::is_convertible_v) { + return static_cast(std::forward(s_value)); + } else if (std::is_convertible_v) { // SerializedT is char range + return static_cast(s_v(s_value.begin(), s_value.end())); + } + + throw std::invalid_argument("trivial deserializer: unsupported serialized value"); +} + + +template +OutputR AdcRangeFromCharRange(InputR&& input_r, DelimR&& delim_r, size_t max_len = std::numeric_limits::max()) +{ + OutputR out_r; +} + +} // namespace adc::utils diff --git a/common/adc_value_holder.h b/common/adc_value_holder.h index fa0bb09..a834879 100644 --- a/common/adc_value_holder.h +++ b/common/adc_value_holder.h @@ -107,22 +107,13 @@ protected: std::type_index _internalTypeIndex; - constexpr static std::tuple _defaultConvTypes{}; - -public: - // always-true validator - constexpr static auto AdcValueHolderDummyValidator = [](const auto&) { return true; }; - - /* CONSTRUCTORS AND DESTRUCTOR */ - - AdcValueHolder(std::invocable<> auto&& getter, - std::invocable&> auto&& setter, - std::predicate&> auto&& validator = AdcValueHolderDummyValidator) - : _internalTypeIndex(std::type_index(typeid(arg_t))) + void init(std::invocable<> auto&& getter, + std::invocable&> auto&& setter, + std::predicate&> auto&& validator) { using value_t = arg_t; - static_assert(std::is_same_v, "THE getter MUST NOT RETURN void type!!!"); + static_assert(std::is_same_v, "THE getter MUST NOT RETURN void TYPE!!!"); using g_t = decltype(getter); _getterWrapper = [wrapper = traits::adc_pf_wrapper(std::forward(getter))](std::any& val) { @@ -164,6 +155,40 @@ public: _convertToInternal[_internalTypeIndex] = [](auto) {}; } +public: + // default trivial types (just arithmetic ones) + constexpr static std::tuple _defaultTrivialConvTypes{}; + + // always-true validator + constexpr static auto AdcValueHolderDummyValidator = [](const auto&) { return true; }; + + /* CONSTRUCTORS AND DESTRUCTOR */ + + AdcValueHolder( + std::invocable<> auto&& getter, + std::invocable&> auto&& setter, + std::predicate&> auto&& validator = AdcValueHolder::AdcValueHolderDummyValidator) + : _internalTypeIndex(std::type_index(typeid(arg_t))) + { + init(std::forward(getter), std::forward(setter), + std::forward(validator)); + } + + + template + AdcValueHolder( + std::invocable<> auto&& getter, + std::invocable&> auto&& setter, + const std::tuple& trivialConvTypes, + std::predicate&> auto&& validator = AdcValueHolder::AdcValueHolderDummyValidator) + : AdcValueHolder(std::forward(getter), + std::forward(setter), + std::forward(validator)) + { + // setup trivially-defined conversion function + AdcValueHolder::setupTrivialConvertFunc, std::tuple>(this); + } + AdcValueHolder(const AdcValueHolder&) = default; AdcValueHolder(AdcValueHolder&&) = default; @@ -184,6 +209,38 @@ public: } + AdcValueHolder& resetValueHolder( + std::invocable<> auto&& getter, + std::invocable&> auto&& setter, + std::predicate&> auto&& validator = AdcValueHolderDummyValidator) + { + _convertFromInternal.clear(); + _convertToInternal.clear(); + + init(std::forward(getter), std::forward(setter), + std::forward(validator)); + + return *this; + } + + + template + AdcValueHolder& resetValueHolder( + std::invocable<> auto&& getter, + std::invocable&> auto&& setter, + const std::tuple& trivialConvTypes, + std::predicate&> auto&& validator = AdcValueHolderDummyValidator) + { + resetValueHolder(std::forward(getter), std::forward(setter), + std::forward(validator)); + + // setup trivially-defined conversion function + AdcValueHolder::setupTrivialConvertFunc, std::tuple>(this); + + return *this; + } + + template AdcValueHolder& addConvertFunc(std::invocable auto&& func_to_internal, std::invocable auto&& func_from_internal) @@ -202,10 +259,11 @@ public: template AdcValueHolder& delConvertFunc() { - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v) { // delete all conversion functions _convertFromInternal.clear(); _convertToInternal.clear(); + // restore conversion functions for internal type _convertFromInternal[_internalTypeIndex] = [](auto) {}; _convertToInternal[_internalTypeIndex] = [](auto) {}; } else { @@ -261,47 +319,35 @@ public: AdcValueHolder& operator=(const AdcValueHolder& other) = default; - template - friend AdcValueHolder& setupDefaultConvFunc(AdcValueHolder& holder) - { - auto t_index = std::type_index(typeid(ValueT)); - - if (t_index != holder._internalTypeIndex) { - throw std::system_error(AdcValueHolderErrorCode::ERROR_INTERNAL_TYPE_MISMATCH); - } - - AdcValueHolder::setupDefaultConvertFunc(&holder); - - return holder; - } - - protected: /* STATIC HELPER METHODS */ template - static void setupDefaultConvertFuncImpl(AdcValueHolder* holder) + static void setupTrivialConvertFuncImpl(AdcValueHolder* holder) { if constexpr (I < std::tuple_size_v) { using elem_t = std::tuple_element_t; - auto t_index = std::type_index(typeid(elem_t)); - holder->_convertToInternal[t_index] = [](std::any& val) { - val = std::make_any(std::any_cast(val)); - }; + if constexpr (!std::is_same_v) { + auto t_index = std::type_index(typeid(elem_t)); - holder->_convertFromInternal[t_index] = [](std::any& val) { - val = std::make_any(std::any_cast(val)); - }; + holder->_convertToInternal[t_index] = [](std::any& val) { + val = std::make_any(std::any_cast(val)); + }; - AdcValueHolder::setupDefaultConvertFuncImpl(holder); + holder->_convertFromInternal[t_index] = [](std::any& val) { + val = std::make_any(std::any_cast(val)); + }; + } + + AdcValueHolder::setupTrivialConvertFuncImpl(holder); } } template - static void setupDefaultConvertFunc(AdcValueHolder* holder) + static void setupTrivialConvertFunc(AdcValueHolder* holder) { - setupDefaultConvertFuncImpl(holder); + setupTrivialConvertFuncImpl(holder); } }; diff --git a/device/adc_device_command.h b/device/adc_device_command.h index b823ec9..50081c9 100644 --- a/device/adc_device_command.h +++ b/device/adc_device_command.h @@ -66,7 +66,7 @@ public: _execFuncWrapper = [wrapper = traits::adc_pf_wrapper(std::forward(exec_func)), this]() { auto& exec_func = std::get<0>(wrapper); - value_t val = *_commandArgUptr; + value_t val = std::any_cast(*_commandArgUptr); exec_func(val); };