Новое размещение lua_newuserdata в QMetaObject - PullRequest
0 голосов
/ 30 августа 2018

Я пытаюсь интегрировать Lua с системой Qt QMetaObject. У меня есть класс, производный от QObject, который я связываю с Lua на основе имени класса, используя 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 будет выделять память для пользовательских данных, но не будет создавать объект, который он представляет. Во всей документации говорится, что для создания вашего объекта в ptr пользовательских данных lua нужно использовать новое размещение, однако QMetaObject не позволяет размещать новое из коробки.

Я включил предложения ixSci по использованию QMetaType для создания объекта на ptr. Однако typeId всегда возвращается как неизвестное.

Ответы [ 3 ]

0 голосов
/ 30 августа 2018

Ваш 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;
}
0 голосов
/ 30 августа 2018

Я нашел решение для моей ситуации.

Изучив ответы от Moia и ixSci, я понял, что я был прав в своем утверждении, что размещение нового нельзя использовать на QObject, потому что 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);

И для получения:

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

Положительным моментом является то, что lua может выделить фиксированный размер для любого объекта класса, поскольку он всегда равен 4 (только указатель). Это означает, что мне не нужно выполнять какую-либо проверку типов.

Очевидным недостатком этого является то, что я не могу выполнять какую-либо проверку типов, поскольку она всегда будет просто указателями. Кроме того, все взаимодействия с этими типами внутри скрипта Lua будут вести себя как указатели. Все копии будут копиями указателя вместо QObject копий. В результате мне придется реализовать свой собственный конструктор копирования для моего QObject's в зависимости от конкретного случая использования.

Спасибо за вашу помощь!

0 голосов
/ 30 августа 2018

Похоже, что вам нужно, доступно в QMetaType классе.

Итак, чтобы получить то, что вы просите, вам нужно что-то вроде этого (не проверено!):

int typeId = QMetaType::type(metaObject->className());
if (typeId != QMetaType::UnknownType)
{
    QMetaType meta(typeId);
    meta.construct(ptr, objectToCopy);
}
...