2

我正在尝试将 Lua 与 Qt 的 QMetaObject 系统集成。我有一个派生自该类的类,QObject我基于类名使用QObject::staticMetaObject.

主.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

主文件

#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 将为 userdata 分配内存,但不会构造它所代表的对象。所有文档都说要使用placement new 在ptrlua 用户数据中构造您的对象,但是QMetaObject不允许开箱即用的placement new。

我已经包含了来自 ixSci 的关于使用QMetaTypeptr. 然而,typeId总是以未知的方式回来。

4

3 回答 3

3

看起来你需要的东西在QMetaType课堂上是可用的。

所以要得到你想要的东西,你需要这样的东西(未经测试!):

int typeId = QMetaType::type(metaObject->className());
if (typeId != QMetaType::UnknownType)
{
    QMetaType meta(typeId);
    meta.construct(ptr, objectToCopy);
}
于 2018-08-30T09:01:04.493 回答
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)

一些值.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;
};

一些值.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);
}

主文件

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;
}
于 2018-08-30T10:42:14.757 回答
0

我找到了适合我情况的解决方案。

在查看了 Moia 和 ixSci 的答案后,我意识到我的声明是正确的,即不能在 a 上使用placement new,QObject因为QObject它的复制构造函数是私有的(不应该公开)。

一种更有效的方法是(显然)存储指向QObject*created from的指针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);

并用于检索:

uintptr_t *objectPointer = (uintptr_t*)lua_touserdata(L, -1);
QObject *object = static_cast<QObject*>((void*)*objectPointer);

好处是lua可以为任何类对象分配固定大小,因为它总是4(只是一个指针)。这意味着我不必进行任何类型检查。

明显的缺点是我不能做任何类型检查,因为它总是只是指针。此外,在 Lua 脚本中与这些类型的所有交互都将表现为指针。所有副本都将是指针副本而不是QObject副本。因此,我将不得不QObject's根据我的具体用例为我实现自己的复制构造函数。

感谢您的所有帮助!

于 2018-08-30T12:08:34.457 回答