mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-03-22 23:05:36 +08:00
refs #759 Allow cache value notification slot to be changed after construction.
Also allow slot to be any type of callable, including member function or lambda.
This commit is contained in:
committed by
Roland Winklmeier
parent
d24c17eba2
commit
e01ae2be11
@@ -286,17 +286,11 @@ namespace BlackMisc
|
||||
class CData : public BlackMisc::CCached<typename Trait::type>
|
||||
{
|
||||
public:
|
||||
//! \copydoc BlackMisc::CCached::NotifySlot
|
||||
template <typename T>
|
||||
using NotifySlot = typename BlackMisc::CCached<typename Trait::type>::template NotifySlot<T>;
|
||||
|
||||
//! 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 <typename T>
|
||||
CData(T *owner, NotifySlot<T> 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 <typename T, typename F>
|
||||
CData(T *owner, F slot) : CData(owner)
|
||||
{
|
||||
this->setNotifySlot(slot);
|
||||
}
|
||||
|
||||
//! \copydoc BlackMisc::CCached::set
|
||||
CStatusMessage set(const typename Trait::type &value, qint64 timestamp = 0)
|
||||
{
|
||||
|
||||
@@ -69,18 +69,22 @@ namespace BlackMisc
|
||||
class CSetting : public BlackMisc::CCached<typename Trait::type>
|
||||
{
|
||||
public:
|
||||
//! \copydoc BlackMisc::CCached::NotifySlot
|
||||
//! Constructor.
|
||||
//! \param owner Will be the parent of the internal QObject used to access the value.
|
||||
template <typename T>
|
||||
using NotifySlot = typename BlackMisc::CCached<typename Trait::type>::template NotifySlot<T>;
|
||||
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 <typename T>
|
||||
CSetting(T *owner, NotifySlot<T> slot = nullptr) :
|
||||
CSetting::CCached(CSettingsCache::instance(), Trait::key(), Trait::humanReadable(), Trait::isValid, Trait::defaultValue(), owner, slot)
|
||||
{}
|
||||
template <typename T, typename F>
|
||||
CSetting(T *owner, F slot) : CSetting(owner)
|
||||
{
|
||||
this->setNotifySlot(slot);
|
||||
}
|
||||
|
||||
//! Reset the setting to its default value.
|
||||
CStatusMessage setDefault() { return this->set(Trait::defaultValue()); }
|
||||
|
||||
@@ -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<NotifySlot> notifySlots;
|
||||
QList<NotifySlot *> 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()
|
||||
|
||||
@@ -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 <typename U>
|
||||
using NotifySlot = Private::TNonDeduced<void (U::*)()>;
|
||||
|
||||
//! 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 <typename U>
|
||||
CCached(CValueCache *cache, const QString &key, const QString &name, U *owner, NotifySlot<U> 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 <typename U, typename F>
|
||||
CCached(CValueCache *cache, const QString &key, const QString &name, F validator, const T &defaultValue, U *owner, NotifySlot<U> 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<T>(), wrap(validator), CVariant::from(defaultValue), slot_cast(slot)))
|
||||
m_element(m_page.createElement(key, name, qMetaTypeId<T>(), 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 <typename F>
|
||||
void setNotifySlot(F slot)
|
||||
{
|
||||
using U = typename Private::TClassOfPointerToMember<F>::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<U *>(obj)); });
|
||||
}
|
||||
|
||||
//! Read the current value.
|
||||
const T &getThreadLocal() const { static const T empty {}; return *(isValid() ? static_cast<const T *>(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<T>()); }; }
|
||||
static Private::CValuePage::Validator wrap(std::nullptr_t) { return {}; }
|
||||
|
||||
template <typename F>
|
||||
static Private::CValuePage::NotifySlot slot_cast(F slot) { return static_cast<Private::CValuePage::NotifySlot>(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<T>()); }
|
||||
|
||||
@@ -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 <typename T>
|
||||
struct TIdentity
|
||||
struct TClassOfPointerToMember
|
||||
{
|
||||
using type = QObject;
|
||||
};
|
||||
//! \cond
|
||||
template <typename T, typename M>
|
||||
struct TClassOfPointerToMember<M T::*>
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \private Trick to force a non-deduced context during template argument type deduction.
|
||||
*/
|
||||
template <typename T>
|
||||
using TNonDeduced = typename TIdentity<T>::type;
|
||||
//! \endcond
|
||||
|
||||
/*!
|
||||
* \private QObject subclass used by CCached<T> class template for signal/slot communication with CValueCache.
|
||||
@@ -61,8 +62,8 @@ namespace BlackMisc
|
||||
//! Functor used for validating values.
|
||||
using Validator = std::function<bool(const CVariant &)>;
|
||||
|
||||
//! 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<void(QObject *)>;
|
||||
|
||||
//! Returns a new instance of the opaque Element type for use by CCached<T> 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
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user