...
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -61,6 +61,7 @@ CMakeLists.txt.user*
|
|||||||
*.opensdf
|
*.opensdf
|
||||||
*.vcxproj
|
*.vcxproj
|
||||||
*vcxproj.*
|
*vcxproj.*
|
||||||
|
.vscode/
|
||||||
|
|
||||||
# MinGW generated files
|
# MinGW generated files
|
||||||
*.Debug
|
*.Debug
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
project(ADCLIB LANGUAGES CXX DESCRIPTION "Abstract Device Components Library")
|
project(adclib LANGUAGES CXX DESCRIPTION "Abstract Device Components Library")
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 23)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
option(BUILD_EXAM "Build examples" ON)
|
||||||
|
|
||||||
# ------- dependencies -------
|
# ------- dependencies -------
|
||||||
|
|
||||||
include(FetchContent)
|
include(FetchContent)
|
||||||
@@ -21,11 +23,25 @@ FetchContent_Declare(
|
|||||||
FetchContent_MakeAvailable(snplib)
|
FetchContent_MakeAvailable(snplib)
|
||||||
|
|
||||||
|
|
||||||
set(ADCLIB_HEADERS include/adclib/adclib_device.h)
|
set(ADCLIB_HEADERS include/adclib/adclib_device.h
|
||||||
|
include/adclib/adclib_common.h)
|
||||||
|
|
||||||
add_library(${PROJECT_NAME} INTERFACE ${ADCLIB_HEADERS})
|
add_library(${PROJECT_NAME} INTERFACE ${ADCLIB_HEADERS})
|
||||||
|
target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_23)
|
||||||
|
target_include_directories(
|
||||||
|
${PROJECT_NAME}
|
||||||
|
INTERFACE
|
||||||
|
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
|
||||||
|
"$<INSTALL_INTERFACE:include/${PROJECT_NAME}>"
|
||||||
|
)
|
||||||
|
|
||||||
|
if(BUILD_EXAM)
|
||||||
|
add_executable(device_exam examples/device_exam.cpp)
|
||||||
|
target_link_libraries(device_exam PRIVATE ${PROJECT_NAME})
|
||||||
|
endif()
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
install(TARGETS ADCLIB
|
install(TARGETS ${PROJECT_NAME}
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||||
)
|
)
|
||||||
|
|||||||
28
examples/device_exam.cpp
Normal file
28
examples/device_exam.cpp
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#include <print>
|
||||||
|
|
||||||
|
#include <adclib/adclib_device.h>
|
||||||
|
|
||||||
|
using namespace adc;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int i = 10;
|
||||||
|
double d = 8.1;
|
||||||
|
std::string s{"dwedjkwlk"};
|
||||||
|
|
||||||
|
AdcGenericDevice<> dev;
|
||||||
|
|
||||||
|
dev.addAttr(
|
||||||
|
"1", [&i]() { return adc_result_t<int>{i}; },
|
||||||
|
[&i](int ii) -> adc_error_t {
|
||||||
|
i = ii;
|
||||||
|
return AdcDeviceErrorCode::ERROR_OK;
|
||||||
|
});
|
||||||
|
|
||||||
|
adc_result_t<int> ri = dev["1"];
|
||||||
|
if (ri) {
|
||||||
|
std::println("dev[1] = {}", ri.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
22
include/adclib/adclib_common.h
Normal file
22
include/adclib/adclib_common.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <expected>
|
||||||
|
#include <system_error>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace adc
|
||||||
|
{
|
||||||
|
|
||||||
|
// library-wide error type definition
|
||||||
|
|
||||||
|
typedef std::error_code adc_error_t;
|
||||||
|
|
||||||
|
template <typename VT>
|
||||||
|
using adc_result_t = std::expected<VT, adc_error_t>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept adc_result_c = requires {
|
||||||
|
[]<typename VT>(std::type_identity<adc_result_t<VT>>) {}(std::type_identity<std::remove_cvref_t<T>>());
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace adc
|
||||||
@@ -4,50 +4,191 @@
|
|||||||
* ABSTRACT DEVICE COMPONENTS LIBRARY *
|
* ABSTRACT DEVICE COMPONENTS LIBRARY *
|
||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
|
|
||||||
|
#include <concepts>
|
||||||
|
#include <string>
|
||||||
|
#include <system_error>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
|
||||||
|
// #include <snipplib/concepts/snplib_concepts.h>
|
||||||
#include <snipplib/containers/snplib_hmap.h>
|
#include <snipplib/containers/snplib_hmap.h>
|
||||||
|
#include <snipplib/serialization/snplib_serialization.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "adclib_common.h"
|
||||||
|
|
||||||
|
|
||||||
namespace adc
|
namespace adc
|
||||||
{
|
{
|
||||||
|
|
||||||
enum class adc_device_error_code_t : int { ERROR_OK, ERROR_RO_ATTR, ERROR_WO_ATTR, ERROR_ATTR_RANGE };
|
|
||||||
|
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 adc
|
||||||
|
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
|
||||||
|
template <>
|
||||||
|
class is_error_code_enum<adc::AdcDeviceErrorCode> : public true_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace adc
|
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 "invalud command ID";
|
||||||
|
case AdcDeviceErrorCode::ERROR_NO_ATTR_ID:
|
||||||
|
return "invalud 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
template <std::formattable<char> ATTR_ID_T = std::string_view, std::formattable<char> CMD_ID_T = std::string_view>
|
template <std::formattable<char> ATTR_ID_T = std::string_view, std::formattable<char> CMD_ID_T = std::string_view>
|
||||||
class adc_device_t
|
class AdcGenericDevice
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
typedef std::error_code error_t;
|
|
||||||
|
|
||||||
template <typename VT>
|
|
||||||
using op_result_t = std::expected<VT, error_t>;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
template <typename VT>
|
template <typename VT>
|
||||||
struct attr_t {
|
struct attr_t {
|
||||||
ATTR_ID_T id;
|
ATTR_ID_T id;
|
||||||
std::function<op_result_t<VT>()> getter;
|
|
||||||
std::function<void(VT const&)> setter;
|
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 {
|
struct attr_proxy_t {
|
||||||
|
attr_proxy_t(AdcGenericDevice& dev, ATTR_ID_T id) : _dev(dev), _id(std::move(id)) {}
|
||||||
|
|
||||||
template <typename VT>
|
template <typename VT>
|
||||||
VT get(ATTR_ID_T id)
|
operator adc_result_t<VT>()
|
||||||
{
|
{
|
||||||
|
auto v = _dev._attrs.template get<VT>(_id);
|
||||||
|
if (v) {
|
||||||
|
return v.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
// error
|
||||||
|
if (v.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 err = _dev._attrs.set(_id, std::forward<VT>(val));
|
||||||
|
if (err == snplib::HeterogenMap<ATTR_ID_T>::ERROR_OK) {
|
||||||
|
return AdcDeviceErrorCode::ERROR_OK;
|
||||||
|
} else if (err == 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{};
|
std::unordered_map<CMD_ID_T, std::function<void()>> _commands{};
|
||||||
snplib::HeterogenMap<ATTR_ID_T> _attrs{};
|
snplib::HeterogenMap<ATTR_ID_T> _attrs{};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
adc_device_t() {}
|
typedef ATTR_ID_T attr_id_t;
|
||||||
|
typedef CMD_ID_T cmd_id_t;
|
||||||
|
|
||||||
|
AdcGenericDevice() {}
|
||||||
|
|
||||||
template <snplib::snplib_callable_c ET>
|
template <snplib::snplib_callable_c ET>
|
||||||
void addCommand(CMD_ID_T id, ET&& exec_func)
|
void addCommand(CMD_ID_T id, ET&& exec_func)
|
||||||
@@ -59,12 +200,42 @@ public:
|
|||||||
void addAttr(ATTR_ID_T id, GT&& getter, ST&& setter)
|
void addAttr(ATTR_ID_T id, GT&& getter, ST&& setter)
|
||||||
{
|
{
|
||||||
// deduce attribute value type
|
// deduce attribute value type
|
||||||
using v_t = std::invoke_result_t<GT>;
|
using ret_t = std::invoke_result_t<GT>;
|
||||||
|
using v_t = typename ret_t::value_type;
|
||||||
|
|
||||||
|
static_assert(adc_result_c<ret_t>, "Invalid return type of the getter!");
|
||||||
|
|
||||||
static_assert(std::invocable<ST, v_t const&>, "Invalid setter 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!");
|
||||||
|
|
||||||
_attrs.push(std::move(id), attr_t<v_t>{.getter = std::forward<GT>(getter), .setter = std::forward<ST>(setter)});
|
_attrs.push(std::move(id), attr_t<v_t>{.getter = std::forward<GT>(getter), .setter = std::forward<ST>(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 <typename VT>
|
||||||
|
adc_result_t<VT> attr(ATTR_ID_T id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename VT>
|
||||||
|
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
|
} // namespace adc
|
||||||
|
|||||||
Reference in New Issue
Block a user