#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 "ADCLIB-DEVICE-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 concept adclib_command_exec_t = snplib::snplib_callable_c && (snplib::snplib_func_traits_t::arity == 0) && std::same_as::ret_t>; template concept adclib_attr_getter_c = std::same_as || requires(T t) { { t() } -> adc_result_c; }; template concept adclib_attr_setter_c = std::same_as || (snplib::snplib_callable_c && (snplib::snplib_func_traits_t::arity >= 1) && std::same_as::ret_t>); // deduce value type from getter and setter template using adclib_attr_value_deduced_t = std::conditional_t< std::is_null_pointer_v, std::conditional_t, void, std::remove_cvref_t>>, typename std::invoke_result_t::value_type>; template struct adclib_is_deduced_void_t { static constexpr bool value = std::is_void_v>; }; // conversional function signature: // "from": adc_result_t func(const adc_result_t&) // "to": adc_result_t func(const adc_result_t&) template concept adclib_attr_conv_from_func_c = std::same_as || (std::invocable&> && !std::is_void_v&>>); template concept adclib_attr_conv_to_func_c = std::same_as || (snplib::snplib_callable_c && (snplib::snplib_func_traits_t::arity == 1) && std::same_as, typename snplib::snplib_func_traits_t::ret_t>); template CONV_FROM_T, adclib_attr_conv_to_func_c CONV_TO_T> using adclib_attr_user_deduced_t = std::conditional_t, std::conditional_t, void, std::remove_cvref_t>>, typename snplib::snplib_func_traits_t::ret_t>; template DEV_ID_T = std::string, std::formattable ATTR_ID_T = std::string, std::formattable CMD_ID_T = std::string> class AdcGenericDevice { protected: template struct attr_t { std::function()> getter{}; std::function setter{}; // template // requires(requires(VT v, UT u) { v = u; } && !std::same_as) // attr_t& operator=(attr_t const& other) // { // std::println("ATTR::operator="); // if (other.getter) { // getter = [&other]() -> adc_result_t { // auto val = other.getter(); // if (val) { // return static_cast(val.value()); // } // return std::unexpected(val.error()); // }; // } else { // write-only attribute // getter = nullptr; // } // if (other.setter) { // setter = [&other](VT const& v) { return other.setter(static_cast(v)); }; // } else { // read-only attribute // setter = nullptr; // } // return *this; // } template requires requires(VT v, UT u) { v = u; } operator attr_t() const { return attr_t{.getter = [this]() -> adc_result_t { auto val = getter(); if (val) { return static_cast(val.value()); } else { return std::unexpected(val.error()); } }, .setter = [this](UT const& u) { return setter(static_cast(u)); }}; } 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() const { auto attr = _dev._attrs.template get>(_id); if (attr) { if (attr.value().getter) { return attr.value().getter(); } return std::unexpected(AdcDeviceErrorCode::ERROR_WO_ATTR); } // 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 attr = _dev._attrs.template get>>(_id); if (attr) { if (attr.value().setter) { return attr.value().setter(std::forward(val)); } return AdcDeviceErrorCode::ERROR_RO_ATTR; } 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; }; DEV_ID_T _devId; // std::unordered_map> _commands{}; std::unordered_map> _commands{}; snplib::HeterogenMap _attrs{}; public: static constexpr std::tuple arithmetic_types{}; typedef DEV_ID_T device_id_t; typedef ATTR_ID_T attr_id_t; typedef CMD_ID_T cmd_id_t; enum AttrAccessType { ATTR_ACCESS_RW, ATTR_ACCESS_RO, ATTR_ACCESS_WO }; AdcGenericDevice(device_id_t id) : _devId(id) {} virtual ~AdcGenericDevice() = default; device_id_t id() const { return _devId; } // template // void addCommand(CMD_ID_T id, ET&& exec_func) // { // _commands.emplace(std::move(id), std::forward(exec_func)); // } 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<>{}) { using v_t = adclib_attr_value_deduced_t; static_assert(!std::is_void_v, "Getter and setter cannot be nullptr_t at the same time!"); if constexpr (!std::is_null_pointer_v && !std::is_null_pointer_v) { static_assert(std::invocable, "Invalid setter argument type!"); } _attrs.push(std::move(id), attr_t{.getter = std::forward(getter), .setter = std::forward(setter)}, std::tuple...>{}); } template requires(sizeof...(FuncTs) > 1) void addAttr(ATTR_ID_T id, GT&& getter, ST&& setter, FuncTs&&... cnv_pairs) { static constexpr size_t NFUNCS = sizeof...(FuncTs); static_assert(NFUNCS % 2 == 0, "Number of conversional functions must be an even!"); using v_t = adclib_attr_value_deduced_t; static_assert(!std::is_void_v, "Getter and setter cannot be nullptr_t at the same time!"); [... cnv_pairs_cap = std::forward(cnv_pairs), id, getter_cap = std::forward(getter), setter_cap = std::forward(setter), this](std::index_sequence) mutable { // auto&& tp = std::forward_as_tuple(std::forward(cnv_pairs_cap)...); auto tp = std::forward_as_tuple(std::forward(cnv_pairs_cap)...); // static_assert( // std::disjunction_v, // std::tuple_element_t>...>, // "Getter and setter cannot be nullptr_t at the same time!"); auto from_cnv_func = [](TpT const& func_tp) { using from_fn_t = std::decay_t>; using to_fn_t = std::decay_t>; using u_t = typename adclib_attr_user_deduced_t::value_type; return [&func_tp](attr_t const& attr) mutable -> attr_t { return attr_t{ .getter = [&attr, &func_tp]() mutable -> adc_result_t { return std::forward>(std::get(func_tp))(attr.getter()); }, .setter = [&attr, &func_tp](u_t const& uv) mutable -> adc_error_t { auto val = std::forward>(std::get(func_tp))(uv); if (val) { return attr.setter(val.value()); } else { return val.error(); } }}; }; }; // auto from_cnv_func = [](TpT&& func_tp) { // using tp_t = std::remove_cvref_t; // using from_fn_t = std::decay_t>; // using to_fn_t = std::decay_t>; // using u_t = adclib_attr_user_deduced_t; // return [func_tp_cap = std::forward(func_tp)](attr_t const& attr) mutable -> attr_t { // using tp_cap_t = decltype(func_tp_cap); // return attr_t{.getter = [&attr, func_tp_cap_cap = std::forward( // func_tp_cap)]() mutable -> adc_result_t { // using tp_cap_cap_t = decltype(func_tp_cap_cap); // return std::forward>( // std::get(std::forward(func_tp_cap_cap)))(attr.getter()); // }, // .setter = [&attr, func_tp_cap_cap = std::forward(func_tp_cap)]( // u_t const& uv) mutable -> adc_error_t { // using tp_cap_cap_t = decltype(func_tp_cap_cap); // auto val = std::forward>( // std::get(std::forward(func_tp_cap_cap)))(uv); // if (val) { // return attr.setter(val.value()); // } else { // return val.error(); // } // }}; // }; // }; auto to_cnv_func = [id, this](TpT const& func_tp) { using from_fn_t = std::decay_t>; using to_fn_t = std::decay_t>; using u_t = typename adclib_attr_user_deduced_t::value_type; return [id, this](attr_t const&) -> attr_t { return _attrs.template get>(id).value(); }; }; std::apply( [this](auto&&... args) { _attrs.pushWithCnv(std::forward(args)...); }, std::tuple_cat( std::forward_as_tuple(id, attr_t{.getter = std::forward(getter_cap), .setter = std::forward(setter_cap)}), std::tuple_cat(std::forward_as_tuple(from_cnv_func.template operator()(tp), to_cnv_func.template operator()(tp))...))); }(std::make_index_sequence{}); } // add attribute of one of arithmetic type template requires std::is_arithmetic_v> void addArithAttr(ATTR_ID_T id, GT&& getter, ST&& setter) { addAttr(std::move(id), std::forward(getter), std::forward(setter), AdcGenericDevice::arithmetic_types); } adc_error_t operator()(CMD_ID_T id) { if (auto it = _commands.find(id); it != _commands.end()) { // return (*it)(); return it->second(); } return AdcDeviceErrorCode::ERROR_NO_CMD_ID; } auto operator[](ATTR_ID_T id) { return attr_proxy_t{*this, std::move(id)}; } // function-like access to attributes template adc_result_t attr(ATTR_ID_T id) { return attr_proxy_t{*this, std::move(id)}; } template adc_error_t attr(ATTR_ID_T id, VT const& v) { return attr_proxy_t{*this, std::move(id)} = v; } }; } // namespace adc