lua_newuserdata放置在QMetaObject上的新功能

时间:2018-08-30 07:46:31

标签: c++ qt qmetaobject

我正在尝试将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关于使用QMetaTypeptr处构造对象的建议。但是,typeId总是以未知形式返回。

3 个答案:

答案 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实现自己的副本构造函数。

感谢您的协助!