Files
ADCLIB/include/adclib/adclib_device.h
Timur A. Fatkhullin 168b0f25ad ...
2026-06-21 23:13:19 +03:00

326 lines
10 KiB
C++

#pragma once
/****************************************************************************************
* ABSTRACT DEVICE COMPONENTS LIBRARY *
****************************************************************************************/
#include <concepts>
#include <format>
#include <functional>
#include <string>
#include <system_error>
#include <type_traits>
#include <utility>
#include <snipplib/concepts/snplib_traits.h>
#include <snipplib/containers/snplib_hmap.h>
#include <snipplib/serialization/snplib_serialization.h>
#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<adc::AdcDeviceErrorCode> : 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<AdcDeviceErrorCode>(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<int>(ec), AdcDeviceErrorCategory::get());
}
namespace details
{
template <typename T>
concept snplib_getter_c = std::same_as<T, std::nullptr_t> || requires(T t) {
{ t() } -> adc_result_c;
};
template <typename T>
concept snplib_setter_c =
std::same_as<T, std::nullptr_t> || (snplib::snplib_callable_c<T> && (snplib::snplib_func_traits_t<T>::arity >= 1) &&
std::same_as<adc_error_t, typename snplib::snplib_func_traits_t<T>::ret_t>);
// deduce value type from getter and setter
template <snplib_getter_c GT, snplib_setter_c ST>
using snplib_attr_value_deduced_t =
std::conditional_t<std::is_null_pointer_v<GT>,
std::conditional_t<std::is_null_pointer_v<ST>, void, snplib::snplib_func_arg1_t<ST>>,
typename std::invoke_result_t<GT>::value_type>;
} // namespace details
template <std::formattable<char> ATTR_ID_T = std::string_view, std::formattable<char> CMD_ID_T = std::string_view>
class AdcGenericDevice
{
protected:
template <typename VT>
struct attr_t {
ATTR_ID_T id;
std::function<adc_result_t<VT>()> getter;
std::function<adc_error_t(VT const&)> 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<VT>{}(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<VT>{}(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 <typename VT>
operator adc_result_t<VT>()
{
auto const& attr = _dev._attrs.template get<attr_t<VT>>(_id);
if (attr) {
if (attr.value().getter) {
return attr.value().getter();
}
return std::unexpected(AdcDeviceErrorCode::ERROR_WO_ATTR);
}
// error
if (attr.error() == snplib::HeterogenMap<ATTR_ID_T>::ERROR_NO_ELEM) {
return std::unexpected(AdcDeviceErrorCode::ERROR_NO_ATTR_ID);
}
return std::unexpected(AdcDeviceErrorCode::ERROR_UNKNOWN);
}
template <typename VT>
adc_error_t operator=(VT&& val)
{
auto const& attr = _dev._attrs.template get<attr_t<VT>>(_id);
if (attr) {
if (attr.value().setter) {
return attr.value().setter(std::forward<VT>(val));
}
return AdcDeviceErrorCode::ERROR_RO_ATTR;
}
if (attr.error() == snplib::HeterogenMap<ATTR_ID_T>::ERROR_NO_ELEM) {
return AdcDeviceErrorCode::ERROR_NO_ATTR_ID;
} else {
return AdcDeviceErrorCode::ERROR_UNKNOWN;
}
}
protected:
AdcGenericDevice& _dev;
ATTR_ID_T _id;
};
std::unordered_map<CMD_ID_T, std::function<void()>> _commands{};
snplib::HeterogenMap<ATTR_ID_T> _attrs{};
public:
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() {}
template <snplib::snplib_callable_c ET>
void addCommand(CMD_ID_T id, ET&& exec_func)
{
_commands.emplace(std::move(id), std::forward<ET>(exec_func));
}
// template <typename GT, typename ST, typename... VTs>
template <details::snplib_getter_c GT, details::snplib_setter_c ST, typename... VTs>
void addAttr(ATTR_ID_T id, GT&& getter, ST&& setter, std::tuple<VTs...> = std::tuple())
{
// static_assert(!(std::is_null_pointer_v<GT> && std::is_null_pointer_v<ST>),
// "Getter and setter cannot be nullptr_t at the same time!");
// // deduce attribute value type
// using ret_t = std::invoke_result_t<GT>;
// static_assert(adc_result_c<ret_t>, "Invalid return type of the getter!");
// using v_t = typename ret_t::value_type;
// static_assert(std::invocable<ST, v_t const&>, "Invalid setter type!");
// static_assert(std::convertible_to<std::invoke_result_t<ST, v_t const&>, adc_error_t>,
// "Invalid return type of the setter!");
using v_t = details::snplib_attr_value_deduced_t<GT, ST>;
static_assert(!std::is_void_v<v_t>, "Getter and setter cannot be nullptr_t at the same time!");
if constexpr (!std::is_null_pointer_v<GT> && !std::is_null_pointer_v<ST>) {
static_assert(std::invocable<ST, v_t const&>, "Invalid setter argument type!");
}
_attrs.push(std::move(id), attr_t<v_t>{.getter = std::forward<GT>(getter), .setter = std::forward<ST>(setter)});
// if constexpr (sizeof...(VTs)) {
// auto func = [id, this]<size_t I>() {
// using u_t = std::tuple_element_t<I, std::tuple<VTs...>>;
// if constexpr (!requires(u_t u, v_t v) {
// u = v;
// v = u;
// }) {
// static_assert(false, "Invalid user type!");
// }
// if constexpr (!std::same_as<u_t, v_t>) {
// _attrs.push(std::move(id), attr_t<u_t>{.getter = [id, this]() -> adc_result_t<u_t> {
// auto const& attr = _attrs.template
// get<attr_t<v_t>>(id); if (attr) {
// adc_result_t v = attr.getter();
// if (v) {
// return
// adc_result_t(static_cast<u_t>(v.value()));
// }
// }
// return AdcDeviceErrorCode::ERROR_UNKNOWN;
// },
// .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;
}
auto operator[](ATTR_ID_T id)
{
return attr_proxy_t{*this, std::move(id)};
}
template <typename VT>
adc_result_t<VT> attr(ATTR_ID_T id)
{
return attr_proxy_t{*this, std::move(id)};
}
template <typename VT>
adc_error_t attr(ATTR_ID_T id, VT const& v)
{
return attr_proxy_t{*this, std::move(id)} = v;
}
};
} // namespace adc