lua_newuserdata QMetaObject 上的新位置
lua_newuserdata placement new on QMetaObject
我正在尝试将 Lua 与 Qt 的 QMetaObject 系统集成。我有一个 class 派生自 QObject
,我使用 QObject::staticMetaObject
.
基于 class 名称绑定到 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 会为用户数据分配内存,但不会构造它所代表的对象。所有文档都说使用 placement new 在 lua 用户数据的 ptr
处构建对象,但是 QMetaObject
不允许开箱即用的 placement new。
我已经包含了 ixSci 关于使用 QMetaType
在 ptr
处构建对象的建议。但是,typeId
总是返回未知。
看起来您需要的东西在 QMetaType
class 中可用。
所以要得到你想要的东西,你需要这样的东西(未经测试!):
int typeId = QMetaType::type(metaObject->className());
if (typeId != QMetaType::UnknownType)
{
QMetaType meta(typeId);
meta.construct(ptr, objectToCopy);
}
你的 Test
class 错过了一个
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;
}
我找到了适合我的情况的解决方案。
在查看了 Moia 和 ixSci 的答案后,我意识到我的陈述是正确的,即 placement new 不能用于 QObject
,因为 QObject
的复制构造函数是私有的(并且不应该 public).
一种更有效的方法是(显然)存储指向从 metaObject->newInstance()
创建的 QObject*
的指针。没错,指针指向指针。
新代码如下:
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);
并用于检索:
uintptr_t *objectPointer = (uintptr_t*)lua_touserdata(L, -1);
QObject *object = static_cast<QObject*>((void*)*objectPointer);
优点是 lua 可以为任何 class 对象分配固定大小,因为它总是 4(只是一个指针)。这意味着我不必进行任何类型检查。
这样做的明显缺点是我无法进行任何类型检查,因为它始终只是指针。此外,在 Lua 脚本中与这些类型的所有交互都将表现为指针。所有副本都将是指针副本,而不是 QObject
副本。因此,我将不得不根据我的具体用例为我的 QObject's
实现我自己的复制构造函数。
感谢您的帮助!
我正在尝试将 Lua 与 Qt 的 QMetaObject 系统集成。我有一个 class 派生自 QObject
,我使用 QObject::staticMetaObject
.
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 会为用户数据分配内存,但不会构造它所代表的对象。所有文档都说使用 placement new 在 lua 用户数据的 ptr
处构建对象,但是 QMetaObject
不允许开箱即用的 placement new。
我已经包含了 ixSci 关于使用 QMetaType
在 ptr
处构建对象的建议。但是,typeId
总是返回未知。
看起来您需要的东西在 QMetaType
class 中可用。
所以要得到你想要的东西,你需要这样的东西(未经测试!):
int typeId = QMetaType::type(metaObject->className());
if (typeId != QMetaType::UnknownType)
{
QMetaType meta(typeId);
meta.construct(ptr, objectToCopy);
}
你的 Test
class 错过了一个
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;
}
我找到了适合我的情况的解决方案。
在查看了 Moia 和 ixSci 的答案后,我意识到我的陈述是正确的,即 placement new 不能用于 QObject
,因为 QObject
的复制构造函数是私有的(并且不应该 public).
一种更有效的方法是(显然)存储指向从 metaObject->newInstance()
创建的 QObject*
的指针。没错,指针指向指针。
新代码如下:
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);
并用于检索:
uintptr_t *objectPointer = (uintptr_t*)lua_touserdata(L, -1);
QObject *object = static_cast<QObject*>((void*)*objectPointer);
优点是 lua 可以为任何 class 对象分配固定大小,因为它总是 4(只是一个指针)。这意味着我不必进行任何类型检查。
这样做的明显缺点是我无法进行任何类型检查,因为它始终只是指针。此外,在 Lua 脚本中与这些类型的所有交互都将表现为指针。所有副本都将是指针副本,而不是 QObject
副本。因此,我将不得不根据我的具体用例为我的 QObject's
实现我自己的复制构造函数。
感谢您的帮助!