我正在尝试将Lua与Qt的QMetaObject系统集成。我有一个从QObject
派生的类,我根据类名使用QObject::staticMetaObject
绑定到Lua。
main.h:
#ifndef MAIN_H
#define MAIN_H
class Test : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE Test(QObject *parent = 0) : QObject(parent){}
~Test(){}
};
Q_DECLARE_METATYPE(Test*)
#endif
main.cpp
#include <QCoreApplication>
#include <QDebug>
#include "main.h"
#include "lua_src/lua.hpp" //Lua include
int CreateUserData(lua_State *L)
{
const QMetaObject *metaObject = (const QMetaObject*)lua_touserdata(L, lua_upvalueindex(1));
//PROBLEM AREA
int typeId = QMetaType::type(metaObject->className());
if(typeId != QMetaType::UnknownType)//typeId is always unknown
{
QMetaType meta(typeId);
void *ptr = lua_newuserdata(L, meta.sizeOf());
meta.construct(ptr);
}
//PROBLEM AREA
lua_newtable(L);
lua_setuservalue(L, 1);
return 1;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QString luaScript("local test = Test.new()");
lua_State *L = luaL_newstate();
//bind Test class to lua
lua_newtable(L);
lua_pushvalue(L, -1);
lua_setglobal(L, "Test");
lua_pushvalue(L, -1);
lua_pushlightuserdata(L, (void*)&Test::staticMetaObject);
lua_pushcclosure(L, CreateUserData, 1);
lua_setfield(L, -2, "new");
//start script
luaL_dostring(L, luaScript.toStdString().c_str());
lua_close(L);
}
问题是lua会为用户数据分配内存,但不会构造它代表的对象。所有文档都说要使用lua用户数据的ptr
处的new place来构造您的对象,但是QMetaObject
不允许开箱即用地放置new。
我已经包括了ixSci关于使用QMetaType
在ptr
处构造对象的建议。但是,typeId
总是以未知形式返回。
答案 0 :(得分:3)
QMetaType
类中提供了您所需的内容。
因此,要获得您的要求,您需要类似以下内容(未经测试!):
int typeId = QMetaType::type(metaObject->className());
if (typeId != QMetaType::UnknownType)
{
QMetaType meta(typeId);
meta.construct(ptr, objectToCopy);
}
答案 1 :(得分:1)
您的Test
班错过
Q_DECLARE_METATYPE(Test*)
和一个
qRegisterMetaType<Test*>("Test");
具有在Qt元系统中正确注册的类型。
请注意声明的指针。您需要声明一个指针,因为QObject
已禁用复制构造函数。
比您可以正确拨打的电话:
Test* test = new Test();
auto name = test.metaObject()->className();
auto type = QMetaType::type(name);
Test* instance = static_cast<Test*>(QMetaType::construct(type));
编辑:一个完整的可行实现(实际上添加了qMetaTypeConstructHelper)
somevalue.h
#include <QObject>
#include <QMetaType>
class SomeValue : public QObject
{
Q_OBJECT
Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)
public:
explicit Q_INVOKABLE SomeValue(QObject* parent = nullptr);
~SomeValue() override = default;
int value() const;
signals:
void valueChanged(int value);
public slots:
void setValue(int value);
private:
int _value;
};
somevalue.cpp
#include "somevalue.h"
Q_DECLARE_METATYPE(SomeValue*)
template <>
void* qMetaTypeConstructHelper<SomeValue>(const SomeValue*)
{
return new SomeValue();
}
static struct SomeValueMetaId
{
SomeValueMetaId()
{
qRegisterMetaType<SomeValue>("SomeValue");
}
} _SomeValueMetaId;
SomeValue::SomeValue(QObject* parent)
: QObject(parent),
_value{100}
{
}
int SomeValue::value() const
{
return _value;
}
void SomeValue::setValue(int value)
{
if (_value == value)
return;
_value = value;
emit valueChanged(_value);
}
main.cpp
int main()
{
SomeValue pint;
auto pintName = pint.metaObject()->className();
auto pintType = QMetaType::type("SomeValue");
qDebug() << pintName << pintType << QMetaType::typeName(pintType);
qDebug() << QMetaType::isRegistered(QMetaType::type("SomeValue*"));
auto otherObj = static_cast<SomeValue*>(QMetaType::construct(pintType));
qDebug() << pint.value();
qDebug() << otherObj->value();
qDebug() << "new classname" << otherObj->metaObject()->className();
qDebug() << otherObj->metaObject()->propertyCount();
int valueId = pint.metaObject()->indexOfProperty("value");
auto minname = pint.metaObject()->property(valueId).name();
qDebug() << "value name" << minname;
auto minvariant = pint.property(minname);
qDebug() << "value type name" << minvariant << minvariant.typeName();
qDebug() << QMetaType::type(minvariant.typeName());
return 0;
}
答案 2 :(得分:0)
我为我的情况找到了解决方案。
查看了Moia和ixSci的答案后,我意识到我的声明是正确的,因为QObject
的复制构造函数为private(因此不应该在公开)。
一种更有效的方法是(显然)存储指向从QObject
创建的QObject*
的指针。没错,指向指针的指针。
新代码如下:
metaObject->newInstance()
对于检索:
const QMetaObject *metaObject = (const QMetaObject*)lua_touserdata(L, lua_upvalueindex(1));
uintptr_t *ptr = (uintptr_t*)lua_newuserdata(L, sizeof(QObject*));
QObject *object = metaObject->newInstance();
*ptr = reinterpret_cast<uintptr_t>(object);
好处是lua可以为任何类对象分配固定大小,因为它始终为4(只是一个指针)。这意味着我不必进行任何类型检查。
明显的缺点是我无法进行任何类型检查,因为它始终只是指针。同样,在Lua脚本中与这些类型的所有交互都将充当指针。所有副本将是指针副本,而不是uintptr_t *objectPointer = (uintptr_t*)lua_touserdata(L, -1);
QObject *object = static_cast<QObject*>((void*)*objectPointer);
副本。结果,我将不得不根据自己的特定用例为QObject
实现自己的副本构造函数。
感谢您的协助!