Files
ADCLIB/include/adclib/adclib_device1.h
2026-09-15 09:37:57 +03:00

665 lines
27 KiB
C++

#pragma once
/****************************************************************************************
* ABSTRACT DEVICE COMPONENTS LIBRARY *
****************************************************************************************/
#include <concepts>
#include <format>
#include <functional>
#include <list>
#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 "ADCLIB-DEVICE-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());
}
template <typename T>
concept adclib_command_exec_t = snplib::snplib_callable_c<T> && (snplib::snplib_func_traits_t<T>::arity == 0) &&
std::same_as<adc_error_t, typename snplib::snplib_func_traits_t<T>::ret_t>;
template <typename T>
concept adclib_attr_getter_c = std::same_as<T, std::nullptr_t> || requires(T t) {
{ t() } -> adc_result_c;
};
template <typename T>
concept adclib_attr_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 <adclib_attr_getter_c GT, adclib_attr_setter_c ST>
using adclib_attr_value_deduced_t = std::conditional_t<
std::is_null_pointer_v<GT>,
std::conditional_t<std::is_null_pointer_v<ST>, void, std::remove_cvref_t<snplib::snplib_func_arg1_t<ST>>>,
typename std::invoke_result_t<GT>::value_type>;
template <adclib_attr_getter_c GT, adclib_attr_setter_c ST>
struct adclib_is_deduced_void_t {
static constexpr bool value = std::is_void_v<adclib_attr_value_deduced_t<GT, ST>>;
};
// conversional function signature:
// "from": adc_result_t<UT> func(const VT&)
// "to": adc_result_t<VT> func(const UT&)
template <typename T, typename VT>
concept adclib_attr_conv_from_func_c =
std::same_as<T, std::nullptr_t> ||
(std::invocable<T, const VT&> && !std::is_void_v<std::invoke_result_t<T, const VT&>>);
template <typename T, typename VT>
concept adclib_attr_conv_to_func_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_result_t<VT>, typename snplib::snplib_func_traits_t<T>::ret_t>);
template <typename VT, adclib_attr_conv_from_func_c<VT> CONV_FROM_T, adclib_attr_conv_to_func_c<VT> CONV_TO_T>
using adclib_attr_user_deduced_t =
std::conditional_t<std::is_null_pointer_v<CONV_FROM_T>,
std::conditional_t<std::is_null_pointer_v<CONV_TO_T>,
void,
std::remove_cvref_t<snplib::snplib_func_arg1_t<CONV_TO_T>>>,
typename snplib::snplib_func_traits_t<CONV_FROM_T>::ret_t::value_type>;
template <std::formattable<char> DEV_ID_T = std::string,
std::formattable<char> ATTR_ID_T = std::string,
std::formattable<char> CMD_ID_T = std::string>
class AdcGenericDevice
{
protected:
template <typename VT>
using attr_getter_t = std::function<adc_result_t<VT>()>;
template <typename VT>
using attr_setter_t = std::function<adc_error_t(VT const&)>;
typedef std::vector<char> serialized_t;
typedef std::function<adc_error_t(serialized_t&)> attr_serial_func_t;
typedef std::function<adc_error_t(serialized_t const&)> attr_deserial_func_t;
struct attr_t {
attr_t(AdcGenericDevice& dev, ATTR_ID_T id) : _dev(dev), _id(std::move(id)) {}
template <typename VT>
operator adc_result_t<VT>() const
{
auto getter = _dev._attrGetter.template get<attr_getter_t<VT>>(_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 <typename VT>
adc_error_t operator=(VT&& val)
{
auto setter = _dev._attrSetter.template get<attr_setter_t<std::remove_cvref_t<VT>>>(_id);
if (setter) {
if (setter.value()) {
return setter.value()(std::forward<VT>(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<CMD_ID_T, std::function<adc_error_t()>> _commands{};
std::unordered_map<ATTR_ID_T, attr_t> _attrs{};
snplib::hmap<ATTR_ID_T> _attrGetter{};
snplib::hmap<ATTR_ID_T> _attrSetter{};
// default convert functions for char-range types
template <typename VR, typename UR>
requires((snplib::snplib_char_range_c<VR> || snplib::snplib_pointer_to_char_c<VR>) &&
(snplib::snplib_char_range_c<UR> || snplib::snplib_pointer_to_char_c<UR>))
static inline std::function<adc_result_t<UR>(VR const&)> default_char_range_conv_from = [](VR const& val)
-> adc_result_t<UR> {
if constexpr (std::same_as<VR, UR>) {
return val;
} else if constexpr (std::constructible_from<UR, VR>) {
return UR(val);
} else if constexpr (std::is_pointer_v<std::decay_t<VR>>) { // char*, const char*, char[]
return default_char_range_conv_from<std::string_view, UR>(std::string_view(val));
} else if constexpr (std::constructible_from<UR, std::ranges::iterator_t<VR>, std::ranges::sentinel_t<VR>>) {
return UR(val.begin(), val.end());
} else if constexpr (std::constructible_from<UR, std::ranges::const_iterator_t<VR>,
std::ranges::const_sentinel_t<VR>>) {
return UR(val.cbegin(), val.cend());
} else if constexpr (std::same_as<VR, std::string> && std::is_pointer_v<UR>) {
return val.data();
} else {
static_assert(false, "UNSUPPORTED CHAR-RANGE TYPES!");
}
};
template <typename VR, typename UR>
static inline std::function<adc_result_t<VR>(UR const&)> default_char_range_conv_to =
default_char_range_conv_from<UR, VR>;
public:
inline static constexpr std::tuple<bool,
char,
short,
int,
long,
long long,
unsigned char,
unsigned short,
unsigned int,
unsigned long,
unsigned long long,
float,
double,
long double>
arithmetic_types{};
typedef std::tuple<bool,
char,
short,
int,
long,
long long,
unsigned char,
unsigned short,
unsigned int,
unsigned long,
unsigned long long,
float,
double,
long double>
arithmetics_t;
typedef std::tuple<std::string, std::string_view, std::vector<char>, std::list<char>, const char*> char_ranges_t;
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 <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 <adclib_command_exec_t 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>
void addAttr(ATTR_ID_T id, GT&& getter, ST&& setter)
{
using v_t = adclib_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!");
}
_attrGetter.push(id, attr_getter_t<v_t>(std::forward<GT>(getter)));
_attrSetter.push(id, attr_setter_t<v_t>(std::forward<ST>(setter)));
}
template <typename GT, typename ST, typename... UTs>
requires(sizeof...(UTs) > 0)
void addAttr(ATTR_ID_T id, GT&& getter, ST&& setter, std::tuple<UTs...>)
{
using v_t = adclib_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!");
}
using tp_t = std::tuple<UTs...>;
[id = std::move(id), getter_cap = std::forward<GT>(getter), setter_cap = std::forward<ST>(setter),
this]<size_t... Is>(std::index_sequence<Is...>) mutable {
return std::apply(
[this](auto&&... args) { addAttr(std::forward<decltype(args)>(args)...); },
std::tuple_cat(
std::forward_as_tuple(id, std::forward<decltype(getter_cap)>(getter_cap),
std::forward<decltype(setter_cap)>(setter_cap)),
std::tuple_cat(std::forward_as_tuple(
decltype(_attrGetter)::template trivial_conv_from<v_t, std::tuple_element_t<Is, tp_t>>,
decltype(_attrGetter)::template trivial_conv_to<v_t, std::tuple_element_t<Is, tp_t>>)...)));
}(std::make_index_sequence<sizeof...(UTs)>{});
}
//
// conversional functions are the same as defined in the snplib::hmap class
// (it must convert attribute values instead of getters or setters itself!)
template <adclib_attr_getter_c GT, adclib_attr_setter_c ST, typename... FuncTs>
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<GT, ST>;
static_assert(!std::is_void_v<v_t>, "Getter and setter cannot be nullptr_t at the same time!");
[id = std::move(id), getter_cap = std::forward<GT>(getter), setter_cap = std::forward<ST>(setter),
tp = std::forward_as_tuple(std::forward<FuncTs>(cnv_pairs)...),
this]<size_t... Is>(std::index_sequence<Is...>) mutable {
//
auto gt_cnv_from_func = [tp]<size_t I>() {
using tp_t = decltype(tp);
using from_fn_t = std::decay_t<std::tuple_element_t<I, tp_t>>;
using to_fn_t = std::decay_t<std::tuple_element_t<I + 1, tp_t>>;
using u_t = adclib_attr_user_deduced_t<v_t, from_fn_t, to_fn_t>;
return [tp = std::move(tp)](attr_getter_t<v_t> const& gt) -> adc_result_t<attr_getter_t<u_t>> {
return [tp = std::move(tp), &gt]() -> adc_result_t<u_t> {
auto v = gt();
if (v) {
// convert from inner type to user's ("convert-from" function)
// it returns adc_result_t<u_t>
return std::forward<std::tuple_element_t<I, tp_t>>(std::get<I>(tp))(v.value());
}
return std::unexpected(v.error());
};
};
};
auto st_cnv_from_func = [tp]<size_t I>() {
using tp_t = decltype(tp);
using from_fn_t = std::decay_t<std::tuple_element_t<I, tp_t>>;
using to_fn_t = std::decay_t<std::tuple_element_t<I + 1, tp_t>>;
using u_t = adclib_attr_user_deduced_t<v_t, from_fn_t, to_fn_t>;
return [tp = std::move(tp)](attr_setter_t<v_t> const& st) -> adc_result_t<attr_setter_t<u_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::tuple_element_t<I + 1, tp_t>>(std::get<I + 1>(tp))(uval);
if (val) {
return st(val.value());
}
return val.error();
};
};
};
auto gt_cnv_to_func = [tp]<size_t I>() {
using tp_t = decltype(tp);
using from_fn_t = std::decay_t<std::tuple_element_t<I, tp_t>>;
using to_fn_t = std::decay_t<std::tuple_element_t<I + 1, tp_t>>;
using u_t = adclib_attr_user_deduced_t<v_t, from_fn_t, to_fn_t>;
return [tp = std::move(tp)](attr_getter_t<u_t> const& gt) -> adc_result_t<attr_getter_t<v_t>> {
return [tp = std::move(tp), &gt]() -> adc_result_t<v_t> {
auto uv = gt();
if (uv) {
// convert to inner type from user's ("convert-to" function)
// it returns adc_result_t<v_t>
return std::forward<std::tuple_element_t<I + 1, tp_t>>(std::get<I + 1>(tp))(uv.value());
}
return std::unexpected(uv.error());
};
};
};
auto st_cnv_to_func = [tp]<size_t I>() {
using tp_t = decltype(tp);
using from_fn_t = std::decay_t<std::tuple_element_t<I, tp_t>>;
using to_fn_t = std::decay_t<std::tuple_element_t<I + 1, tp_t>>;
using u_t = adclib_attr_user_deduced_t<v_t, from_fn_t, to_fn_t>;
return [tp = std::move(tp)](attr_setter_t<u_t> const& st) -> adc_result_t<attr_setter_t<v_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::tuple_element_t<I, tp_t>>(std::get<I>(tp))(val);
if (uval) {
return st(uval.value());
}
return uval.error();
};
};
};
std::apply(
[this](auto&&... args) { _attrGetter.push(std::forward<decltype(args)>(args)...); },
std::tuple_cat(
std::forward_as_tuple(id, attr_getter_t<v_t>(std::forward<decltype(getter_cap)>(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<decltype(args)>(args)...); },
std::tuple_cat(
std::forward_as_tuple(id, attr_setter_t<v_t>(std::forward<decltype(setter_cap)>(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<NFUNCS / 2>{});
return;
[... cnv_pairs_cap = std::forward<FuncTs>(cnv_pairs), id, getter_cap = std::forward<GT>(getter),
setter_cap = std::forward<ST>(setter), this]<size_t... Is>(std::index_sequence<Is...>) mutable {
auto tp = std::forward_as_tuple(std::forward<FuncTs>(cnv_pairs_cap)...);
auto gt_cnv_from_func = []<size_t I, snplib::snplib_tuple_c TpT>(TpT const& tpl) {
using from_fn_t = std::decay_t<std::tuple_element_t<I, TpT>>;
using to_fn_t = std::decay_t<std::tuple_element_t<I + 1, TpT>>;
using u_t = adclib_attr_user_deduced_t<v_t, from_fn_t, to_fn_t>;
// using u_t = std::invoke_result_t<from_fn_t>::value_type;
// static_assert(requires(v_t v, u_t u) { u = v; }, "Invalid deduced user type!");
return [&tpl](attr_getter_t<v_t> const& gt) -> adc_result_t<attr_getter_t<u_t>> {
return [&tpl, &gt]() -> adc_result_t<u_t> {
auto v = gt();
if (v) {
// convert from inner type to user's ("convert-from" function)
// it returns adc_result_t<u_t>
return std::forward<std::tuple_element_t<I, TpT>>(std::get<I>(tpl))(v.value());
}
return std::unexpected(v.error());
};
};
};
auto st_cnv_from_func = []<size_t I, snplib::snplib_tuple_c TpT>(TpT const& tpl) {
using from_fn_t = std::decay_t<std::tuple_element_t<I, TpT>>;
using to_fn_t = std::decay_t<std::tuple_element_t<I + 1, TpT>>;
using u_t = adclib_attr_user_deduced_t<v_t, from_fn_t, to_fn_t>;
// using u_t = std::invoke_result_t<from_fn_t>::value_type;
// static_assert(requires(v_t v, u_t u) { u = v; }, "Invalid deduced user type!");
return [&tpl](attr_setter_t<v_t> const& st) -> adc_result_t<attr_setter_t<u_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::tuple_element_t<I + 1, TpT>>(std::get<I + 1>(tpl))(uval);
if (val) {
return st(val.value());
}
return val.error();
};
};
};
auto gt_cnv_to_func = []<size_t I, snplib::snplib_tuple_c TpT>(TpT const& tpl) {
using from_fn_t = std::decay_t<std::tuple_element_t<I, TpT>>;
using to_fn_t = std::decay_t<std::tuple_element_t<I + 1, TpT>>;
using u_t = adclib_attr_user_deduced_t<v_t, from_fn_t, to_fn_t>;
// using u_t = std::invoke_result_t<from_fn_t>::value_type;
// static_assert(requires(v_t v, u_t u) { v = u; }, "Invalid deduced user type!");
return [&tpl](attr_getter_t<u_t> const& gt) -> adc_result_t<attr_getter_t<v_t>> {
return [&tpl, &gt]() -> adc_result_t<v_t> {
auto uv = gt();
if (uv) {
// convert to inner type from user's ("convert-to" function)
// it returns adc_result_t<v_t>
return std::forward<std::tuple_element_t<I + 1, TpT>>(std::get<I + 1>(tpl))(uv.value());
}
return std::unexpected(uv.error());
};
};
};
auto st_cnv_to_func = []<size_t I, snplib::snplib_tuple_c TpT>(TpT const& tpl) {
using from_fn_t = std::decay_t<std::tuple_element_t<I, TpT>>;
using to_fn_t = std::decay_t<std::tuple_element_t<I + 1, TpT>>;
using u_t = adclib_attr_user_deduced_t<v_t, from_fn_t, to_fn_t>;
// using u_t = std::invoke_result_t<from_fn_t>::value_type;
// static_assert(requires(v_t v, u_t u) { v = u; }, "Invalid deduced user type!");
return [&tpl](attr_setter_t<u_t> const& st) -> adc_result_t<attr_setter_t<v_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::tuple_element_t<I, TpT>>(std::get<I>(tpl))(val);
if (uval) {
return st(uval.value());
}
return uval.error();
};
};
};
std::apply(
[this](auto&&... args) { _attrGetter.push(std::forward<decltype(args)>(args)...); },
std::tuple_cat(
std::forward_as_tuple(id, attr_getter_t<v_t>(std::forward<decltype(getter_cap)>(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<decltype(args)>(args)...); },
std::tuple_cat(
std::forward_as_tuple(id, attr_setter_t<v_t>(std::forward<decltype(setter_cap)>(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<NFUNCS / 2>{});
}
// add attribute of one of arithmetic type
template <typename GT, typename ST>
requires std::is_arithmetic_v<adclib_attr_value_deduced_t<GT, ST>>
void addArithAttr(ATTR_ID_T id, GT&& getter, ST&& setter)
{
// addAttr(std::move(id), std::forward<GT>(getter), std::forward<ST>(setter),
// AdcGenericDevice::arithmetic_types);
addAttr(std::move(id), std::forward<GT>(getter), std::forward<ST>(setter), AdcGenericDevice::arithmetics_t{});
}
// add attribute of char-range type
template <typename GT, typename ST>
requires snplib::snplib_char_range_c<adclib_attr_value_deduced_t<GT, ST>>
void addCharRangeAttr(ATTR_ID_T id, GT&& getter, ST&& setter)
{
using v_t = adclib_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!");
}
[&id, getter_cap = std::forward<GT>(getter), setter_cap = std::forward<ST>(setter),
this]<size_t... Is>(std::index_sequence<Is...>) mutable {
std::apply(
[this](auto&&... args) { addAttr(std::forward<decltype(args)>(args)...); },
std::tuple_cat(
std::forward_as_tuple(id, std::forward<decltype(getter_cap)>(getter_cap),
std::forward<decltype(setter_cap)>(setter_cap)),
std::tuple_cat(std::forward_as_tuple(
default_char_range_conv_from<v_t, std::tuple_element_t<Is, AdcGenericDevice::char_ranges_t>>,
default_char_range_conv_to<v_t,
std::tuple_element_t<Is, AdcGenericDevice::char_ranges_t>>)...)));
}(std::make_index_sequence<std::tuple_size_v<AdcGenericDevice::char_ranges_t>>{});
}
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 <typename VT>
adc_result_t<VT> attr(ATTR_ID_T id)
{
return attr_t{*this, std::move(id)};
}
template <typename VT>
adc_error_t attr(ATTR_ID_T id, VT const& v)
{
return attr_t{*this, std::move(id)} = v;
}
};
} // namespace adc