Initial commit

This commit is contained in:
Timur A. Fatkhullin 2024-01-19 17:53:41 +03:00
commit 75ec7065d2
3 changed files with 462 additions and 0 deletions

74
.gitignore vendored Normal file
View File

@ -0,0 +1,74 @@
# This file is used to ignore files which are generated
# ----------------------------------------------------------------------------
*~
*.autosave
*.a
*.core
*.moc
*.o
*.obj
*.orig
*.rej
*.so
*.so.*
*_pch.h.cpp
*_resource.rc
*.qm
.#*
*.*#
core
!core/
tags
.DS_Store
.directory
*.debug
Makefile*
*.prl
*.app
moc_*.cpp
ui_*.h
qrc_*.cpp
Thumbs.db
*.res
*.rc
/.qmake.cache
/.qmake.stash
# qtcreator generated files
*.pro.user*
CMakeLists.txt.user*
# xemacs temporary files
*.flc
# Vim temporary files
.*.swp
# Visual Studio generated files
*.ib_pdb_index
*.idb
*.ilk
*.pdb
*.sln
*.suo
*.vcproj
*vcproj.*.*.user
*.ncb
*.sdf
*.opensdf
*.vcxproj
*vcxproj.*
# MinGW generated files
*.Debug
*.Release
# Python byte code
*.pyc
# Binaries
# --------
*.dll
*.exe

23
CMakeLists.txt Normal file
View File

@ -0,0 +1,23 @@
cmake_minimum_required(VERSION 3.14)
project(ADC LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(ADC_DEVICE_HEADERS
device/adc_value_holder.h
)
include(GNUInstallDirs)
add_library(${PROJECT_NAME} INTERFACE ${ADC_DEVICE_HEADERS})
target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_20)
# target_link_libraries(${PROJECT_NAME} INTERFACE ASIO::ASIO)
target_include_directories(
${PROJECT_NAME}
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)

365
device/adc_value_holder.h Normal file
View File

