[英]How to properly use qRegisterMetaType on a class derived from QObject?
I've been searching far and wide for an answer to this but to no avail. 我一直在寻找答案,但无济于事。 My lament is as follows:
我的哀叹如下:
I have a ClassA
that roughly looks like this: 我有一个大致如下的
ClassA
:
class ClassA : public QObject {
Q_OBJECT
public:
ClassA() { mName = "lol"; }
~ClassA();
void ShowName() { std::cout << mName << std::endl; }
std::string mName;
};
Of course, since I use moc, this class is actually split into cpp and hpp in my project but that part is not the issue here. 当然,因为我使用moc,这个类实际上在我的项目中被分成cpp和hpp,但这部分不是问题。
Please note that I do not use Q_DECLARE_METATYPE
on purpose because I don't actually need its features (QVariant expansion) right now. 请注意,我没有故意使用
Q_DECLARE_METATYPE
,因为我现在实际上并不需要它的功能(QVariant扩展)。 I only care about runtime instantiation. 我只关心运行时实例化。
The issue here is that Q_OBJECT
forbids the copy and assignment constructors. 这里的问题是
Q_OBJECT
禁止复制和赋值构造函数。 Due to that, I have to apply qRegisterMetaType
not to ClassA
itself but to ClassA*
which seems to work fine at first glance. 因此,我必须将
qRegisterMetaType
不应用于ClassA
本身,而是应用于ClassA*
,它似乎乍看之下工作正常。
Now, I want to create this class dynamically at runtime from a string and run the method ShowName()
. 现在,我想在运行时从字符串动态创建此类并运行
ShowName()
方法。 I'm doing that like this: 我是这样做的:
int main() {
qRegisterMetaType<ClassA*>("ClassA*");
int id = QMetaType::type("ClassA*");
std::cout << "meta id: " << id << std::endl; // Outputs correct generated user id (not 0)
ClassA* myclass = static_cast<ClassA*>(QMetaType::construct(id));
myclass->ShowName(); // Segfaults, oh dear
return 0;
}
Now, there is my issue. 现在,有我的问题。 I don't seem to actually have a correctly constructed object there.
我似乎并没有真正构造正确的对象。
If we change the class to look like this: 如果我们将类更改为如下所示:
class ClassA : public QObject {
Q_OBJECT
public:
ClassA() { mName = "lol"; }
ClassA(const ClassA& other) { assert(false && "DONT EVER USE THIS"); }
~ClassA();
void ShowName() { std::cout << mName << std::endl; }
std::string mName;
};
then we can change our program accordingly to: 然后我们可以相应地改变我们的程序:
int main() {
qRegisterMetaType<ClassA>("ClassA");
int id = QMetaType::type("ClassA");
std::cout << "meta id: " << id << std::endl; // Outputs correct generated user id (not 0)
ClassA* myclass = static_cast<ClassA*>(QMetaType::construct(id));
myclass->ShowName(); // "lol", yay
return 0;
}
Obviously I could just use my fake overwritten copy constructor but it doesn't feel right and Qt suggests against that and instead suggests the use of pointers to QObjects only. 显然我可以使用我的假覆盖复制构造函数,但它感觉不对,Qt暗示反对,而是建议仅使用指向QObjects的指针。
Does anyone see what's wrong here? 有谁看到这里有什么问题? Also, I am aware there are similar questions on SO but none of them tackle this exact problem.
此外,我知道在SO上有类似的问题,但没有一个解决这个问题。
A few things: 一些东西:
The reason that registering ClassA* isn't working is because your call to construct() is constructing a pointer to a ClassA object, but not an actual object. 注册ClassA *不起作用的原因是因为你对construct()的调用是构造一个指向ClassA对象的指针,而不是一个实际的对象。
It is worthy of noting the following quote from the QMetaType documentation: 值得注意的是QMetaType文档中的以下引用:
Any class or struct that has a public default constructor, a public copy constructor, and a public destructor can be registered.
可以注册具有公共默认构造函数,公共复制构造函数和公共析构函数的任何类或结构。
Take a look at Qt's implementation of qMetaTypeConstructHelper: 看看Qt的qMetaTypeConstructHelper实现:
template <typename T> void *qMetaTypeConstructHelper(const T *t) { if (!t) return new T(); return new T(*static_cast<const T*>(t)); }
and note their usage of the copy constructor. 并注意它们对复制构造函数的使用。 This being the case, you have two ways around the problem:
在这种情况下,您有两种解决问题的方法:
1) Provide a copy constructor (which you have done) 1)提供一个拷贝构造函数(你已经完成)
2) Provide a specialization of qMetaTypeConstructHelper that doesn't use the copy constructor: 2)提供不使用复制构造函数的qMetaTypeConstructHelper的特化:
template <>
void *qMetaTypeConstructHelper<ClassA>(const ClassA *)
{
return new ClassA();
}
If you want to create instances of QObject
classes by name, you can use QMetaObject
instead of QMetaType
. 如果
QMetaObject
名称创建QObject
类的实例,可以使用QMetaObject
而不是QMetaType
。
First, you have to declare your constructor as invokable: 首先,您必须将构造函数声明为invokable:
class ClassA : public QObject {
Q_OBJECT
public:
Q_INVOKABLE ClassA() { mName = "lol"; }
~ClassA();
void showName() { std::cout << mName << std::endl; }
std::string mName;
};
Then you have to create your own registration system for the classes you want to instantiate, and populate it manually: 然后,您必须为要实例化的类创建自己的注册系统,并手动填充它:
int main(int argc, char *argv[])
{
// Register your QObject derived classes
QList<const QMetaObject*> metaObjectList;
metaObjectList << &ClassA::staticMetaObject;
// Index the classes/metaobject by their names
QMap<QString, const QMetaObject*> metaObjectLookup;
foreach(const QMetaObject *mo, metaObjectList) {
metaObjectLookup.insert(mo->className(), mo);
}
And finally you'll be able instantiate by name any registered class: 最后,您将能够通过名称实例化任何已注册的类:
const QMetaObject * myMetaObject = metaObjectLookup.value("ClassA", 0);
if(!myMetaObject)
{
// The class doesn't exist
return 1;
}
ClassA *myObject =
static_cast<ClassA*>(myMetaObject->newInstance());
if(!myObject)
{
// Couldn't create an instance (constructor not declared Q_INVOKABLE ?)
return 1;
}
myObject->showName();
return 0;
}
Here's an update to Chris' solution #2 for Qt 5: 以下是针对Qt 5的Chris解决方案#2的更新:
namespace QtMetaTypePrivate {
template <>
struct QMetaTypeFunctionHelper<ClassA, true> {
static void Delete(void *t)
{
delete static_cast<ClassA*>(t);
}
static void *Create(const void *t)
{
Q_UNUSED(t)
return new ClassA();
}
static void Destruct(void *t)
{
Q_UNUSED(t) // Silence MSVC that warns for POD types.
static_cast<ClassA*>(t)->~ClassA();
}
static void *Construct(void *where, const void *t)
{
Q_UNUSED(t)
return new (where) ClassA;
}
#ifndef QT_NO_DATASTREAM
static void Save(QDataStream &stream, const void *t)
{
stream << *static_cast<const ClassA*>(t);
}
static void Load(QDataStream &stream, void *t)
{
stream >> *static_cast<ClassA*>(t);
}
#endif // QT_NO_DATASTREAM
};
}
If your ClassA doesn't implement operator<< and operator>> helpers for QDataStream, comment out the bodies of Save and Load or you'll still have a compiler error. 如果您的ClassA没有为QDataStream实现operator <<和operator >>帮助器,请注释掉Save和Load的主体,否则您仍然会遇到编译器错误。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.