From 5dcc57707b28be2bb5f8d21f8ce2cf42170cc828 Mon Sep 17 00:00:00 2001 From: "Timur A. Fatkhullin" Date: Wed, 15 May 2024 02:27:16 +0300 Subject: [PATCH] due to GCC strange behavior with cast operator AdcDeviceAttribute class is now has its own full "value holder" implementation without inheritance from AdcValueHolder --- CMakeLists.txt | 2 +- common/adc_traits.h | 4 +- device/adc_device_attribute.h | 455 +++++++++++++++++++++++++++++----- tests/adc_devattr_test.cpp | 4 +- 4 files changed, 399 insertions(+), 66 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 60fe157..7ad9aab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,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_traits.h b/common/adc_traits.h index 245b063..4d5a5ff 100644 --- a/common/adc_traits.h +++ b/common/adc_traits.h @@ -70,8 +70,8 @@ struct adc_func_traits_helper_t { // callable concept and its signature traits template -concept adc_is_callable = - std::is_function_v || (std::is_object_v && requires(T) { std::is_function_v; }); +concept adc_is_callable = std::is_function_v || (std::is_object_v && requires(T) { &T::operator(); }); +// std::is_function_v || (std::is_object_v && requires(T) { std::is_function_v; }); template struct adc_func_traits { diff --git a/device/adc_device_attribute.h b/device/adc_device_attribute.h index 8a1ae45..bc2337e 100644 --- a/device/adc_device_attribute.h +++ b/device/adc_device_attribute.h @@ -6,15 +6,67 @@ */ +#include +#include -#include "../common/adc_value.h" +#include "../common/adc_traits.h" +#include "../common/adc_utils.h" + +// #include "../common/adc_value.h" namespace adc { +namespace constants +{ + +static constexpr std::tuple + AdcDefaultTrivialConvTypes{}; + +} // namespace constants + +namespace traits +{ + +// a callable and it has exactly one argument +template +concept adc_is_setter = adc_func_traits::arity == 1; + +// a callable with exactly one argument of type VT +template +concept adc_is_setter_arg = (adc_func_traits::arity == 1) && std::same_as>; + + +// a callable with exactly one argument of type VT and it returns a value of type ST +template +concept adc_is_serializer = + (adc_func_traits::arity == 1) && std::same_as> && std::same_as>; + +} // namespace traits + // error codes -enum class AdcDeviceAttributeErrorCode : int { ERROR_OK, ERROR_READ_ONLY, ERROR_WRITE_ONLY }; +enum class AdcDeviceAttributeErrorCode : int { + ERROR_OK, + ERROR_NO_CONV_FUNC, + ERROR_INTERNAL_TYPE_MISMATCH, + ERROR_READ_ONLY, + ERROR_WRITE_ONLY, + ERROR_INVALID_SERIALIZED_TYPE +}; } // namespace adc @@ -47,10 +99,16 @@ struct AdcDeviceAttributeErrorCategory : public std::error_category { switch (err) { case AdcDeviceAttributeErrorCode::ERROR_OK: return "OK"; + case AdcDeviceAttributeErrorCode::ERROR_NO_CONV_FUNC: + return "conversion function is not defined"; + case AdcDeviceAttributeErrorCode::ERROR_INTERNAL_TYPE_MISMATCH: + return "try to setup default conversion function for invalid type (internal type mismatch)"; case AdcDeviceAttributeErrorCode::ERROR_READ_ONLY: return "device attribute is read-only"; case AdcDeviceAttributeErrorCode::ERROR_WRITE_ONLY: return "device attribute is write-only"; + case AdcDeviceAttributeErrorCode::ERROR_INVALID_SERIALIZED_TYPE: + return "invalid user-passed serialized type"; default: return "UNKNOWN"; } @@ -71,17 +129,36 @@ inline std::error_code make_error_code(AdcDeviceAttributeErrorCode ec) -template -class AdcDeviceAttribute : protected AdcSerializingValueHolder +template +class AdcDeviceAttribute { - typedef AdcSerializingValueHolder base_t; - +protected: template - using ret_value_t = base_t::template ret_value_t; + using ret_value_t = std::decay_t>; + + template + inline static std::unordered_map> _getterFunc{}; + + template + inline static std::unordered_map> _setterFunc{}; + + template + inline static std::unordered_map> _serializerFunc{}; + + template + inline static std::unordered_map> + _deserializerFunc{}; + + std::function _clearFunc; + + std::function _copyFunc; + std::function _moveFunc; + public: typedef IdentT ident_t; - typedef SerializedT serialized_t; + + typedef std::string default_serialized_t; enum AccessType { ReadOnly, WriteOnly, ReadWrite }; @@ -97,13 +174,13 @@ public: { } - template + template constexpr static SerializedT DummySerializer(const T&) { return SerializedT(); } - template + template constexpr static T DummyDeserializer(const SerializedT&) { return T(); @@ -112,104 +189,269 @@ public: /* CONSTRUCTORS AND DESTRUCTOR */ - // general read-write attribute constructor - template - AdcDeviceAttribute(const IdentT& ident, SerValHolderTs&&... ctor_args) - : base_t(std::forward(ctor_args)...), _ident(ident), _accessType(ReadWrite) + template 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)> + AdcDeviceAttribute(GT&& getter, + ST&& setter, + SRT&& serializer = utils::AdcDefaultValueConverter<>::serialize, + DSRT&& deserializer = utils::AdcDefaultValueConverter<>::deserialize) { + _getterFunc.emplace(this, std::forward(getter)); + _setterFunc.emplace(this, 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]() { + _getterFunc.erase(this); + _setterFunc.erase(this); + + _serializerFunc.erase(this); + _deserializerFunc.erase(this); + }; + + + // copy TO other + _copyFunc = [this](AdcDeviceAttribute* other) { + _getterFunc.emplace(other, _getterFunc[this]); + _setterFunc.emplace(other, _setterFunc[this]); + + _serializerFunc.emplace(other, _serializerFunc[this]); + _deserializerFunc.emplace(other, _deserializerFunc[this]); + }; + + + // move TO other + _moveFunc = [this](AdcDeviceAttribute* other) { + _getterFunc.emplace(other, std::move(_getterFunc[this])); + _setterFunc.emplace(other, std::move(_setterFunc[this])); + + _serializerFunc.emplace(other, std::move(_serializerFunc[this])); + _deserializerFunc.emplace(other, std::move(_deserializerFunc[this])); + }; } + template 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)> + AdcDeviceAttribute(TupleT&&, + GT&& getter, + ST&& setter, + SRT&& serializer = utils::AdcDefaultValueConverter<>::serialize, + DSRT&& deserializer = utils::AdcDefaultValueConverter<>::deserialize) + : AdcDeviceAttribute(std::forward(getter), + std::forward(setter), + std::forward(serializer), + std::forward(deserializer)) + { + AdcDeviceAttribute::setupTrivialConvertFunc>(); + } + + + // read-only attribute constructor - template , - typename SRT = decltype(utils::AdcDefaultValueConverter<>::serialize)> + template GT, + typename ValueT = ret_value_t, + std::invocable SRT = + decltype(utils::AdcDefaultValueConverter<>::serialize), + typename SerializedT = ret_value_t> AdcDeviceAttribute(const IdentT& ident, GT&& getter, - SRT&& serializer = utils::AdcDefaultValueConverter<>::serialize) - requires std::invocable && std::invocable + SRT&& serializer = utils::AdcDefaultValueConverter<>::serialize) : AdcDeviceAttribute(ident, std::forward(getter), - AdcDeviceAttribute::DummySetter, - AdcValueHolder::AdcValueHolderDummyValidator, + AdcDeviceAttribute::DummySetter, std::forward(serializer), - AdcDeviceAttribute::DummyDeserializer) + AdcDeviceAttribute::DummyDeserializer) { _accessType = ReadOnly; } - template , - typename SRT = decltype(utils::AdcDefaultValueConverter<>::serialize)> + template TupleT, + std::invocable<> GT, + typename ValueT = ret_value_t, + std::invocable SRT = + decltype(utils::AdcDefaultValueConverter<>::serialize), + typename SerializedT = ret_value_t> AdcDeviceAttribute(const IdentT& ident, - traits::adc_tuple_like<> auto&& tpl, + TupleT&&, GT&& getter, - SRT&& serializer = utils::AdcDefaultValueConverter<>::serialize) - requires std::invocable && std::invocable + SRT&& serializer = utils::AdcDefaultValueConverter<>::serialize) : AdcDeviceAttribute(ident, - std::forward(tpl), + TupleT{}, std::forward(getter), - AdcDeviceAttribute::DummySetter, - AdcValueHolder::AdcValueHolderDummyValidator, + AdcDeviceAttribute::DummySetter, std::forward(serializer), - AdcDeviceAttribute::DummyDeserializer) + AdcDeviceAttribute::DummyDeserializer) { _accessType = ReadOnly; } // write-only attribute constructor - template >, - typename DSRT = decltype(utils::AdcDefaultValueConverter<>::deserialize)> + template ST, + typename ValueT = std::decay_t>, + typename DSRT = decltype(utils::AdcDefaultValueConverter<>::deserialize), + typename SerializedT = std::decay_t>> AdcDeviceAttribute(const IdentT& ident, ST&& setter, - std::predicate auto&& validator, - DSRT&& deserializer = utils::AdcDefaultValueConverter<>::deserialize) - requires std::invocable + DSRT&& deserializer = utils::AdcDefaultValueConverter<>::deserialize) : AdcDeviceAttribute(ident, - AdcDeviceAttribute::DummyGetter, + AdcDeviceAttribute::DummyGetter, std::forward(setter), - std::forward(validator), - AdcDeviceAttribute::DummySerializer, + AdcDeviceAttribute::DummySerializer, std::forward(deserializer)) { _accessType = WriteOnly; } - template >, - typename DSRT = decltype(utils::AdcDefaultValueConverter<>::deserialize)> + template TupleT, + traits::adc_is_setter<> ST, + typename ValueT = std::decay_t>, + typename DSRT = decltype(utils::AdcDefaultValueConverter<>::deserialize), + typename SerializedT = std::decay_t>> AdcDeviceAttribute(const IdentT& ident, - traits::adc_tuple_like<> auto&& tpl, + TupleT&&, ST&& setter, - std::predicate auto&& validator, - DSRT&& deserializer = utils::AdcDefaultValueConverter<>::deserialize) - requires std::invocable + DSRT&& deserializer = utils::AdcDefaultValueConverter<>::deserialize) : AdcDeviceAttribute(ident, - std::forward(tpl), - AdcDeviceAttribute::DummyGetter, + TupleT{}, + AdcDeviceAttribute::DummyGetter, std::forward(setter), - std::forward(validator), - AdcDeviceAttribute::DummySerializer, + AdcDeviceAttribute::DummySerializer, std::forward(deserializer)) { _accessType = WriteOnly; } - AdcDeviceAttribute(const AdcDeviceAttribute&) = default; - AdcDeviceAttribute(AdcDeviceAttribute&&) = default; + + AdcDeviceAttribute(const AdcDeviceAttribute& other) + { + _clearFunc(); + + other._copyFunc(this); + + _clearFunc = other._clearFunc; + _copyFunc = other._copyFunc; + _moveFunc = other._moveFunc; + } + + AdcDeviceAttribute(AdcDeviceAttribute&& other) + { + _clearFunc(); + + other._moveFunc(this); + + _clearFunc = std::move(other._clearFunc); + _copyFunc = std::move(other._copyFunc); + _moveFunc = std::move(other._moveFunc); + } + + + virtual ~AdcDeviceAttribute() { _clearFunc(); } + + + /* PUBLIC METHODS */ - virtual ~AdcDeviceAttribute() = default; IdentT ident() const { return _ident; } AccessType accessType() const { return _accessType; } + + template + AdcDeviceAttribute& 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(AdcDeviceAttributeErrorCode::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(AdcDeviceAttributeErrorCode::ERROR_INTERNAL_TYPE_MISMATCH); + } + }; + + _clearFunc = [prev_clear = _clearFunc, this]() { + prev_clear(); + + _getterFunc.erase(this); + _setterFunc.erase(this); + }; + + + // copy TO other + _copyFunc = [prev_copy = _copyFunc, this](AdcDeviceAttribute* other) { + prev_copy(other); + + _getterFunc.emplace(other, _getterFunc[this]); + _setterFunc.emplace(other, _setterFunc[this]); + }; + + // move TO other + _moveFunc = [prev_move = _moveFunc, this](AdcDeviceAttribute* other) { + prev_move(other); + + _getterFunc.emplace(other, std::move(_getterFunc[this])); + _setterFunc.emplace(other, std::move(_setterFunc[this])); + }; + + return *this; + } + template operator UT() const { @@ -217,28 +459,119 @@ public: throw std::system_error(AdcDeviceAttributeErrorCode::ERROR_WRITE_ONLY); } - return base_t::operator UT(); + using val_t = std::decay_t; + + auto getter = _getterFunc[this]; + if (getter) { + return getter(); + } + + throw std::system_error(AdcDeviceAttributeErrorCode::ERROR_NO_CONV_FUNC); } - AdcDeviceAttribute& operator=(const AdcDeviceAttribute&) = default; - AdcDeviceAttribute& operator=(AdcDeviceAttribute&&) = default; - template - AdcDeviceAttribute& operator=(UT&& value) + AdcDeviceAttribute& operator=(UT&& other) { if (_accessType == ReadOnly) { throw std::system_error(AdcDeviceAttributeErrorCode::ERROR_READ_ONLY); } - base_t::operator=(std::forward(value)); + using val_t = std::decay_t; + + auto setter = _setterFunc[this]; + if (setter) { + setter(std::forward(other)); + } else { + throw std::system_error(AdcDeviceAttributeErrorCode::ERROR_NO_CONV_FUNC); + } return *this; } + AdcDeviceAttribute& operator=(const AdcDeviceAttribute& other) + { + if (&other != this) { + _clearFunc(); + + other._copyFunc(this); + + _clearFunc = other._clearFunc; + _copyFunc = other._copyFunc; + _moveFunc = other._moveFunc; + } + + return *this; + } + + + AdcDeviceAttribute& operator=(AdcDeviceAttribute&& 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; + } + + template + SerializedT serialize() + { + using s_t = std::decay_t; + + auto& serializer = _serializerFunc[this]; + + if (serializer) { + return serializer(); + } + + throw std::system_error(AdcDeviceAttributeErrorCode::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(AdcDeviceAttributeErrorCode::ERROR_INVALID_SERIALIZED_TYPE); + } + } + protected: IdentT _ident; AccessType _accessType; + + + 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(); + } }; } // namespace adc diff --git a/tests/adc_devattr_test.cpp b/tests/adc_devattr_test.cpp index dfc38f0..767dbd4 100644 --- a/tests/adc_devattr_test.cpp +++ b/tests/adc_devattr_test.cpp @@ -32,7 +32,7 @@ TEST_CASE("[ADC DEVICE ATTRIBUTE]") using attr_t = AdcDeviceAttribute; - attr_t attr("ATTR_A", AdcValueHolder::_defaultTrivialConvTypes, vv::getter, vv::setter, vv::validator); + attr_t attr("ATTR_A", adc::constants::AdcDefaultTrivialConvTypes, vv::getter, vv::setter); attr = 10.7; av = attr; @@ -40,7 +40,7 @@ TEST_CASE("[ADC DEVICE ATTRIBUTE]") std::cout << "ATTR = " << av << "\n"; // std::cout << "ATTR = " << (unsigned)attr << "\n"; - attr_t aw("ATTR_WO", AdcValueHolder::_defaultTrivialConvTypes, vv::setter, vv::validator); + attr_t aw("ATTR_WO", adc::constants::AdcDefaultTrivialConvTypes, vv::setter); std::cout << "ACC_TYPE: " << aw.accessType() << "\n"; }