From 6ebea9e29d1a0c79bbba49d5e05a35513fba04cb Mon Sep 17 00:00:00 2001 From: "Timur A. Fatkhullin" Date: Sun, 21 Jun 2026 01:48:32 +0300 Subject: [PATCH] ... --- .gitignore | 1 + CMakeLists.txt | 26 ++++- examples/device_exam.cpp | 28 +++++ include/adclib/adclib_common.h | 22 ++++ include/adclib/adclib_device.h | 197 ++++++++++++++++++++++++++++++--- 5 files changed, 256 insertions(+), 18 deletions(-) create mode 100644 examples/device_exam.cpp create mode 100644 include/adclib/adclib_common.h diff --git a/.gitignore b/.gitignore index ca8b61e..7bc9380 100644 --- a/.gitignore +++ b/.gitignore @@ -61,6 +61,7 @@ CMakeLists.txt.user* *.opensdf *.vcxproj *vcxproj.* +.vscode/ # MinGW generated files *.Debug diff --git a/CMakeLists.txt b/CMakeLists.txt index de20950..588bb58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,12 @@ cmake_minimum_required(VERSION 3.16) -project(ADCLIB LANGUAGES CXX DESCRIPTION "Abstract Device Components Library") +project(adclib LANGUAGES CXX DESCRIPTION "Abstract Device Components Library") set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) +option(BUILD_EXAM "Build examples" ON) + # ------- dependencies ------- include(FetchContent) @@ -21,11 +23,25 @@ FetchContent_Declare( FetchContent_MakeAvailable(snplib) -set(ADCLIB_HEADERS include/adclib/adclib_device.h) -add_library(${PROJECT_NAME} INTERFACE ${ADCLIB_HEADERS}) +set(ADCLIB_HEADERS include/adclib/adclib_device.h + include/adclib/adclib_common.h) -include(GNUInstallDirs) -install(TARGETS ADCLIB +add_library(${PROJECT_NAME} INTERFACE ${ADCLIB_HEADERS}) +target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_23) +target_include_directories( + ${PROJECT_NAME} + INTERFACE + "$" + "$" +) + +if(BUILD_EXAM) + add_executable(device_exam examples/device_exam.cpp) + target_link_libraries(device_exam PRIVATE ${PROJECT_NAME}) +endif() + +include(GNUInstallDirs) +install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) diff --git a/examples/device_exam.cpp b/examples/device_exam.cpp new file mode 100644 index 0000000..06acbce --- /dev/null +++ b/examples/device_exam.cpp @@ -0,0 +1,28 @@ +#include + +#include + +using namespace adc; + +int main() +{ + int i = 10; + double d = 8.1; + std::string s{"dwedjkwlk"}; + + AdcGenericDevice<> dev; + + dev.addAttr( + "1", [&i]() { return adc_result_t{i}; }, + [&i](int ii) -> adc_error_t { + i = ii; + return AdcDeviceErrorCode::ERROR_OK; + }); + + adc_result_t ri = dev["1"]; + if (ri) { + std::println("dev[1] = {}", ri.value()); + } + + return 0; +} \ No newline at end of file diff --git a/include/adclib/adclib_common.h b/include/adclib/adclib_common.h new file mode 100644 index 0000000..e580750 --- /dev/null +++ b/include/adclib/adclib_common.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include + +namespace adc +{ + +// library-wide error type definition + +typedef std::error_code adc_error_t; + +template +using adc_result_t = std::expected; + +template +concept adc_result_c = requires { + [](std::type_identity>) {}(std::type_identity>()); +}; + +} // namespace adc \ No newline at end of file diff --git a/include/adclib/adclib_device.h b/include/adclib/adclib_device.h index f1fd018..83bdaa6 100644 --- a/include/adclib/adclib_device.h +++ b/include/adclib/adclib_device.h @@ -4,50 +4,191 @@ * ABSTRACT DEVICE COMPONENTS LIBRARY * ****************************************************************************************/ +#include +#include +#include +#include +#include + + +// #include #include +#include + + + +#include "adclib_common.h" namespace adc { -enum class adc_device_error_code_t : int { ERROR_OK, ERROR_RO_ATTR, ERROR_WO_ATTR, ERROR_ATTR_RANGE }; + +enum class AdcDeviceErrorCode : int { + ERROR_OK, + ERROR_NO_CMD_ID, + ERROR_NO_ATTR_ID, + ERROR_RO_ATTR, + ERROR_WO_ATTR, + ERROR_ATTR_RANGE, + ERROR_SERIALIZATION, + ERROR_DESERIALIZATION, + ERROR_UNKNOWN +}; } // namespace adc +namespace std +{ + +template <> +class is_error_code_enum : public true_type +{ +}; + +} // namespace std + + + namespace adc { +// error category + +struct AdcDeviceErrorCategory : std::error_category { + const char* name() const noexcept + { + return "MCC-DESERIALIZER-ERR-CATEGORY"; + } + + std::string message(int ec) const + { + AdcDeviceErrorCode err = static_cast(ec); + + switch (err) { + case AdcDeviceErrorCode::ERROR_OK: + return "OK"; + case AdcDeviceErrorCode::ERROR_NO_CMD_ID: + return "invalud command ID"; + case AdcDeviceErrorCode::ERROR_NO_ATTR_ID: + return "invalud attribute ID"; + case AdcDeviceErrorCode::ERROR_RO_ATTR: + return "read-only attribute"; + case AdcDeviceErrorCode::ERROR_WO_ATTR: + return "write-only attribute"; + case AdcDeviceErrorCode::ERROR_ATTR_RANGE: + return "value is out of attribute range "; + case AdcDeviceErrorCode::ERROR_SERIALIZATION: + return "serialization error"; + case AdcDeviceErrorCode::ERROR_DESERIALIZATION: + return "deserialization error"; + default: + return "UNKNOWN"; + } + } + + static const AdcDeviceErrorCategory& get() + { + static const AdcDeviceErrorCategory constInst; + return constInst; + } +}; + template ATTR_ID_T = std::string_view, std::formattable CMD_ID_T = std::string_view> -class adc_device_t +class AdcGenericDevice { -public: - typedef std::error_code error_t; - - template - using op_result_t = std::expected; - protected: template struct attr_t { ATTR_ID_T id; - std::function()> getter; - std::function setter; + + std::function()> getter; + std::function setter; + + adc_error_t serialize(snplib::snplib_output_char_range_c auto& output, + snplib::snplib_serialization_params_c auto const& params) + { + if (!getter) { + return AdcDeviceErrorCode::ERROR_WO_ATTR; + } + + auto val = getter(); + if (val) { + auto ret = snplib::snplib_serializer_t{}(output, val.value(), params); + if (ret) { + return snplib::snplib_deduced_error(ret, AdcDeviceErrorCode::ERROR_SERIALIZATION); + } + + return AdcDeviceErrorCode::ERROR_OK; + } + + return val.error(); + } + + adc_error_t deserialize(snplib::snplib_input_char_range_c auto const& input, + snplib::snplib_serialization_params_c auto const& params) + { + if (!setter) { + return AdcDeviceErrorCode::ERROR_RO_ATTR; + } + + VT value; // WARNING: must be default-constructible!!! + auto ret = snplib::snplib_deserializer_t{}(input, value, params); + if (ret) { + return snplib::snplib_deduced_error(ret, AdcDeviceErrorCode::ERROR_DESERIALIZATION); + } + + return setter(value); + } }; struct attr_proxy_t { + attr_proxy_t(AdcGenericDevice& dev, ATTR_ID_T id) : _dev(dev), _id(std::move(id)) {} + template - VT get(ATTR_ID_T id) + operator adc_result_t() { + auto v = _dev._attrs.template get(_id); + if (v) { + return v.value(); + } + + // error + if (v.error() == snplib::HeterogenMap::ERROR_NO_ELEM) { + return std::unexpected(AdcDeviceErrorCode::ERROR_NO_ATTR_ID); + } + + return std::unexpected(AdcDeviceErrorCode::ERROR_UNKNOWN); } + + template + adc_error_t operator=(VT&& val) + { + auto err = _dev._attrs.set(_id, std::forward(val)); + if (err == snplib::HeterogenMap::ERROR_OK) { + return AdcDeviceErrorCode::ERROR_OK; + } else if (err == snplib::HeterogenMap::ERROR_NO_ELEM) { + return AdcDeviceErrorCode::ERROR_NO_ATTR_ID; + } else { + return AdcDeviceErrorCode::ERROR_UNKNOWN; + } + } + + protected: + AdcGenericDevice& _dev; + ATTR_ID_T _id; }; std::unordered_map> _commands{}; snplib::HeterogenMap _attrs{}; public: - adc_device_t() {} + typedef ATTR_ID_T attr_id_t; + typedef CMD_ID_T cmd_id_t; + + AdcGenericDevice() {} template void addCommand(CMD_ID_T id, ET&& exec_func) @@ -59,12 +200,42 @@ public: void addAttr(ATTR_ID_T id, GT&& getter, ST&& setter) { // deduce attribute value type - using v_t = std::invoke_result_t; + using ret_t = std::invoke_result_t; + using v_t = typename ret_t::value_type; + + static_assert(adc_result_c, "Invalid return type of the getter!"); static_assert(std::invocable, "Invalid setter type!"); + static_assert(std::convertible_to, adc_error_t>, + "Invalid return type of the setter!"); _attrs.push(std::move(id), attr_t{.getter = std::forward(getter), .setter = std::forward(setter)}); } + + adc_error_t operator()(CMD_ID_T id) + { + if (auto it = _commands.find(id); it != _commands.end()) { + return (*it)(); + } + + return AdcDeviceErrorCode::ERROR_NO_CMD_ID; + } + + + template + adc_result_t attr(ATTR_ID_T id) + { + } + + template + adc_error_t attr(ATTR_ID_T id, VT const& v) + { + } + + auto operator[](ATTR_ID_T id) + { + return attr_proxy_t{*this, std::move(id)}; + } }; } // namespace adc