QVariant вызывает функцию хранимого объекта, определенного в основной части проекта - PullRequest
0 голосов
/ 19 мая 2018

Общая часть (создание библиотеки) моего проекта в настоящее время содержит интерфейсный класс типа:

class CanBeAddedToGroup{
public:
void addToGroup(Group& )=0;
}

Теперь я также хотел использовать программу в классе, содержащем данные в QVariant, поэтомуя начал с простого:

class DataContainingClass: public CanBeAddedToGroup{
QMap<QString,QVarient> data;

public:
void addToGroup(Group& ){
  QMap<QString,QVarient>::itterator itter = data.begin();
  QMap<QString,QVarient>::itterator end= data.end();
  for(;itter !=end;itter ++){
      <Handle Data>
    }
  }
  };
}

Теперь один из типов данных, добавляемых в список (вне библиотеки), имеет тип:

class DataClass: public QObject, public CanBeAddedToGroup{
void addToGroup(Group& );
}
Q_DECLARE_METATYPE(DataClass)

И он добавляется на карту.используя «QVariant :: fromValue (», теперь мне нужен путь в «DataConistingClass», чтобы проверить, получены ли данные из QObject, поэтому я знаю, что static_cast (variable.data ()) действителен. Тогда я мог бы попытаться выполнить dynamic_castуказатель объекта на CanBeAddedToGroup и вызов его.

--- EDIT ---: Проблема НЕТ: наличие QObject и проверка, наследует ли он другой QObject, даже не проверяет, наследует ли класс отдругой - узнать, действительно ли данные, которые у меня есть, являются QObject.

Минимальный пример: Заголовочный файл:

#include <QObject>
#include <QDebug>
class DmbClass: public QObject{
    Q_OBJECT
public:
    DmbClass(){TST="RealOne";}
    DmbClass(const DmbClass&d){TST="Clone";}
    QString TST;
};
Q_DECLARE_METATYPE(DmbClass)

class Tst{
public:
    virtual void tstFunct()=0;
};

class CanClass: public QObject, public Tst{
    Q_OBJECT
public:
    CanClass(){TST="RealOne";}
    CanClass(const CanClass&d){TST="Clone";}
    QString TST;

    virtual void tstFunct() override{
        qDebug()<<"tst";
    }
};
Q_DECLARE_METATYPE(CanClass)


class DmbGadget{
    Q_GADGET
public:
    DmbGadget(){TST="RealOne";}
    DmbGadget(const DmbGadget&d){TST="Clone";}
    QString TST;
};
Q_DECLARE_METATYPE(DmbGadget)

C Файл:

// QObject in QVariant
DmbClass dC;
QVariant varC=QVariant::fromValue(dC);
const void* vPC = varC.data();
DmbClass dCc = varC.value<DmbClass>();
QObject* objC = (QObject*)varC.data();
QObject* schouldWork = objC->parent();
Tst* schouldBeNull = dynamic_cast<Tst*>(objC);


// Object using correct base class in QVariant
CanClass dT;
QVariant varT=QVariant::fromValue(dT);
const void* vPT = varT.data();
CanClass dTc = varC.value<CanClass>();
QObject* objT = (QObject*)varT.data();
QObject* schouldWork2 = objT->parent();
Tst* schouldNotNull = dynamic_cast<Tst*>(objT);
schouldNotNull->tstFunct();

// Q_Gadget in QVariant
DmbGadget dG;
QVariant varG=QVariant::fromValue(dG);
const void* vPG = varG.data();
DmbGadget dGg = varG.value<DmbGadget>();
QObject* objD = (QObject*)varG.data();
//QObject* schouldSegFault = objD->parent();

// base value in QVariant
QVariant var4=4;
const void* vP4 = var4.data();
QObject* obj4 = (QObject*)var4.data();
//QObject* schouldSegFault2 = obj4 ->parent();

Мне нужен способ отличить случаи 1 и 2 от 3 и 4 ("schouldSegОшибка "), без использования чего-либо, определенного только за пределами библиотеки.

Я уже пробовал:

int tst4 = qRegisterMetaType<CanClass>("CanClass");
QMetaType help2(tst4);

Но help2 имеет MetaObject, равный 0, поэтому я не могу проверить наследование от QObject.

Редактировать / для того, кто добавил " Правильный способ проверки типа производного класса QObject в Qt ", в моей программе была проблема, что класс наследует от другого QObject класса, поэтому я не могуЧак за наследование моего интерфейса (даже если он определен как Q_Gadget) с использованием наследников, поскольку это будет верно только для первого элемента.

PS: Для всех, кто пытается вызывать функции в QVariant, содержащем объект, а неуказатель может быть заинтересован в таком подходе: Как поддерживать сравнения для объектов QVariant, содержащих пользовательский тип? / https://pastebin.com/tNLa0jSa

Хотя наличие глобального реестра для типов - это то, чего я хотел бы избежатьдля дела.

Ответы [ 2 ]

0 голосов
/ 20 мая 2018

Ваш дизайн полностью нарушен: QObject s нельзя использовать в качестве неограниченных значений.Они не могут быть ни скопированы, ни перемещены, и ваши реализации конструкторов копирования скрывают этот фундаментальный факт.

0 голосов
/ 19 мая 2018

Просто попробуйте использовать QVariant :: value и посмотрите, можно ли преобразовать значение в QVariant в целевой класс.Вот минимальный пример:

#include <QObject>
#include <QVariant>
#include <QVariantMap>
#include <QDebug>


class MyQObjectClass : public QObject {
    Q_OBJECT
public:
    explicit MyQObjectClass(QObject *parent = nullptr) : QObject(parent) {}
    void greet() { qDebug() << "I am a MyQObjectClass!"; }
};

Q_DECLARE_METATYPE(MyQObjectClass*)


int main(int, char *[])
{
    MyQObjectClass obj;
    QVariantMap map;
    map.insert("foo", QString("Hello World"));
    map.insert("bar", QVariant::fromValue(&obj));

    QVariantMap::iterator iter = map.begin();
    QVariantMap::iterator end= map.end();
    for(;iter !=end;iter ++) {
        auto value = iter.value();
        // Try to convert to MyQObjectClass*
        auto obj = value.value<MyQObjectClass*>();
        if (obj != nullptr) {
            qDebug() << iter.key() << "is an instance of MyQObjectClass";
            obj->greet();
            continue;
        }
        qDebug() << iter.key() << "is not an instance of MyQObjectClass";
    }
}

#include "main.moc"

Запуск его должен привести к следующему выводу на консоли:

"bar" is an instance of MyQObjectClass
I am a MyQObjectClass!
"foo" is not an instance of MyQObjectClass

Важные части:

  1. Убедитесь, чтокласс, который вы хотите сохранить в QVariant, наследуется от QObject и имеет макрос Q_OBJECT.
  2. При выполнении итерации по карте используйте QVariant::value() и попытайтесь преобразовать содержащееся в нем значение в целевой класс.В этом примере я использую QVariant::value<MyQObjectClass*>() - в соответствии с документацией это либо возвращает содержащийся экземпляр MyQObjectClass*, если значение может быть преобразовано в него, либо - что в случае, если QVariant содержит базовые значения или гаджеты- построенное значение по умолчанию.В случае указателя это будет нулевой указатель, поэтому просто проверьте, является ли возвращаемое значение нулевым.Вот и все.

Никогда не работайте на Qvariant::data() напрямую.

Обновление

Так же, как замечание: объявлен класс Qobjectконструктор копирования и операторы присваивания как частные:

Из официальной документации :

QObject не имеет ни конструктора копирования, ни оператора присваивания.Это по замыслу.На самом деле они объявлены, но в закрытом разделе с макросом Q_DISABLE_COPY ().Фактически, все классы Qt, производные от QObject (прямые или косвенные), используют этот макрос, чтобы объявить их конструктор копирования и оператор присваивания закрытыми.Обоснование можно найти в обсуждении Identity vs Value на странице Qt Object Model.

Следовательно, вы не можете копировать вокруг экземпляров QObject (и, следовательно, вы не можете хранить их в QVariant),Вместо этого вы передаете указатели QObject экземплярам.

Update # 2

Если ваш интерфейсный класс не может быть производным непосредственно от QObject, вы можете рассмотреть возможность использования Механизм подключаемого модуля Qt .Вот приведенный выше пример, слегка отредактированный для соответствия этому подходу:

#include <QObject>
#include <QVariant>
#include <QVariantMap>
#include <QDebug>


class MyInterfaceClass {
public:
    MyInterfaceClass() {}
    virtual ~MyInterfaceClass() {}
    virtual void greet() = 0;
};

#define MyInterfaceClass_IID "org.example.MyInterfaceClass"
Q_DECLARE_INTERFACE(MyInterfaceClass, MyInterfaceClass_IID)


class MyConcreteClass : public QObject, public MyInterfaceClass {
    Q_OBJECT
    Q_INTERFACES(MyInterfaceClass)
public:
    MyConcreteClass(QObject *parent = nullptr) : QObject(parent) {}
    void greet() override { qDebug() << "I am a MyInterfaceClass!"; }
};


int main(int, char *[])
{
    MyConcreteClass obj;
    QVariantMap map;
    map.insert("foo", QString("Hello World"));
    map.insert("bar", QVariant::fromValue(&obj));

    QVariantMap::iterator iter = map.begin();
    QVariantMap::iterator end= map.end();
    for(;iter !=end;iter ++) {
        auto value = iter.value();
        // Try to convert to QObject*:
        auto obj = value.value<QObject*>();
        if (obj != nullptr) {
            // Try if we can cast to our interface class:
            auto ifc = qobject_cast<MyInterfaceClass*>(obj);
            if (ifc != nullptr) {
                qDebug() << iter.key() << "is an instance of MyInterfaceClass";
                ifc->greet();
            }
            continue;
        }
        qDebug() << iter.key() << "is not an instance of MyInterfaceClass";
    }
}

#include "main.moc"

Вам необходимо:

  1. Определить класс интерфейса и зарегистрировать его в Qt с помощью макроса Q_DECLARE_INTERFACE.
  2. Объявите ваши конкретные классы, производные от QObject и ваш интерфейсный класс.Кроме того, вам нужно сообщить Qt об интерфейсной части с помощью макроса Q_INTERFACES.
  3. При проверке значений на вашей карте, сначала попробуйте преобразовать в QObject* через QVariant::value().Если это удастся, вы можете попробовать qobject_cast к вашему классу интерфейса.
...