dbus/properties: add QObjectBindableProperty based dbus property

Many times more lightweight than the original QObject-based one.
This commit is contained in:
outfoxxed 2024-11-20 19:22:23 -08:00
parent 4163713bc4
commit 1955deee74
Signed by: outfoxxed
GPG key ID: 4C88A185FB89301E
3 changed files with 151 additions and 15 deletions

View file

@ -2,22 +2,45 @@
#include <algorithm>
#include <type_traits>
#include <bit>
#include <qlatin1stringview.h>
#include <qobject.h>
#include <qproperty.h>
#include <qstringview.h>
#include <qtclasshelpermacros.h>
#include <qtmetamacros.h>
template <size_t Length>
template <size_t length>
struct StringLiteral {
constexpr StringLiteral(const char (&str)[Length]) { // NOLINT
std::copy_n(str, Length, this->value);
constexpr StringLiteral(const char (&str)[length]) { // NOLINT
std::copy_n(str, length, this->value);
}
constexpr operator const char*() const noexcept { return this->value; }
operator QLatin1StringView() const { return QLatin1String(this->value, Length); }
operator QLatin1StringView() const { return QLatin1String(this->value, length); }
char value[Length]; // NOLINT
char value[length]; // NOLINT
};
template <size_t length>
struct StringLiteral16 {
constexpr StringLiteral16(const char16_t (&str)[length]) { // NOLINT
std::copy_n(str, length, this->value);
}
[[nodiscard]] constexpr const QChar* qCharPtr() const noexcept {
return std::bit_cast<const QChar*>(&this->value);
}
[[nodiscard]] Q_ALWAYS_INLINE operator QString() const noexcept {
return QString::fromRawData(this->qCharPtr(), static_cast<qsizetype>(length - 1));
}
[[nodiscard]] Q_ALWAYS_INLINE operator QStringView() const noexcept {
return QStringView(this->qCharPtr(), static_cast<qsizetype>(length - 1));
}
char16_t value[length]; // NOLINT
};
// NOLINTBEGIN
@ -149,11 +172,10 @@ private:
// NOLINTEND
template <typename T>
class MemberPointerTraits;
struct MemberPointerTraits;
template <typename T, typename C>
class MemberPointerTraits<T C::*> {
public:
struct MemberPointerTraits<T C::*> {
using Class = C;
using Type = T;
};

View file

@ -162,6 +162,10 @@ void DBusPropertyGroup::attachProperty(AbstractDBusProperty* property) {
property->group = this;
}
void DBusPropertyGroup::attachProperty(DBusPropertyCore* property) {
this->properties.append(property);
}
void DBusPropertyGroup::updateAllDirect() {
qCDebug(logDbusProperties).noquote()
<< "Updating all properties of" << this->toString() << "via individual queries";

View file

@ -3,6 +3,7 @@
#include <functional>
#include <utility>
#include <bit>
#include <qcontainerfwd.h>
#include <qdbusabstractinterface.h>
#include <qdbuserror.h>
@ -14,10 +15,14 @@
#include <qlogging.h>
#include <qloggingcategory.h>
#include <qobject.h>
#include <qoverload.h>
#include <qstringview.h>
#include <qtclasshelpermacros.h>
#include <qtmetamacros.h>
#include <qvariant.h>
#include "../core/util.hpp"
class DBusPropertiesInterface;
Q_DECLARE_LOGGING_CATEGORY(logDbusProperties);
@ -132,21 +137,94 @@ private:
friend class DBusPropertyGroup;
};
namespace bindable_p {
template <typename T>
struct BindableParams;
template <template <typename, typename, auto, auto> class B, typename C, typename T, auto O, auto S>
struct BindableParams<B<C, T, O, S>> {
using Class = C;
using Type = T;
static constexpr size_t OFFSET = O;
};
} // namespace bindable_p
template <
auto offset,
auto bindablePtr,
auto updatedPtr,
auto groupPtr,
StringLiteral16 Name,
bool required>
class DBusBindableProperty: public DBusPropertyCore {
using PtrMeta = MemberPointerTraits<decltype(bindablePtr)>;
using Bindable = PtrMeta::Type;
using Owner = PtrMeta::Class;
using BindableMeta = bindable_p::BindableParams<Bindable>;
using DataType = BindableMeta::Type;
public:
explicit DBusBindableProperty() { this->group()->attachProperty(this); }
[[nodiscard]] QString name() const override { return Name; };
[[nodiscard]] QStringView nameRef() const override { return Name; };
[[nodiscard]] bool isRequired() const override { return required; };
[[nodiscard]] QString valueString() override {
QString str;
QDebug(&str) << this->bindable()->value();
return str;
}
void write() { this->group()->pushPropertyUpdate(this); }
void requestUpdate() { this->group()->requestPropertyUpdate(this); }
protected:
QDBusError store(const QVariant& variant) override {
auto result = demarshallVariant<DataType>(variant);
if (result.isValid()) {
this->bindable()->setValue(std::move(result.value));
if constexpr (updatedPtr != nullptr) {
(this->owner()->*updatedPtr)();
}
}
return result.error;
}
QVariant serialize() override { return QVariant::fromValue(this->bindable()->value()); }
private:
[[nodiscard]] constexpr Owner* owner() const {
auto* self = std::bit_cast<char*>(this);
return std::bit_cast<Owner*>(self - offset()); // NOLINT
}
[[nodiscard]] constexpr DBusPropertyGroup* group() const { return &(this->owner()->*groupPtr); }
[[nodiscard]] constexpr Bindable* bindable() const { return &(this->owner()->*bindablePtr); }
};
class DBusPropertyGroup: public QObject {
Q_OBJECT;
public:
explicit DBusPropertyGroup(
QVector<DBusPropertyCore*> properties = QVector<DBusPropertyCore*>(),
QObject* parent = nullptr
);
explicit DBusPropertyGroup(QVector<DBusPropertyCore*> properties = {}, QObject* parent = nullptr);
explicit DBusPropertyGroup(QObject* parent): DBusPropertyGroup({}, parent) {}
void setInterface(QDBusAbstractInterface* interface);
void attachProperty(AbstractDBusProperty* property);
void attachProperty(DBusPropertyCore* property);
void updateAllDirect();
void updateAllViaGetAll();
[[nodiscard]] QString toString() const;
void pushPropertyUpdate(DBusPropertyCore* property);
void requestPropertyUpdate(DBusPropertyCore* property);
signals:
void getAllFinished();
void getAllFailed(QDBusError error);
@ -160,8 +238,6 @@ private slots:
private:
void updatePropertySet(const QVariantMap& properties, bool complainMissing);
void requestPropertyUpdate(DBusPropertyCore* property);
void pushPropertyUpdate(DBusPropertyCore* property);
void tryUpdateProperty(DBusPropertyCore* property, const QVariant& variant) const;
[[nodiscard]] QString propertyString(const DBusPropertyCore* property) const;
@ -224,7 +300,41 @@ protected:
private:
T value;
friend class DBusPropertyGroup;
friend class DBusPropertyCore;
};
} // namespace qs::dbus
// NOLINTBEGIN
#define QS_DBUS_BINDABLE_PROPERTY_GROUP(Class, name) qs::dbus::DBusPropertyGroup name {this};
#define QS_DBUS_PROPERTY_BINDING_P(Class, property, bindable, updated, group, name, required) \
static constexpr size_t _qs_property_##property##_offset() { return offsetof(Class, property); } \
\
qs::dbus::DBusBindableProperty< \
&Class::_qs_property_##property##_offset, \
&Class::bindable, \
updated, \
&Class::group, \
u##name, \
required> \
property;
#define QS_DBUS_PROPERTY_BINDING_7(Class, property, bindable, updated, group, name, required) \
QS_DBUS_PROPERTY_BINDING_P(Class, property, bindable, &Class::updated, group, name, required)
#define QS_DBUS_PROPERTY_BINDING_6(Class, property, bindable, group, name, required) \
QS_DBUS_PROPERTY_BINDING_P(Class, property, bindable, nullptr, group, name, required)
#define QS_DBUS_PROPERTY_BINDING_5(Class, property, bindable, group, name) \
QS_DBUS_PROPERTY_BINDING_6(Class, property, bindable, group, name, true)
// Q_OBJECT_BINDABLE_PROPERTY elides the warning for the exact same reason,
// so we consider it safe to disable the warning.
// clang-format off
#define QS_DBUS_PROPERTY_BINDING(...) \
QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
QT_OVERLOADED_MACRO(QS_DBUS_PROPERTY_BINDING, __VA_ARGS__) \
QT_WARNING_POP
// clang-format on
// NOLINTEND