#pragma once /**************************************************************************************** * ABSTRACT DEVICE COMPONENTS LIBRARY * ****************************************************************************************/ #include #include #include #include #include #include #include // #include #include #include #include "adclib_common.h" namespace adc { 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 "invalid command ID"; case AdcDeviceErrorCode::ERROR_NO_ATTR_ID: return "invalid 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; } }; inline std::error_code make_error_code(AdcDeviceErrorCode ec) { return std::error_code(static_cast(ec), AdcDeviceErrorCategory::get()); } template ATTR_ID_T = std::string_view, std::formattable CMD_ID_T = std::string_view> class AdcGenericDevice { protected: template struct attr_t { ATTR_ID_T id; 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 operator adc_result_t() { auto const& attr = _dev._attrs.template get>(_id); if (attr) { return attr.value().getter(); } // error if (attr.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 const& attr = _dev._attrs.template get>(_id); if (attr) { return attr.value().setter(std::forward(val)); } if (attr.error() == 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: 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) { _commands.emplace(std::move(id), std::forward(exec_func)); } template void addAttr(ATTR_ID_T id, GT&& getter, ST&& setter, std::tuple = std::tuple()) { // deduce attribute value type 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)}); // if constexpr (sizeof...(VTs)) { // auto func = [id, this]() { // using u_t = std::tuple_element_t>; // if constexpr (!requires(u_t u, v_t v) { // u = v; // v = u; // }) { // static_assert(false, "Invalid user type!"); // } // _attrs.push(std::move(id), attr_t{.getter = []() {}, .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