#pragma once /**************************************************************************************** * ABSTRACT DEVICE COMPONENTS LIBRARY * ****************************************************************************************/ #include #include #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 VT&) // "to": adc_result_t func(const UT&) 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::value_type>; template DEV_ID_T = std::string, std::formattable ATTR_ID_T = std::string, std::formattable CMD_ID_T = std::string> class AdcGenericDevice { protected: template using attr_getter_t = std::function()>; template using attr_setter_t = std::function; typedef std::vector serialized_t; typedef std::function attr_serial_func_t; typedef std::function attr_deserial_func_t; struct attr_t { attr_t(AdcGenericDevice& dev, ATTR_ID_T id) : _dev(dev), _id(std::move(id)) {} template operator adc_result_t() const { auto getter = _dev._attrGetter.template get>(_id); if (getter) { if (getter.value()) { return getter.value()(); } else { return std::unexpected(AdcDeviceErrorCode::ERROR_WO_ATTR); } } if (getter.error() == snplib::hmap_error_e::ERROR_INVALID_KEY) { return std::unexpected(AdcDeviceErrorCode::ERROR_NO_ATTR_ID); } // fallback ... return std::unexpected(AdcDeviceErrorCode::ERROR_UNKNOWN); } template adc_error_t operator=(VT&& val) { auto setter = _dev._attrSetter.template get>>(_id); if (setter) { if (setter.value()) { return setter.value()(std::forward(val)); } else { return AdcDeviceErrorCode::ERROR_RO_ATTR; } } if (setter.error() == snplib::hmap_error_e::ERROR_INVALID_KEY) { return AdcDeviceErrorCode::ERROR_NO_ATTR_ID; } // fallback ... return AdcDeviceErrorCode::ERROR_UNKNOWN; } private: AdcGenericDevice& _dev; ATTR_ID_T _id; }; DEV_ID_T _devId; std::unordered_map> _commands{}; std::unordered_map _attrs{}; snplib::hmap _attrGetter{}; snplib::hmap _attrSetter{}; // default convert functions for char-range types template requires((snplib::snplib_char_range_c || snplib::snplib_pointer_to_char_c) && (snplib::snplib_char_range_c || snplib::snplib_pointer_to_char_c)) static inline std::function(VR const&)> default_char_range_conv_from = [](VR const& val) -> adc_result_t { if constexpr (std::same_as) { return val; } else if constexpr (std::constructible_from) { return UR(val); } else if constexpr (std::is_pointer_v>) { // char*, const char*, char[] return default_char_range_conv_from(std::string_view(val)); } else if constexpr (std::constructible_from, std::ranges::sentinel_t>) { return UR(val.begin(), val.end()); } else if constexpr (std::constructible_from, std::ranges::const_sentinel_t>) { return UR(val.cbegin(), val.cend()); } else if constexpr (std::same_as && std::is_pointer_v) { return val.data(); } else { static_assert(false, "UNSUPPORTED CHAR-RANGE TYPE!"); } }; template static inline std::function(UR const&)> default_char_range_conv_to = default_char_range_conv_from; // default convert functions for std::filesystem::path type template static inline std::function(std::filesystem::path const&)> default_fspath_conv_from = [](std::filesystem::path const& val) -> adc_result_t { if constexpr (std::same_as) { return val; } else if constexpr (std::constructible_from) { return UT(val); } else if constexpr (std::is_pointer_v> && (std::same_as || std::same_as)) { // char*/wchar_t* return val.c_str(); } else if constexpr (snplib::snplib_char_range_c) { using iter_t = std::filesystem::path::value_type*; auto sz = val.native().size(); if constexpr (std::constructible_from) { return UT(val.c_str(), val.c_str() + sz); } else if constexpr (std::constructible_from) { return UT(val.c_str(), sz); } else { static_assert(false, "UNSUPPORTED CHAR-RANGE TYPE!"); } } else { static_assert(false, "UNSUPPORTED TYPE!"); } }; template static inline std::function(UT const&)> default_fspath_conv_to = [](UT const& uval) -> adc_result_t { if constexpr (std::same_as) { return uval; } else if constexpr (std::constructible_from) { return std::filesystem::path(uval); } else if constexpr (snplib::snplib_char_range_c) { return std::filesystem::path(uval.begin(), uval.end()); } else { static_assert(false, "UNSUPPORTED TYPE!"); } }; public: inline static constexpr std::tuple arithmetic_types{}; typedef std::tuple arithmetics_t; typedef std::tuple, std::list, const char*> char_ranges_t; #ifdef _WIN32 typedef std::tuple, std::list, const wchar_t*> fspath_repr_t; #else typedef std::tuple, std::list, const char*> fspath_repr_t; #endif 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) { 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!"); } _attrGetter.push(id, attr_getter_t(std::forward(getter))); _attrSetter.push(id, attr_setter_t(std::forward(setter))); } template requires(sizeof...(UTs) > 0) void addAttr(ATTR_ID_T id, GT&& getter, ST&& setter, 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!"); } using tp_t = std::tuple; [id = std::move(id), getter_cap = std::forward(getter), setter_cap = std::forward(setter), this](std::index_sequence) mutable { return std::apply( [this](auto&&... args) { addAttr(std::forward(args)...); }, std::tuple_cat( std::forward_as_tuple(id, std::forward(getter_cap), std::forward(setter_cap)), std::tuple_cat(std::forward_as_tuple( decltype(_attrGetter)::template trivial_conv_from>, decltype(_attrGetter)::template trivial_conv_to>)...))); }(std::make_index_sequence{}); } // // conversional functions are the same as defined in the snplib::hmap class // (it must convert attribute values instead of getters or setters itself!) 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!"); [id = std::move(id), getter_cap = std::forward(getter), setter_cap = std::forward(setter), tp = std::forward_as_tuple(std::forward(cnv_pairs)...), this](std::index_sequence) mutable { // auto gt_cnv_from_func = [tp]() { using tp_t = decltype(tp); using from_fn_t = std::decay_t>; using to_fn_t = std::decay_t>; using u_t = adclib_attr_user_deduced_t; return [tp = std::move(tp)](attr_getter_t const& gt) -> adc_result_t> { return [tp = std::move(tp), >]() -> adc_result_t { auto v = gt(); if (v) { // convert from inner type to user's ("convert-from" function) // it returns adc_result_t return std::forward>(std::get(tp))(v.value()); } return std::unexpected(v.error()); }; }; }; auto st_cnv_from_func = [tp]() { using tp_t = decltype(tp); using from_fn_t = std::decay_t>; using to_fn_t = std::decay_t>; using u_t = adclib_attr_user_deduced_t; return [tp = std::move(tp)](attr_setter_t const& st) -> adc_result_t> { return [tp = std::move(tp), &st](u_t const& uval) -> adc_error_t { // convert from user type to inner ("convert-to" function)! auto val = std::forward>(std::get(tp))(uval); if (val) { return st(val.value()); } return val.error(); }; }; }; auto gt_cnv_to_func = [tp]() { using tp_t = decltype(tp); using from_fn_t = std::decay_t>; using to_fn_t = std::decay_t>; using u_t = adclib_attr_user_deduced_t; return [tp = std::move(tp)](attr_getter_t const& gt) -> adc_result_t> { return [tp = std::move(tp), >]() -> adc_result_t { auto uv = gt(); if (uv) { // convert to inner type from user's ("convert-to" function) // it returns adc_result_t return std::forward>(std::get(tp))(uv.value()); } return std::unexpected(uv.error()); }; }; }; auto st_cnv_to_func = [tp]() { using tp_t = decltype(tp); using from_fn_t = std::decay_t>; using to_fn_t = std::decay_t>; using u_t = adclib_attr_user_deduced_t; return [tp = std::move(tp)](attr_setter_t const& st) -> adc_result_t> { return [tp = std::move(tp), &st](v_t const& val) -> adc_error_t { // convert to user type from inner ("convert-from" function)! auto uval = std::forward>(std::get(tp))(val); if (uval) { return st(uval.value()); } return uval.error(); }; }; }; std::apply( [this](auto&&... args) { _attrGetter.push(std::forward(args)...); }, std::tuple_cat( std::forward_as_tuple(id, attr_getter_t(std::forward(getter_cap))), std::tuple_cat(std::forward_as_tuple(gt_cnv_from_func.template operator()<2 * Is>(), gt_cnv_to_func.template operator()<2 * Is>())...))); std::apply( [this](auto&&... args) { _attrSetter.push(std::forward(args)...); }, std::tuple_cat( std::forward_as_tuple(id, attr_setter_t(std::forward(setter_cap))), std::tuple_cat(std::forward_as_tuple(st_cnv_from_func.template operator()<2 * Is>(), st_cnv_to_func.template operator()<2 * Is>())...))); }(std::make_index_sequence{}); return; [... 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 gt_cnv_from_func = [](TpT const& tpl) { using from_fn_t = std::decay_t>; using to_fn_t = std::decay_t>; using u_t = adclib_attr_user_deduced_t; // using u_t = std::invoke_result_t::value_type; // static_assert(requires(v_t v, u_t u) { u = v; }, "Invalid deduced user type!"); return [&tpl](attr_getter_t const& gt) -> adc_result_t> { return [&tpl, >]() -> adc_result_t { auto v = gt(); if (v) { // convert from inner type to user's ("convert-from" function) // it returns adc_result_t return std::forward>(std::get(tpl))(v.value()); } return std::unexpected(v.error()); }; }; }; auto st_cnv_from_func = [](TpT const& tpl) { using from_fn_t = std::decay_t>; using to_fn_t = std::decay_t>; using u_t = adclib_attr_user_deduced_t; // using u_t = std::invoke_result_t::value_type; // static_assert(requires(v_t v, u_t u) { u = v; }, "Invalid deduced user type!"); return [&tpl](attr_setter_t const& st) -> adc_result_t> { return [&tpl, &st](u_t const& uval) -> adc_error_t { // convert from user type to inner ("convert-to" function)! auto val = std::forward>(std::get(tpl))(uval); if (val) { return st(val.value()); } return val.error(); }; }; }; auto gt_cnv_to_func = [](TpT const& tpl) { using from_fn_t = std::decay_t>; using to_fn_t = std::decay_t>; using u_t = adclib_attr_user_deduced_t; // using u_t = std::invoke_result_t::value_type; // static_assert(requires(v_t v, u_t u) { v = u; }, "Invalid deduced user type!"); return [&tpl](attr_getter_t const& gt) -> adc_result_t> { return [&tpl, >]() -> adc_result_t { auto uv = gt(); if (uv) { // convert to inner type from user's ("convert-to" function) // it returns adc_result_t return std::forward>(std::get(tpl))(uv.value()); } return std::unexpected(uv.error()); }; }; }; auto st_cnv_to_func = [](TpT const& tpl) { using from_fn_t = std::decay_t>; using to_fn_t = std::decay_t>; using u_t = adclib_attr_user_deduced_t; // using u_t = std::invoke_result_t::value_type; // static_assert(requires(v_t v, u_t u) { v = u; }, "Invalid deduced user type!"); return [&tpl](attr_setter_t const& st) -> adc_result_t> { return [&tpl, &st](v_t const& val) -> adc_error_t { // convert to user type from inner ("convert-from" function)! auto uval = std::forward>(std::get(tpl))(val); if (uval) { return st(uval.value()); } return uval.error(); }; }; }; std::apply( [this](auto&&... args) { _attrGetter.push(std::forward(args)...); }, std::tuple_cat( std::forward_as_tuple(id, attr_getter_t(std::forward(getter_cap))), std::tuple_cat(std::forward_as_tuple(gt_cnv_from_func.template operator()<2 * Is>(tp), gt_cnv_to_func.template operator()<2 * Is>(tp))...))); std::apply( [this](auto&&... args) { _attrSetter.push(std::forward(args)...); }, std::tuple_cat( std::forward_as_tuple(id, attr_setter_t(std::forward(setter_cap))), std::tuple_cat(std::forward_as_tuple(st_cnv_from_func.template operator()<2 * Is>(tp), st_cnv_to_func.template operator()<2 * Is>(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); addAttr(std::move(id), std::forward(getter), std::forward(setter), AdcGenericDevice::arithmetics_t{}); } // add attribute of char-range type template requires snplib::snplib_char_range_c> void addCharRangeAttr(ATTR_ID_T id, GT&& getter, ST&& setter) { 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!"); } [&id, getter_cap = std::forward(getter), setter_cap = std::forward(setter), this](std::index_sequence) mutable { std::apply( [this](auto&&... args) { addAttr(std::forward(args)...); }, std::tuple_cat( std::forward_as_tuple(id, std::forward(getter_cap), std::forward(setter_cap)), std::tuple_cat(std::forward_as_tuple( default_char_range_conv_from>, default_char_range_conv_to>)...))); }(std::make_index_sequence>{}); } // add attribute of std::filesystem::path type template requires std::same_as, std::filesystem::path> void addFSPathAttr(ATTR_ID_T id, GT&& getter, ST&& setter) { 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!"); } [&id, getter_cap = std::forward(getter), setter_cap = std::forward(setter), this](std::index_sequence) mutable { std::apply([this](auto&&... args) { addAttr(std::forward(args)...); }, std::tuple_cat( std::forward_as_tuple(id, std::forward(getter_cap), std::forward(setter_cap)), std::tuple_cat(std::forward_as_tuple( default_fspath_conv_from>, default_fspath_conv_to>)...))); }(std::make_index_sequence>{}); } 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_t{*this, std::move(id)}; } // function-like access to attributes template adc_result_t attr(ATTR_ID_T id) { return attr_t{*this, std::move(id)}; } template adc_error_t attr(ATTR_ID_T id, VT const& v) { return attr_t{*this, std::move(id)} = v; } }; } // namespace adc