@ -0,0 +1,365 @@
#pragma once
/*
ABSTRACT DEVICE COMPONENTS LIBRARY
*/
#include <any>
#include <concepts>
#include <functional>
#include <map>
#include <system_error>
#include <tuple>
#include <type_traits>
#include <typeindex>
namespace adc
{
// error codes
enum class AdcValueHolderErrorCode : int {
ERROR_OK,
ERROR_NO_CONV_FUNC,
ERROR_INTERNAL_TYPE_MISMATCH,
ERROR_VALUE_IS_NOT_VALID
};
} // namespace adc
namespace std
{
template <>
class is_error_code_enum<adc::AdcValueHolderErrorCode> : public true_type
{
};
} // namespace std
namespace adc
{
namespace traits
{
// deduce returned type of callable
template <typename T>
using adc_retval_t = std::invoke_result_t<std::remove_cvref_t<T>>;
// helper classes
template <typename... Ts>
struct adc_func_traits_helper_t;
template <typename R>
struct adc_func_traits_helper_t<R> {
using ret_t = R;
using args_t = std::tuple<>;
using arg1_t = void;
};
template <typename R, typename Arg, typename... Args>
struct adc_func_traits_helper_t<R, Arg, Args...> {
using ret_t = R;
using args_t = std::tuple<Arg, Args...>;
using arg1_t = Arg;
};
// callable traits
template <typename F>
struct adc_func_traits;
template <typename R, typename... Args>
struct adc_func_traits<R (*)(Args...)> : adc_func_traits_helper_t<R, Args...> {
};
template <typename R, typename... Args>
struct adc_func_traits<R(Args...)> : adc_func_traits_helper_t<R, Args...> {
};
template <typename C, typename R, typename... Args>
struct adc_func_traits<R (C::*)(Args...)> : adc_func_traits_helper_t<R, Args...> {
};
template <typename C, typename R, typename... Args>
struct adc_func_traits<R (C::*)(Args...) const> : adc_func_traits_helper_t<R, Args...> {
};
template <typename F>
struct adc_func_traits : adc_func_traits<decltype(&F::operator())> {
};
// deduce type
template <typename T>
using adc_deduced_type =
std::conditional_t<std::is_lvalue_reference_v<T> && !std::is_const_v<std::remove_reference_t<T>>,
T,
std::remove_cvref_t<T>>;
} // namespace traits
// error category
struct AdcValueHolderErrorCategory : public std::error_category {
AdcValueHolderErrorCategory() : std::error_category() {}
const char* name() const noexcept { return "DEVA_DEVICE_ATTRIBUTE"; }
std::string message(int ec) const
{
AdcValueHolderErrorCode err = static_cast<AdcValueHolderErrorCode>(ec);
switch (err) {
case AdcValueHolderErrorCode::ERROR_OK:
return "OK";
case AdcValueHolderErrorCode::ERROR_NO_CONV_FUNC:
return "conversion function is not defined";
case AdcValueHolderErrorCode::ERROR_INTERNAL_TYPE_MISMATCH:
return "try to setup default conversion function for invalid type (internal type mismatch)";
case AdcValueHolderErrorCode::ERROR_VALUE_IS_NOT_VALID:
return "user value is not valid";
default:
return "UNKNOWN";
}
}
static const AdcValueHolderErrorCategory& get()
{
static const AdcValueHolderErrorCategory constInst;
return constInst;
}
};
inline std::error_code make_error_code(AdcValueHolderErrorCode ec)
{
return std::error_code(static_cast<int>(ec), AdcValueHolderErrorCategory::get());
}
/*
GENERAL-PURPOSE VALUE HOLDER CLASS WITH OPTIONAL VALIDATOR
*/
class AdcValueHolder
{
protected:
template <typename T>
using arg_t = typename traits::adc_func_traits<T>::arg1_t;
std::function<void(std::any&)> _getterWrapper;
std::function<void(const std::any&)> _setterWrapper;
std::map<std::type_index, std::function<void(std::any&)>> _convertToInternal;
std::map<std::type_index, std::function<void(std::any&)>> _convertFromInternal;
std::type_index _internalTypeIndex;
constexpr static std::tuple<char, short, int, long, long long, float, double> _defaultConvTypes{};
public:
// always-true validator
constexpr static auto AdcValueHolderDummyValidator = [](const auto&) { return true; };
/* CONSTRUCTORS AND DESTRUCTOR */
AdcValueHolder(std::invocable<> auto&& getter,
std::invocable<const arg_t<decltype(getter)>&> auto&& setter,
std::predicate<const arg_t<decltype(getter)>&> auto&& validator = AdcValueHolderDummyValidator)
: _internalTypeIndex(std::type_index(typeid(arg_t<decltype(getter)>)))
{
using value_t = arg_t<decltype(getter)>;
static_assert(std::is_same_v<value_t, void>, "THE getter MUST NOT RETURN void type!!!");
using g_t = decltype(getter);
_getterWrapper = [wrapper =
std::tuple<traits::adc_deduced_type<g_t>>(std::forward<g_t>(getter))](std::any& val) {
auto& getter = std::get<0>(wrapper);
val = getter();
};
using vld_t = decltype(validator);
using s_t = decltype(setter);
if constexpr (std::is_same_v<vld_t, decltype(AdcValueHolderDummyValidator)>) {
_setterWrapper = [wrapper = std::tuple<traits::adc_deduced_type<s_t>>(std::forward<s_t>(setter)),
this](const std::any& val) {
auto& setter = std::get<0>(wrapper);
setter(std::any_cast<value_t>(val));
};
} else {
_setterWrapper = [wrapper = std::tuple<traits::adc_deduced_type<s_t>, traits::adc_deduced_type<vld_t>>(
std::forward<s_t>(setter), std::forward<vld_t>(validator)),
this](const std::any& val) {
auto& setter = std::get<0>(wrapper);
auto& validator = std::get<1>(wrapper);
auto v = std::any_cast<value_t>(val);
if (validator(v)) {
setter(v);
} else {
throw std::system_error(AdcValueHolderErrorCode::ERROR_VALUE_IS_NOT_VALID);
}
};
}
// By default no conversion will be made if user value of internal type is given.
// But such a conversion may be defined explicitly by calling addConvertFunc method
_convertFromInternal[_internalTypeIndex] = [](auto) {};
_convertToInternal[_internalTypeIndex] = [](auto) {};
}
AdcValueHolder(const AdcValueHolder&) = default;
AdcValueHolder(AdcValueHolder&&) = default;
virtual ~AdcValueHolder() = default;
/* PUBLIC METHODS */
template <typename UT>
AdcValueHolder& addConvertFunc(std::invocable<std::any&> auto&& func_to_internal,
std::invocable<std::any&> auto&& func_from_internal)
{
static_assert(std::is_same_v<UT, void>, "void IS NOT VALID TYPE!!!");
auto t_index = std::type_index(typeid(UT));
_convertToInternal[t_index] = std::forward<decltype(func_to_internal)>(func_to_internal);
_convertFromInternal[t_index] = std::forward<decltype(func_from_internal)>(func_from_internal);
return *this;
}
template <typename UT = void>
AdcValueHolder& delConvertFunc()
{
if constexpr (std::is_same_v<UT, void>) {
_convertFromInternal.clear();
_convertToInternal.clear();
_convertFromInternal[_internalTypeIndex] = [](auto) {};
_convertToInternal[_internalTypeIndex] = [](auto) {};
} else {
auto t_index = std::type_index(typeid(UT));
_convertFromInternal.erase(t_index);
_convertToInternal.erase(t_index);
}
return *this;
}
template <typename UT>
operator UT()
{
std::any val;
auto t_index = std::type_index(typeid(UT));
auto it = _convertFromInternal.find(t_index);
if (it == _convertFromInternal.end()) {
throw std::system_error(AdcValueHolderErrorCode::ERROR_NO_CONV_FUNC);
}
_getterWrapper(val);
it->second(val); // call conversion function
return std::any_cast<UT>(val);
}
template <typename UT>
AdcValueHolder& operator=(const UT& value)
{
std::any val = std::make_any<UT>(value);
auto t_index = std::type_index(typeid(UT));
auto it = _convertToInternal.find(t_index);
if (it == _convertToInternal.end()) {
throw std::system_error(AdcValueHolderErrorCode::ERROR_NO_CONV_FUNC);
}
it->second(val); // call conversion function
_setterWrapper(val);
return *this;
}
AdcValueHolder& operator=(AdcValueHolder&& other) = default;
AdcValueHolder& operator=(const AdcValueHolder& other) = default;
template <typename ValueT, typename TupleT = decltype(AdcValueHolder::_defaultConvTypes)>
friend AdcValueHolder& setupDefaultConvFunc(AdcValueHolder& holder)
{
auto t_index = std::type_index(typeid(ValueT));
if (t_index != holder._internalTypeIndex) {
throw std::system_error(AdcValueHolderErrorCode::ERROR_INTERNAL_TYPE_MISMATCH);
}
AdcValueHolder::setupDefaultConvertFunc<ValueT, TupleT>(&holder);
return holder;
}
protected:
/* STATIC HELPER METHODS */
template <typename VT, size_t I, typename TupleT>
static void setupDefaultConvertFuncImpl(AdcValueHolder* holder)
{
if constexpr (I < std::tuple_size_v<TupleT>) {
using elem_t = std::tuple_element_t<I, TupleT>;
auto t_index = std::type_index(typeid(elem_t));
holder->_convertToInternal[t_index] = [](std::any& val) {
val = std::make_any<VT>(std::any_cast<elem_t>(val));
};
holder->_convertFromInternal[t_index] = [](std::any& val) {
val = std::make_any<elem_t>(std::any_cast<VT>(val));
};
AdcValueHolder::setupDefaultConvertFuncImpl<VT, I + 1, TupleT>(holder);
}
}
template <typename VT, typename TupleT>
static void setupDefaultConvertFunc(AdcValueHolder* holder)
{
setupDefaultConvertFuncImpl<VT, 0, TupleT>(holder);
}
};
} // namespace adc