diff --git a/CMakeLists.txt b/CMakeLists.txt index 65f1db6..60fe157 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(ADC_COMMON_HEADERS common/adc_traits.h common/adc_value_holder.h - common/adc_value.h) + common/adc_value.h + common/adc_valholder.h) set(ADC_DEVICE_HEADERS device/adc_device_attribute.h @@ -26,7 +27,7 @@ if (BUILD_TESTS) add_executable(${VALUEHOLDER_TEST_APP} tests/adc_valueholder_test.cpp) set(DEVATTR_TEST_APP adc_devattr_test) - add_executable(${DEVATTR_TEST_APP} tests/adc_devattr_test.cpp) + # add_executable(${DEVATTR_TEST_APP} tests/adc_devattr_test.cpp) if (NOT doctest_FOUND) include(FetchContent) diff --git a/common/adc_valholder.h b/common/adc_valholder.h new file mode 100644 index 0000000..ee54cd0 --- /dev/null +++ b/common/adc_valholder.h @@ -0,0 +1,548 @@ +#pragma once + + +/* + +ABSTRACT DEVICE COMPONENTS LIBRARY + + */ + + +#include +#include + +#include "adc_traits.h" +#include "adc_utils.h" + + +namespace adc +{ + +// error codes +enum class AdcValueHolderErrorCode : int { ERROR_OK, ERROR_NO_CONV_FUNC, ERROR_INTERNAL_TYPE_MISMATCH }; + +enum class AdcSerializingValueHolderErrorCode : int { ERROR_OK, ERROR_INVALID_SERIALIZED_TYPE }; + +} // namespace adc + + +namespace std +{ + +template <> +class is_error_code_enum : public true_type +{ +}; + +template <> +class is_error_code_enum : public true_type +{ +}; + +} // namespace std + + +namespace adc +{ + +// error category +struct AdcValueHolderErrorCategory : public std::error_category { + AdcValueHolderErrorCategory() : std::error_category() {} + + const char* name() const noexcept { return "ADC_VALUE_HOLDER_CATEGORY"; } + + std::string message(int ec) const + { + AdcValueHolderErrorCode err = static_cast(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)"; + default: + return "UNKNOWN"; + } + } + + static const AdcValueHolderErrorCategory& get() + { + static const AdcValueHolderErrorCategory constInst; + return constInst; + } +}; + + +struct AdcSerializingValueHolderErrorCategory : public std::error_category { + AdcSerializingValueHolderErrorCategory() : std::error_category() {} + + const char* name() const noexcept { return "ADC_SERIALIZING_VALUE_HOLDER_CATEGORY"; } + + std::string message(int ec) const + { + AdcSerializingValueHolderErrorCode err = static_cast(ec); + + switch (err) { + case AdcSerializingValueHolderErrorCode::ERROR_OK: + return "OK"; + case AdcSerializingValueHolderErrorCode::ERROR_INVALID_SERIALIZED_TYPE: + return "invalid user-passed serialized type"; + default: + return "UNKNOWN"; + } + } + + static const AdcSerializingValueHolderErrorCategory& get() + { + static const AdcSerializingValueHolderErrorCategory constInst; + return constInst; + } +}; + + +inline std::error_code make_error_code(AdcValueHolderErrorCode ec) +{ + return std::error_code(static_cast(ec), AdcValueHolderErrorCategory::get()); +} + + +inline std::error_code make_error_code(AdcSerializingValueHolderErrorCode ec) +{ + return std::error_code(static_cast(ec), AdcSerializingValueHolderErrorCategory::get()); +} + + +/* + + GENERAL-PURPOSE VALUE HOLDER CLASS + + */ + + +class AdcValueHolder +{ +protected: + template + using ret_value_t = std::decay_t>; + + template + inline static std::unordered_map> _getterFunc{}; + + template + inline static std::unordered_map> _setterFunc{}; + + std::function _clearFunc; + + std::function _copyFunc; + std::function _moveFunc; + + +public: + // default trivial types (just arithmetic ones) + constexpr static std::tuple + defaultTrivialConvTypes{}; + + + /* CONSTRUCTORS AND DESTRUCTOR */ + + template GT, typename ValueT = ret_value_t, std::invocable ST> + AdcValueHolder(GT&& getter, ST&& setter) + { + _getterFunc.emplace(this, std::forward(getter)); + _setterFunc.emplace(this, std::forward(setter)); + + _clearFunc = [this]() { + _getterFunc.erase(this); + _setterFunc.erase(this); + }; + + // copy TO other + _copyFunc = [this](AdcValueHolder* other) { + _getterFunc.emplace(other, _getterFunc[this]); + _setterFunc.emplace(other, _setterFunc[this]); + }; + + // move TO other + _moveFunc = [this](AdcValueHolder* other) { + _getterFunc.emplace(other, std::move(_getterFunc[this])); + _setterFunc.emplace(other, std::move(_setterFunc[this])); + }; + } + + + template TupleT, + std::invocable<> GT, + typename ValueT = ret_value_t, + std::invocable ST> + AdcValueHolder(TupleT&&, GT&& getter, ST&& setter) + : AdcValueHolder(std::forward(getter), std::forward(setter)) + { + // setup trivially-defined conversion function + AdcValueHolder::setupTrivialConvertFunc>(); + } + + AdcValueHolder(const AdcValueHolder& other) + { + _clearFunc(); + + other._copyFunc(this); + + _clearFunc = other._clearFunc; + _copyFunc = other._copyFunc; + _moveFunc = other._moveFunc; + }; + + + AdcValueHolder(AdcValueHolder&& other) + { + _clearFunc(); + + other._moveFunc(this); + + _clearFunc = std::move(other._clearFunc); + _copyFunc = std::move(other._copyFunc); + _moveFunc = std::move(other._moveFunc); + }; + + virtual ~AdcValueHolder() { _clearFunc(); }; + + + /* PUBLIC METHODS */ + + + template + AdcValueHolder& addConvertFunc(FromFuncT&& func_from_internal, ToFuncT&& func_to_internal) + requires std::invocable&> && + std::invocable&> + { + using value_t = ret_value_t; // it must be internal value type + using user_t = ret_value_t; + + _getterFunc[this] = [wrapper = traits::adc_pf_wrapper(std::forward(func_from_internal)), + this]() { + auto& getter = _getterFunc[this]; + if (getter) { + auto val = getter(); + return std::get<0>(wrapper)(val); // convert from internal type + } + // invalid conversion function signature + throw std::system_error(AdcValueHolderErrorCode::ERROR_INTERNAL_TYPE_MISMATCH); + }; + + + _setterFunc[this] = [wrapper = traits::adc_pf_wrapper(std::forward(func_to_internal)), + this](const user_t& val) { + value_t value = std::get<0>(wrapper)(val); // convert to internal type + auto& setter = _setterFunc[this]; + if (setter) { + setter(value); + } else { + // invalid conversion function signature + throw std::system_error(AdcValueHolderErrorCode::ERROR_INTERNAL_TYPE_MISMATCH); + } + }; + + _clearFunc = [prev_clear = _clearFunc, this]() { + prev_clear(); + + _getterFunc.erase(this); + _setterFunc.erase(this); + }; + + + _copyFunc = [prev_copy = _copyFunc, this](AdcValueHolder* other) { + prev_copy(other); + + _getterFunc.emplace(other, _getterFunc[this]); + _setterFunc.emplace(other, _setterFunc[this]); + }; + + // move TO other + _moveFunc = [prev_move = _moveFunc, this](AdcValueHolder* other) { + prev_move(other); + + _getterFunc.emplace(other, std::move(_getterFunc[this])); + _setterFunc.emplace(other, std::move(_setterFunc[this])); + }; + + return *this; + } + + + template + operator UT() const + { + using val_t = std::decay_t; + + auto getter = _getterFunc[this]; + if (getter) { + return getter(); + } + + throw std::system_error(AdcValueHolderErrorCode::ERROR_NO_CONV_FUNC); + } + + template + AdcValueHolder& operator=(UT&& value) + { + using val_t = std::decay_t; + + auto setter = _setterFunc[this]; + if (setter) { + setter(std::forward(value)); + // setter(value); + } else { + throw std::system_error(AdcValueHolderErrorCode::ERROR_NO_CONV_FUNC); + } + + return *this; + } + + + AdcValueHolder& operator=(AdcValueHolder&& other) + { + if (&other != this) { + _clearFunc(); + + other._moveFunc(this); + + _clearFunc = std::move(other._clearFunc); + _copyFunc = std::move(other._copyFunc); + _moveFunc = std::move(other._moveFunc); + } + + return *this; + } + + AdcValueHolder& operator=(const AdcValueHolder& other) + { + if (&other != this) { + _clearFunc(); + + other._copyFunc(this); + + _clearFunc = other._clearFunc; + _copyFunc = other._copyFunc; + _moveFunc = other._moveFunc; + } + + return *this; + } + + +protected: + template + void setupTrivialConvertFuncImpl() + { + if constexpr (I < std::tuple_size_v) { + using elem_t = std::tuple_element_t; + + if constexpr (!std::is_same_v) { + addConvertFunc([](const VT& v) { return static_cast(v); }, + [](const elem_t& v) { return static_cast(v); }); + } + + setupTrivialConvertFuncImpl(); + } + } + + template + void setupTrivialConvertFunc() + { + setupTrivialConvertFuncImpl(); + } +}; + + + +class AdcSerializingValueHolder : public AdcValueHolder +{ +protected: + template + inline static std::unordered_map> _serializerFunc{}; + + template + inline static std::unordered_map> + _deserializerFunc{}; + + // shadow base class's members + std::function _clearFunc; + std::function _copyFunc; + std::function _moveFunc; + +public: + template < + std::invocable<> GT, + typename ValueT = ret_value_t, + std::invocable ST, + std::invocable SRT = decltype(utils::AdcDefaultValueConverter<>::serialize), + typename SerializedT = ret_value_t, + std::invocable DSRT = + decltype(utils::AdcDefaultValueConverter<>::deserialize)> + AdcSerializingValueHolder(GT&& getter, + ST&& setter, + SRT&& serializer = utils::AdcDefaultValueConverter<>::serialize, + DSRT&& deserializer = utils::AdcDefaultValueConverter<>::deserialize) + : AdcValueHolder(std::forward(getter), std::forward(setter)) + { + _serializerFunc[this] = [wrapper = traits::adc_pf_wrapper(std::forward(serializer)), this]() { + auto& serializer = std::get<0>(wrapper); + + + auto val = _getterFunc[this](); + + return serializer(val); + }; + + + _deserializerFunc[this] = [wrapper = traits::adc_pf_wrapper(std::forward(deserializer)), + this](const SerializedT& sval) { + auto& deserializer = std::get<0>(wrapper); + + ValueT val = deserializer(sval); + + _setterFunc[this](val); + }; + + _clearFunc = [this]() { + _serializerFunc.erase(this); + _deserializerFunc.erase(this); + }; + + _copyFunc = [this](AdcSerializingValueHolder* other) { + _serializerFunc.emplace(other, _serializerFunc[this]); + _deserializerFunc.emplace(other, _deserializerFunc[this]); + }; + + _moveFunc = [this](AdcSerializingValueHolder* other) { + _serializerFunc.emplace(other, std::move(_serializerFunc[this])); + _deserializerFunc.emplace(other, std::move(_deserializerFunc[this])); + }; + } + + + template < + traits::adc_tuple_like<> TupleT, + std::invocable<> GT, + typename ValueT = ret_value_t, + std::invocable ST, + std::invocable SRT = decltype(utils::AdcDefaultValueConverter<>::serialize), + typename SerializedT = ret_value_t, + std::invocable DSRT = + decltype(utils::AdcDefaultValueConverter<>::deserialize)> + AdcSerializingValueHolder(TupleT&&, + GT&& getter, + ST&& setter, + SRT&& serializer = utils::AdcDefaultValueConverter<>::serialize, + DSRT&& deserializer = utils::AdcDefaultValueConverter<>::deserialize) + : AdcSerializingValueHolder(std::forward(getter), + std::forward(setter), + std::forward(serializer), + std::forward(deserializer)) + { + AdcValueHolder::setupTrivialConvertFunc>(); + } + + + virtual ~AdcSerializingValueHolder() { _clearFunc(); }; + + AdcSerializingValueHolder(const AdcSerializingValueHolder& other) : AdcValueHolder(other) + { + _clearFunc(); + + _copyFunc(other); + } + + + AdcSerializingValueHolder(AdcSerializingValueHolder&& other) : AdcValueHolder(std::move(other)) + { + _clearFunc(); + + _moveFunc(other); + } + + AdcSerializingValueHolder& operator=(const AdcSerializingValueHolder& other) + { + AdcValueHolder::operator=(other); + _copyFunc(other); + + return *this; + } + + AdcSerializingValueHolder& operator=(AdcSerializingValueHolder&& other) + { + AdcValueHolder::operator=(std::move(other)); + _moveFunc(other); + + return *this; + } + + + using AdcValueHolder::operator=; + + + template + SerializedT serialize() + { + using s_t = std::decay_t; + + auto& serializer = _serializerFunc[this]; + + if (serializer) { + return serializer(); + } + + throw std::system_error(AdcSerializingValueHolderErrorCode::ERROR_INVALID_SERIALIZED_TYPE); + } + + template + void deserialize(const SerializedT& sval) + { + using s_t = std::decay_t; + + auto& deserializer = _deserializerFunc[this]; + if (deserializer) { + deserializer(sval); + } else { + throw std::system_error(AdcSerializingValueHolderErrorCode::ERROR_INVALID_SERIALIZED_TYPE); + } + } +}; + + +/* + factory functions + + */ + +template GT, + typename VALT = traits::adc_retval_t, + std::invocable ST, + typename... Ts> +HolderT makeArithmValue(GT&& getter, ST&& setter, Ts&&... other_ctor_args) +{ + using val_t = traits::adc_retval_t; + + static_assert(std::is_arithmetic_v, "GETTER MUST RETURN AN ARITHMETIC TYPE VALUE!!!"); + + return HolderT(AdcValueHolder::defaultTrivialConvTypes, std::forward(getter), std::forward(setter), + std::forward(other_ctor_args)...); +} + +} // namespace adc diff --git a/tests/adc_valueholder_test.cpp b/tests/adc_valueholder_test.cpp index 958b971..c7551e8 100644 --- a/tests/adc_valueholder_test.cpp +++ b/tests/adc_valueholder_test.cpp @@ -1,12 +1,15 @@ -#include +// #include #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include -#include +// #include #include -#include "../common/adc_utils.h" +// #include "../common/adc_utils.h" + + // #include "../common/adc_value_holder.h" -#include "../common/adc_value.h" +// #include "../common/adc_value.h" +#include "../common/adc_valholder.h" static double dbl_val = 99.99; @@ -33,6 +36,96 @@ bool vdbl(const double& v) } +TEST_CASE("[ADC VALUEHOLDER]") +{ + int int_val = 10; + + auto getter = [&int_val]() { return int_val; }; + auto setter = [&int_val](const int& v) { int_val = v; }; + + + adc::AdcValueHolder vh(getter, setter); + auto vah = adc::makeArithmValue(getter, setter); + + vh = 77; + + REQUIRE_EQ(int_val, 77); + + vh = 100; + + int res = vh; + + REQUIRE_EQ(res, 100); + + CHECK_THROWS_WITH_AS(vh = 1.1, "conversion function is not defined", std::system_error); + + std::cout << "int_val = " << int_val << "\n"; + + const double dv = 33.33; + vah = dv; + + REQUIRE_EQ(int_val, 33); + + vah = 100; + auto res_long = static_cast(vah); + + REQUIRE_EQ(res_long, 100ul); + + vah = -10.1234; + REQUIRE_EQ(int_val, -10); + + + + std::string str = "THIS IS A STRING"; + + auto sget = [&str]() { return str; }; + auto sset = [&str](const std::string& s) { str = s; }; + + + adc::AdcValueHolder vsh(sget, sset); + + auto lls = [](const char* v) { return std::string(v); }; + vsh.addConvertFunc([&str](const std::string&) { return str.c_str(); }, lls); + + vsh = "NEW VALUE"; + + REQUIRE_EQ(str, "NEW VALUE"); + + const char* sptr = vsh; + + std::cout << "SPTR: " << sptr << "\n"; + + REQUIRE_EQ(std::strcmp(sptr, "NEW VALUE"), 0); + + // vah = vsh; + + + // vah = 10; + + + // adc::AdcSerializingValueHolder svh(adc::AdcValueHolder::defaultTrivialConvTypes, getter, setter); + auto svh = adc::makeArithmValue(getter, setter); + + svh = 77.65412; + + std::cout << "SERIALIZED: " << svh.serialize() << "\n"; + + std::string sval = "123"; + svh.deserialize(sval); + float fl = svh; + std::cout << "DESERIALIZED: " << fl << "\n"; + + + // adc::AdcSerializingValueHolder svhs( + // sget, sset, [](const std::string& s) { return s; }, [](const std::string& s) { return s; }); + + // svh = svhs; + + // svh = 10; +} + +/* + TEST_CASE("[ADC VALUEHOLDER]") { int int_val = 10; @@ -81,7 +174,6 @@ TEST_CASE("[ADC VALUEHOLDER]") REQUIRE_EQ(int_val, -10); - /* std::string holder */ std::string str = "THIS IS A STRING"; @@ -204,3 +296,5 @@ TEST_CASE("[ADC VALUEHOLDER]") res = svh; std::cout << "DESERIALIZED: " << res << "\n"; } + +*/