diff --git a/src/blackmisc/datacache.h b/src/blackmisc/datacache.h index d83beacd8..7b403cfcb 100644 --- a/src/blackmisc/datacache.h +++ b/src/blackmisc/datacache.h @@ -286,17 +286,11 @@ namespace BlackMisc class CData : public BlackMisc::CCached { public: - //! \copydoc BlackMisc::CCached::NotifySlot - template - using NotifySlot = typename BlackMisc::CCached::template NotifySlot; - //! Constructor. //! \param owner Will be the parent of the internal QObject used to access the value. - //! \param slot Slot to call when the value is modified by another object. - //! Must be a void, non-const member function of the owner. template - CData(T *owner, NotifySlot slot = nullptr) : - CData::CCached(CDataCache::instance(), Trait::key(), Trait::humanReadable(), Trait::isValid, Trait::defaultValue(), owner, slot) + CData(T *owner) : + CData::CCached(CDataCache::instance(), Trait::key(), Trait::humanReadable(), Trait::isValid, Trait::defaultValue(), owner) { if (Trait::timeToLive() >= 0) { CDataCache::instance()->setTimeToLive(Trait::key(), Trait::timeToLive()); } if (Trait::isPinned()) { CDataCache::instance()->pinValue(Trait::key()); } @@ -304,6 +298,16 @@ namespace BlackMisc static_assert(! (Trait::isPinned() && Trait::isDeferred()), "trait can not be both pinned and deferred"); } + //! Constructor. + //! \param owner Will be the parent of the internal QObject used to access the value. + //! \param slot Slot to call when the value is modified by another object. + //! Must be a void, non-const member function of the owner. + template + CData(T *owner, F slot) : CData(owner) + { + this->setNotifySlot(slot); + } + //! \copydoc BlackMisc::CCached::set CStatusMessage set(const typename Trait::type &value, qint64 timestamp = 0) { diff --git a/src/blackmisc/settingscache.h b/src/blackmisc/settingscache.h index e46c4be85..ed085c298 100644 --- a/src/blackmisc/settingscache.h +++ b/src/blackmisc/settingscache.h @@ -69,18 +69,22 @@ namespace BlackMisc class CSetting : public BlackMisc::CCached { public: - //! \copydoc BlackMisc::CCached::NotifySlot + //! Constructor. + //! \param owner Will be the parent of the internal QObject used to access the value. template - using NotifySlot = typename BlackMisc::CCached::template NotifySlot; + CSetting(T *owner) : + CSetting::CCached(CSettingsCache::instance(), Trait::key(), Trait::humanReadable(), Trait::isValid, Trait::defaultValue(), owner) + {} //! Constructor. //! \param owner Will be the parent of the internal QObject used to access the value. //! \param slot Slot to call when the value is modified by another object. //! Must be a void, non-const member function of the owner. - template - CSetting(T *owner, NotifySlot slot = nullptr) : - CSetting::CCached(CSettingsCache::instance(), Trait::key(), Trait::humanReadable(), Trait::isValid, Trait::defaultValue(), owner, slot) - {} + template + CSetting(T *owner, F slot) : CSetting(owner) + { + this->setNotifySlot(slot); + } //! Reset the setting to its default value. CStatusMessage setDefault() { return this->set(Trait::defaultValue()); } diff --git a/src/blackmisc/valuecache.cpp b/src/blackmisc/valuecache.cpp index e8067e8e4..e5930e80b 100644 --- a/src/blackmisc/valuecache.cpp +++ b/src/blackmisc/valuecache.cpp @@ -538,8 +538,8 @@ namespace BlackMisc struct CValuePage::Element { - Element(const QString &key, const QString &name, int metaType, Validator validator, const CVariant &defaultValue, NotifySlot slot) : - m_key(key), m_name(name), m_metaType(metaType), m_validator(validator), m_default(defaultValue), m_notifySlot(slot) + Element(const QString &key, const QString &name, int metaType, Validator validator, const CVariant &defaultValue) : + m_key(key), m_name(name), m_metaType(metaType), m_validator(validator), m_default(defaultValue) {} const QString m_key; const QString m_name; @@ -549,18 +549,18 @@ namespace BlackMisc const int m_metaType = QMetaType::UnknownType; const Validator m_validator; const CVariant m_default; - const NotifySlot m_notifySlot = nullptr; + NotifySlot m_notifySlot; int m_pendingChanges = 0; bool m_saved = false; }; - CValuePage::Element &CValuePage::createElement(const QString &key, const QString &name, int metaType, Validator validator, const CVariant &defaultValue, NotifySlot slot) + CValuePage::Element &CValuePage::createElement(const QString &key, const QString &name, int metaType, Validator validator, const CVariant &defaultValue) { Q_ASSERT_X(! m_elements.contains(key), "CValuePage", "Can't have two CCached in the same object referring to the same value"); Q_ASSERT_X(defaultValue.isValid() ? defaultValue.userType() == metaType : true, "CValuePage", "Metatype mismatch for default value"); Q_ASSERT_X(defaultValue.isValid() && validator ? validator(defaultValue) : true, "CValuePage", "Validator rejects default value"); - auto &element = *(m_elements[key] = ElementPtr(new Element(key, name, metaType, validator, defaultValue, slot))); + auto &element = *(m_elements[key] = ElementPtr(new Element(key, name, metaType, validator, defaultValue))); std::forward_as_tuple(element.m_value.uniqueWrite(), element.m_timestamp, element.m_saved) = m_cache->getValue(key); auto status = validate(element, element.m_value.read(), CStatusMessage::SeverityDebug); @@ -586,6 +586,11 @@ namespace BlackMisc return element; } + void CValuePage::setNotifySlot(Element &element, NotifySlot slot) + { + element.m_notifySlot = slot; + } + bool CValuePage::isValid(const Element &element, int typeId) const { auto reader = element.m_value.read(); @@ -667,7 +672,7 @@ namespace BlackMisc Q_ASSERT(QThread::currentThread() == thread()); Q_ASSERT_X(values.valuesChanged(), Q_FUNC_INFO, "packet with unchanged values should not reach here"); - QList notifySlots; + QList notifySlots; forEachIntersection(m_elements, values, [changedBy, this, ¬ifySlots, &values](const QString &, const ElementPtr & element, CValueCachePacket::const_iterator it) { @@ -684,9 +689,9 @@ namespace BlackMisc element->m_value.uniqueWrite() = it.value(); element->m_timestamp = it.timestamp(); element->m_saved = values.isSaved(); - if (element->m_notifySlot && ! notifySlots.contains(element->m_notifySlot)) + if (element->m_notifySlot && ! notifySlots.contains(&element->m_notifySlot)) { - notifySlots.push_back(element->m_notifySlot); + notifySlots.push_back(&element->m_notifySlot); } } else @@ -696,7 +701,7 @@ namespace BlackMisc } }); - for (auto slot : notifySlots) { (parent()->*slot)(); } + for (auto slot : notifySlots) { (*slot)(parent()); } } void CValuePage::beginBatch() diff --git a/src/blackmisc/valuecache.h b/src/blackmisc/valuecache.h index 4c47ec169..ce3a328eb 100644 --- a/src/blackmisc/valuecache.h +++ b/src/blackmisc/valuecache.h @@ -335,20 +335,14 @@ namespace BlackMisc class CCached { public: - //! Type of pointer to non-const member function of U taking no arguments and returning void, - //! for slot parameter of CCached constructor. - template - using NotifySlot = Private::TNonDeduced; - //! Constructor. //! \param cache The CValueCache object which manages the value. //! \param key The key string which identifies the value. //! \param name Human readable name corresponding to the key. //! \param owner Will be the parent of the internal QObject used for signal/slot connections. - //! \param slot A member function of owner which will be called when the value is changed by another source. template - CCached(CValueCache *cache, const QString &key, const QString &name, U *owner, NotifySlot slot = nullptr) : - CCached(cache, key, name, nullptr, T{}, owner, slot) + CCached(CValueCache *cache, const QString &key, const QString &name, U *owner) : + CCached(cache, key, name, nullptr, T{}, owner) {} //! Constructor. @@ -358,15 +352,24 @@ namespace BlackMisc //! \param validator A functor which tests the validity of a value and returns true if it is valid. //! \param defaultValue A value which will be used as default if the value is invalid. //! \param owner Will be the parent of the internal QObject used for signal/slot connections. - //! \param slot A member function of owner which will be called when the value is changed by another source. template - CCached(CValueCache *cache, const QString &key, const QString &name, F validator, const T &defaultValue, U *owner, NotifySlot slot = nullptr) : + CCached(CValueCache *cache, const QString &key, const QString &name, F validator, const T &defaultValue, U *owner) : m_page(Private::CValuePage::getPageFor(owner, cache)), - m_element(m_page.createElement(key, name, qMetaTypeId(), wrap(validator), CVariant::from(defaultValue), slot_cast(slot))) + m_element(m_page.createElement(key, name, qMetaTypeId(), wrap(validator), CVariant::from(defaultValue))) { cache->setHumanReadableName(key, name); } + //! Set a callback to be called when the value is changed by another source. + //! \todo Qt 5.7.0: in assert use m_page.parent()->metaObject()->inherits(&U::staticMetaObject) + template + void setNotifySlot(F slot) + { + using U = typename Private::TClassOfPointerToMember::type; + Q_ASSERT_X(m_page.parent()->inherits(U::staticMetaObject.className()), Q_FUNC_INFO, "Slot is member function of wrong class"); + m_page.setNotifySlot(m_element, [slot](QObject *obj) { Private::invokeSlot(slot, static_cast(obj)); }); + } + //! Read the current value. const T &getThreadLocal() const { static const T empty {}; return *(isValid() ? static_cast(getVariant().data()) : &empty); } @@ -416,9 +419,6 @@ namespace BlackMisc static Private::CValuePage::Validator wrap(F func) { return [func](const CVariant &value)->bool { return func(value.to()); }; } static Private::CValuePage::Validator wrap(std::nullptr_t) { return {}; } - template - static Private::CValuePage::NotifySlot slot_cast(F slot) { return static_cast(slot); } - const QVariant &getVariant() const { return m_page.getValue(m_element).getQVariant(); } QVariant getVariantCopy() const { return m_page.getValueCopy(m_element).getQVariant(); } bool isValid() const { return m_page.isValid(m_element, qMetaTypeId()); } diff --git a/src/blackmisc/valuecacheprivate.h b/src/blackmisc/valuecacheprivate.h index 7ff676b16..05fbf46f3 100644 --- a/src/blackmisc/valuecacheprivate.h +++ b/src/blackmisc/valuecacheprivate.h @@ -28,19 +28,20 @@ namespace BlackMisc { /*! - * \private Identity type trait. + * \private Trait for obtaining the class type of a pointer to member type, or QObject if T is not a pointer to member. */ template - struct TIdentity + struct TClassOfPointerToMember + { + using type = QObject; + }; + //! \cond + template + struct TClassOfPointerToMember { using type = T; }; - - /*! - * \private Trick to force a non-deduced context during template argument type deduction. - */ - template - using TNonDeduced = typename TIdentity::type; + //! \endcond /*! * \private QObject subclass used by CCached class template for signal/slot communication with CValueCache. @@ -61,8 +62,8 @@ namespace BlackMisc //! Functor used for validating values. using Validator = std::function; - //! Pointer-to-member-function type of slot to notify parent of changes. - using NotifySlot = void (QObject:: *)(); + //! Functor used to notify parent of changes. + using NotifySlot = std::function; //! Returns a new instance of the opaque Element type for use by CCached to interact with CValuePage. //! \param key The key string of the value in the cache. @@ -70,8 +71,10 @@ namespace BlackMisc //! \param metaType The Qt metatype ID of the value object's expected type. //! \param validator Optional functor which returns true if the value is valid. //! \param defaultValue Optional value which is used in case the value is invalid. - //! \param slot Optional member function of parent, will be called when value changes. - Element &createElement(const QString &key, const QString &name, int metaType, Validator validator, const CVariant &defaultValue, NotifySlot slot); + Element &createElement(const QString &key, const QString &name, int metaType, Validator validator, const CVariant &defaultValue); + + //! Set the functor to call to notify that the value corresponding to the element's key was modified. + void setNotifySlot(Element &element, NotifySlot slot); //! True if the currently paged value is a valid instance of the given type. //! \threadsafe diff --git a/tests/blackmisc/testvaluecache.cpp b/tests/blackmisc/testvaluecache.cpp index 75a9c77c8..1925d7cd4 100644 --- a/tests/blackmisc/testvaluecache.cpp +++ b/tests/blackmisc/testvaluecache.cpp @@ -270,9 +270,12 @@ namespace BlackMiscTest } CValueCacheUser::CValueCacheUser(CValueCache *cache) : - m_value1(cache, "value1", "", validator, 0, this, &CValueCacheUser::ps_valueChanged), - m_value2(cache, "value2", "", validator, 0, this, &CValueCacheUser::ps_valueChanged) - {} + m_value1(cache, "value1", "", validator, 0, this), + m_value2(cache, "value2", "", validator, 0, this) + { + m_value1.setNotifySlot(&CValueCacheUser::ps_valueChanged); + m_value2.setNotifySlot(&CValueCacheUser::ps_valueChanged); + } void CValueCacheUser::ps_valueChanged() {