commit 75ec7065d28f93d5edf57b77528322d8e51ad001 Author: Timur A. Fatkhullin Date: Fri Jan 19 17:53:41 2024 +0300 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4a0b530 --- /dev/null +++ b/.gitignore @@ -0,0 +1,74 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* +CMakeLists.txt.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..d427221 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.14) + +project(ADC LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(ADC_DEVICE_HEADERS + device/adc_value_holder.h +) + + +include(GNUInstallDirs) + +add_library(${PROJECT_NAME} INTERFACE ${ADC_DEVICE_HEADERS}) +target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_20) +# target_link_libraries(${PROJECT_NAME} INTERFACE ASIO::ASIO) +target_include_directories( + ${PROJECT_NAME} + INTERFACE + $ + $ +) diff --git a/device/adc_value_holder.h b/device/adc_value_holder.h new file mode 100644 index 0000000..8a9774d --- /dev/null +++ b/device/adc_value_holder.h @@ -0,0 +1,365 @@ +#pragma once + +/* + + ABSTRACT DEVICE COMPONENTS LIBRARY + +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace adc +{ + +// error codes +enum class AdcValueHolderErrorCode : int { + ERROR_OK, + ERROR_NO_CONV_FUNC, + ERROR_INTERNAL_TYPE_MISMATCH, + ERROR_VALUE_IS_NOT_VALID +}; + +} // namespace adc + + +namespace std +{ + +template <> +class is_error_code_enum : public true_type +{ +}; + +} // namespace std + + +namespace adc +{ + + +namespace traits +{ + +// deduce returned type of callable +template +using adc_retval_t = std::invoke_result_t>; + + +// helper classes +template +struct adc_func_traits_helper_t; + +template +struct adc_func_traits_helper_t { + using ret_t = R; + using args_t = std::tuple<>; + using arg1_t = void; +}; + +template +struct adc_func_traits_helper_t { + using ret_t = R; + using args_t = std::tuple; + using arg1_t = Arg; +}; + + +// callable traits + +template +struct adc_func_traits; + +template +struct adc_func_traits : adc_func_traits_helper_t { +}; + +template +struct adc_func_traits : adc_func_traits_helper_t { +}; + +template +struct adc_func_traits : adc_func_traits_helper_t { +}; + +template +struct adc_func_traits : adc_func_traits_helper_t { +}; + +template +struct adc_func_traits : adc_func_traits { +}; + + +// deduce type +template +using adc_deduced_type = + std::conditional_t && !std::is_const_v>, + T, + std::remove_cvref_t>; + + +} // namespace traits + + + +// error category +struct AdcValueHolderErrorCategory : public std::error_category { + AdcValueHolderErrorCategory() : std::error_category() {} + + const char* name() const noexcept { return "DEVA_DEVICE_ATTRIBUTE"; } + + std::string message(int ec) const + { + AdcValueHolderErrorCode err = static_cast(ec); + + switch (err) { + case AdcValueHolderErrorCode::ERROR_OK: + return "OK"; + case AdcValueHolderErrorCode::ERROR_NO_CONV_FUNC: + return "conversion function is not defined"; + case AdcValueHolderErrorCode::ERROR_INTERNAL_TYPE_MISMATCH: + return "try to setup default conversion function for invalid type (internal type mismatch)"; + case AdcValueHolderErrorCode::ERROR_VALUE_IS_NOT_VALID: + return "user value is not valid"; + default: + return "UNKNOWN"; + } + } + + static const AdcValueHolderErrorCategory& get() + { + static const AdcValueHolderErrorCategory constInst; + return constInst; + } +}; + + +inline std::error_code make_error_code(AdcValueHolderErrorCode ec) +{ + return std::error_code(static_cast(ec), AdcValueHolderErrorCategory::get()); +} + + + +/* + + GENERAL-PURPOSE VALUE HOLDER CLASS WITH OPTIONAL VALIDATOR + +*/ + + +class AdcValueHolder +{ +protected: + template + using arg_t = typename traits::adc_func_traits::arg1_t; + + std::function _getterWrapper; + std::function _setterWrapper; + + std::map> _convertToInternal; + std::map> _convertFromInternal; + + 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))) + { + using value_t = arg_t; + + static_assert(std::is_same_v, "THE getter MUST NOT RETURN void type!!!"); + + using g_t = decltype(getter); + _getterWrapper = [wrapper = + std::tuple>(std::forward(getter))](std::any& val) { + auto& getter = std::get<0>(wrapper); + val = getter(); + }; + + + using vld_t = decltype(validator); + using s_t = decltype(setter); + + if constexpr (std::is_same_v) { + _setterWrapper = [wrapper = std::tuple>(std::forward(setter)), + this](const std::any& val) { + auto& setter = std::get<0>(wrapper); + + setter(std::any_cast(val)); + }; + } else { + _setterWrapper = [wrapper = std::tuple, traits::adc_deduced_type>( + std::forward(setter), std::forward(validator)), + this](const std::any& val) { + auto& setter = std::get<0>(wrapper); + auto& validator = std::get<1>(wrapper); + + auto v = std::any_cast(val); + + if (validator(v)) { + setter(v); + } else { + throw std::system_error(AdcValueHolderErrorCode::ERROR_VALUE_IS_NOT_VALID); + } + }; + } + + + // By default no conversion will be made if user value of internal type is given. + // But such a conversion may be defined explicitly by calling addConvertFunc method + _convertFromInternal[_internalTypeIndex] = [](auto) {}; + _convertToInternal[_internalTypeIndex] = [](auto) {}; + } + + + AdcValueHolder(const AdcValueHolder&) = default; + AdcValueHolder(AdcValueHolder&&) = default; + + virtual ~AdcValueHolder() = default; + + + /* PUBLIC METHODS */ + + template + AdcValueHolder& addConvertFunc(std::invocable auto&& func_to_internal, + std::invocable auto&& func_from_internal) + { + static_assert(std::is_same_v, "void IS NOT VALID TYPE!!!"); + + auto t_index = std::type_index(typeid(UT)); + + _convertToInternal[t_index] = std::forward(func_to_internal); + _convertFromInternal[t_index] = std::forward(func_from_internal); + + return *this; + } + + + template + AdcValueHolder& delConvertFunc() + { + if constexpr (std::is_same_v) { + _convertFromInternal.clear(); + _convertToInternal.clear(); + + _convertFromInternal[_internalTypeIndex] = [](auto) {}; + _convertToInternal[_internalTypeIndex] = [](auto) {}; + } else { + auto t_index = std::type_index(typeid(UT)); + + _convertFromInternal.erase(t_index); + _convertToInternal.erase(t_index); + } + + return *this; + } + + + template + operator UT() + { + std::any val; + + auto t_index = std::type_index(typeid(UT)); + auto it = _convertFromInternal.find(t_index); + + if (it == _convertFromInternal.end()) { + throw std::system_error(AdcValueHolderErrorCode::ERROR_NO_CONV_FUNC); + } + + _getterWrapper(val); + it->second(val); // call conversion function + + return std::any_cast(val); + } + + template + AdcValueHolder& operator=(const UT& value) + { + std::any val = std::make_any(value); + + auto t_index = std::type_index(typeid(UT)); + auto it = _convertToInternal.find(t_index); + + if (it == _convertToInternal.end()) { + throw std::system_error(AdcValueHolderErrorCode::ERROR_NO_CONV_FUNC); + } + + it->second(val); // call conversion function + + _setterWrapper(val); + + return *this; + } + + + AdcValueHolder& operator=(AdcValueHolder&& other) = default; + 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) + { + 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)); + }; + + holder->_convertFromInternal[t_index] = [](std::any& val) { + val = std::make_any(std::any_cast(val)); + }; + + AdcValueHolder::setupDefaultConvertFuncImpl(holder); + } + } + + template + static void setupDefaultConvertFunc(AdcValueHolder* holder) + { + setupDefaultConvertFuncImpl(holder); + } +}; + + + +} // namespace adc