mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-03-22 14:55:36 +08:00
refs #684, #766, #776 Support %OwnerName% in cache keys by allowing CCached::m_element to be a dummy
and by using a trick with explicit destructor call and placement new to reconstruct CCached when owner's name changes.
This commit is contained in:
@@ -309,6 +309,11 @@ namespace BlackMisc
|
||||
CData(T *owner) :
|
||||
CData::CCached(CDataCache::instance(), Trait::key(), Trait::humanReadable(), Trait::isValid, Trait::defaultValue(), owner)
|
||||
{
|
||||
if (! this->isInitialized())
|
||||
{
|
||||
this->onOwnerNameChanged([this, owner] { Private::reconstruct(this, owner); });
|
||||
return;
|
||||
}
|
||||
if (Trait::timeToLive() >= 0) { CDataCache::instance()->setTimeToLive(this->getKey(), Trait::timeToLive()); }
|
||||
if (Trait::isPinned()) { CDataCache::instance()->pinValue(this->getKey()); }
|
||||
if (Trait::isDeferred()) { CDataCache::instance()->deferValue(this->getKey()); }
|
||||
|
||||
@@ -74,7 +74,12 @@ namespace BlackMisc
|
||||
template <typename T>
|
||||
CSetting(T *owner) :
|
||||
CSetting::CCached(CSettingsCache::instance(), Trait::key(), Trait::humanReadable(), Trait::isValid, Trait::defaultValue(), owner)
|
||||
{}
|
||||
{
|
||||
if (! this->isInitialized())
|
||||
{
|
||||
this->onOwnerNameChanged([this, owner] { Private::reconstruct(this, owner); });
|
||||
}
|
||||
}
|
||||
|
||||
//! Constructor.
|
||||
//! \param owner Will be the parent of the internal QObject used to access the value.
|
||||
|
||||
@@ -48,7 +48,6 @@ namespace BlackMisc
|
||||
template <typename T>
|
||||
bool isSafeToIncrement(const T &value) { return value < std::numeric_limits<T>::max(); }
|
||||
|
||||
|
||||
//! \private
|
||||
std::pair<QString &, std::atomic<bool> &> getCacheRootDirectoryMutable()
|
||||
{
|
||||
@@ -557,11 +556,16 @@ namespace BlackMisc
|
||||
|
||||
CValuePage::Element &CValuePage::createElement(const QString &keyTemplate, const QString &name, int metaType, Validator validator, const CVariant &defaultValue)
|
||||
{
|
||||
auto *category = parent()->findChild<CValueCacheCategory *>();
|
||||
if (parent()->objectName().isEmpty() && keyTemplate.contains("%OwnerName%"))
|
||||
{
|
||||
static Element dummy("", "", QMetaType::UnknownType, nullptr, {});
|
||||
return dummy;
|
||||
}
|
||||
|
||||
QString key = keyTemplate;
|
||||
key.replace("%Application%", QFileInfo(QCoreApplication::applicationFilePath()).completeBaseName(), Qt::CaseInsensitive);
|
||||
key.replace("%OwnerClass%", QString(parent()->metaObject()->className()).replace("::", "/"), Qt::CaseInsensitive);
|
||||
key.replace("%OwnerCategory%", category ? category->getCategory() : QString(parent()->metaObject()->className()).replace("::", "/"), Qt::CaseInsensitive);
|
||||
key.replace("%OwnerName%", parent()->objectName(), Qt::CaseInsensitive);
|
||||
|
||||
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");
|
||||
@@ -598,6 +602,11 @@ namespace BlackMisc
|
||||
element.m_notifySlot = slot;
|
||||
}
|
||||
|
||||
bool CValuePage::isInitialized(const Element &element) const
|
||||
{
|
||||
return ! element.m_key.isEmpty();
|
||||
}
|
||||
|
||||
bool CValuePage::isValid(const Element &element, int typeId) const
|
||||
{
|
||||
auto reader = element.m_value.read();
|
||||
@@ -606,6 +615,7 @@ namespace BlackMisc
|
||||
|
||||
const CVariant &CValuePage::getValue(const Element &element) const
|
||||
{
|
||||
Q_ASSERT_X(! element.m_key.isEmpty(), Q_FUNC_INFO, "Empty key suggests an attempt to use value before objectName available for %%OwnerName%%");
|
||||
Q_ASSERT(QThread::currentThread() == thread());
|
||||
|
||||
return element.m_value.read();
|
||||
@@ -613,11 +623,13 @@ namespace BlackMisc
|
||||
|
||||
CVariant CValuePage::getValueCopy(const Element &element) const
|
||||
{
|
||||
Q_ASSERT_X(! element.m_key.isEmpty(), Q_FUNC_INFO, "Empty key suggests an attempt to use value before objectName available for %%OwnerName%%");
|
||||
return element.m_value.read();
|
||||
}
|
||||
|
||||
CStatusMessage CValuePage::setValue(Element &element, CVariant value, qint64 timestamp, bool save, bool ignoreValue)
|
||||
{
|
||||
Q_ASSERT_X(! element.m_key.isEmpty(), Q_FUNC_INFO, "Empty key suggests an attempt to use value before objectName available for %%OwnerName%%");
|
||||
Q_ASSERT(QThread::currentThread() == thread());
|
||||
|
||||
if (timestamp == 0) { timestamp = QDateTime::currentMSecsSinceEpoch(); }
|
||||
@@ -661,6 +673,7 @@ namespace BlackMisc
|
||||
|
||||
qint64 CValuePage::getTimestamp(const Element &element) const
|
||||
{
|
||||
Q_ASSERT_X(! element.m_key.isEmpty(), Q_FUNC_INFO, "Empty key suggests an attempt to use value before objectName available for %%OwnerName%%");
|
||||
return element.m_timestamp;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include <cstddef>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
|
||||
namespace BlackMisc
|
||||
{
|
||||
@@ -357,7 +358,7 @@ namespace BlackMisc
|
||||
m_page(Private::CValuePage::getPageFor(owner, cache)),
|
||||
m_element(m_page.createElement(key, name, qMetaTypeId<T>(), wrap(validator), CVariant::from(defaultValue)))
|
||||
{
|
||||
cache->setHumanReadableName(key, name);
|
||||
if (isInitialized()) { cache->setHumanReadableName(getKey(), name); }
|
||||
}
|
||||
|
||||
//! Set a callback to be called when the value is changed by another source.
|
||||
@@ -365,6 +366,11 @@ namespace BlackMisc
|
||||
template <typename F>
|
||||
void setNotifySlot(F slot)
|
||||
{
|
||||
if (! isInitialized())
|
||||
{
|
||||
onOwnerNameChanged([this, slot] { setNotifySlot(slot); });
|
||||
return;
|
||||
}
|
||||
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)); }, makeId(slot) });
|
||||
@@ -408,6 +414,9 @@ namespace BlackMisc
|
||||
//! Return true if this value is currently saving.
|
||||
bool isSaving() const { return m_page.isSaving(m_element); }
|
||||
|
||||
//! Can be false if key contains %OwnerName% and owner's objectName was empty.
|
||||
bool isInitialized() const { return m_page.isInitialized(m_element); }
|
||||
|
||||
//! Deleted copy constructor.
|
||||
CCached(const CCached &) = delete;
|
||||
|
||||
@@ -429,6 +438,17 @@ namespace BlackMisc
|
||||
bool isValid() const { return m_page.isValid(m_element, qMetaTypeId<T>()); }
|
||||
|
||||
protected:
|
||||
//! \private Connect a function to be called (only once) when the owner's objectName changes.
|
||||
void onOwnerNameChanged(std::function<void()> function)
|
||||
{
|
||||
auto connection = std::make_shared<QMetaObject::Connection>();
|
||||
*connection = QObject::connect(m_page.parent(), &QObject::objectNameChanged, [connection, function](const QString &)
|
||||
{
|
||||
QObject::disconnect(*connection);
|
||||
function();
|
||||
});
|
||||
}
|
||||
|
||||
Private::CValuePage &m_page; //!< \private
|
||||
Private::CValuePage::Element &m_element; //!< \private
|
||||
};
|
||||
|
||||
@@ -43,6 +43,16 @@ namespace BlackMisc
|
||||
};
|
||||
//! \endcond
|
||||
|
||||
/*!
|
||||
* \private Destroy an object and reconstruct it with the given constructor arguments.
|
||||
*/
|
||||
template <typename T, typename... Args>
|
||||
void reconstruct(T *object, Args &&... args)
|
||||
{
|
||||
object->~T();
|
||||
new (object) T(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \private QObject subclass used by CCached<T> class template for signal/slot communication with CValueCache.
|
||||
* An instance of this class is shared between all CCached<T> referring to the same CValueCache and owned by the same QObject,
|
||||
@@ -76,6 +86,9 @@ namespace BlackMisc
|
||||
//! 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 corresponds to a valid key.
|
||||
bool isInitialized(const Element &element) const;
|
||||
|
||||
//! True if the currently paged value is a valid instance of the given type.
|
||||
//! \threadsafe
|
||||
bool isValid(const Element &element, int typeId) const;
|
||||
|
||||
Reference in New Issue
Block a user