c++ q_property - QObject에서 파생 된 클래스에서 qRegisterMetaType을 올바르게 사용하는 방법?



in qt (4)

나는 이것에 대한 답변을 찾기 위해 광범위하게 검색했지만 아무 소용이 없습니다. 나의 애도는 다음과 같습니다.

나는 대략 다음과 같은 ClassA 를 가지고있다.

class ClassA : public QObject {
    Q_OBJECT
public:
    ClassA() { mName = "lol"; }
    ~ClassA();
    void ShowName() { std::cout << mName << std::endl; }
    std::string mName;
};

물론 moc을 사용하기 때문에이 클래스는 실제로 프로젝트에서 cpp와 hpp로 나뉘지만 여기서는 문제가 아닙니다.

Q_DECLARE_METATYPE 은 실제로 그 기능 (QVariant 확장)이 필요하지 않으므로 사용하지 마십시오. 런타임 인스턴스화에만 관심이 있습니다.

여기서 문제는 Q_OBJECT 가 복사 및 할당 생성자를 금지한다는 것입니다. 그 때문에, 나는 qRegisterMetaTypeClassA 자체가 아니라 ClassA* 에 적용해야합니다.이 qRegisterMetaType 는 처음에는 잘 작동하는 것으로 보입니다.

이제 런타임에서이 클래스를 문자열에서 동적으로 만들고 ShowName() 메서드를 실행하려고합니다. 나는 이렇게하고있다.

int main() {
    qRegisterMetaType<ClassA*>("ClassA*");

    int id = QMetaType::type("ClassA*");
    std::cout << "meta id: " << id << std::endl; // Outputs correct generated user id (not 0)

    ClassA* myclass = static_cast<ClassA*>(QMetaType::construct(id));
    myclass->ShowName(); // Segfaults, oh dear

    return 0;
}

자, 내 문제가 있습니다. 실제로 올바르게 구성된 객체가없는 것 같습니다.

클래스를 다음과 같이 변경하면 :

class ClassA : public QObject {
    Q_OBJECT
public:
    ClassA() { mName = "lol"; }
    ClassA(const ClassA& other) { assert(false && "DONT EVER USE THIS"); }
    ~ClassA();
    void ShowName() { std::cout << mName << std::endl; }
    std::string mName;
};

그러면 우리는 다음과 같이 프로그램을 변경할 수 있습니다.

int main() {
    qRegisterMetaType<ClassA>("ClassA");

    int id = QMetaType::type("ClassA");
    std::cout << "meta id: " << id << std::endl; // Outputs correct generated user id (not 0)

    ClassA* myclass = static_cast<ClassA*>(QMetaType::construct(id));
    myclass->ShowName(); // "lol", yay

    return 0;
}

당연히 필자는 위조 된 복사본 생성자를 사용할 수는 있지만 올바른 느낌이 들지 않으며 Qt는 그 점에 대해 제안하고 대신 QObjects에 대한 포인터 사용만을 제안합니다.

누구가 여기에서 틀린 것을 보는지? 또한, 나는 비슷한 문제가 있음을 알고 있지만, 아무도이 정확한 문제를 다루지 않습니다.


Answers

몇 가지:

  • ClassA * 등록이 작동하지 않는 이유는 construct () 호출이 실제 객체가 아닌 ClassA 객체에 대한 포인터를 구성하기 때문입니다.

  • QMetaType 문서에서 다음 인용문에 주목할 가치가 있습니다.

공용 기본 생성자, 공용 복사본 생성자 및 공용 소멸자가있는 모든 클래스 또는 구조체를 등록 할 수 있습니다.

  • qMetaTypeConstructHelper의 Qt 구현을 살펴보십시오.

    template <typename T>
    void *qMetaTypeConstructHelper(const T *t)
    {
        if (!t)
            return new T();
        return new T(*static_cast<const T*>(t));
    }
    

복사 생성자의 사용법을 기록하십시오. 이 경우 문제를 해결하는 데 두 가지 방법이 있습니다.

1) 복사 생성자를 제공합니다 (이미 완료했습니다).

2) 복사 생성자를 사용하지 않는 qMetaTypeConstructHelper의 특수화를 제공하십시오.

template <>
void *qMetaTypeConstructHelper<ClassA>(const ClassA *)
{
    return new ClassA();
}

이름으로 QObject 클래스의 인스턴스를 만들려면 QMetaObject 대신 QMetaObject 를 사용할 수 있습니다.

먼저 생성자를 invokable로 선언해야합니다.

class ClassA : public QObject {
    Q_OBJECT
public:
    Q_INVOKABLE ClassA() { mName = "lol"; }
    ~ClassA();
    void showName() { std::cout << mName << std::endl; }
    std::string mName;
};

그런 다음 인스턴스화 할 클래스에 대한 자체 등록 시스템을 작성하고 수동으로 채 웁니다.

int main(int argc, char *argv[])
{    
    // Register your QObject derived classes
    QList<const QMetaObject*> metaObjectList;
    metaObjectList << &ClassA::staticMetaObject;

    // Index the classes/metaobject by their names
    QMap<QString, const QMetaObject*> metaObjectLookup;
    foreach(const QMetaObject *mo, metaObjectList) {
        metaObjectLookup.insert(mo->className(), mo);
    }

마지막으로 등록 된 클래스의 이름을 인스턴스화 할 수 있습니다.

    const QMetaObject * myMetaObject = metaObjectLookup.value("ClassA", 0);
    if(!myMetaObject)
    {
        // The class doesn't exist
        return 1;
    }

    ClassA *myObject =
            static_cast<ClassA*>(myMetaObject->newInstance());
    if(!myObject)
    {
        // Couldn't create an instance (constructor not declared Q_INVOKABLE ?)
        return 1;
    }
    myObject->showName();

    return 0;
}

다음은 Qt 5의 Chris '솔루션 # 2에 대한 업데이트입니다.

namespace QtMetaTypePrivate {
    template <>
    struct QMetaTypeFunctionHelper<ClassA, true> {
        static void Delete(void *t)
        {
            delete static_cast<ClassA*>(t);
        }

        static void *Create(const void *t)
        {
            Q_UNUSED(t)
            return new ClassA();
        }

        static void Destruct(void *t)
        {
            Q_UNUSED(t) // Silence MSVC that warns for POD types.
            static_cast<ClassA*>(t)->~ClassA();
        }

        static void *Construct(void *where, const void *t)
        {
            Q_UNUSED(t)
            return new (where) ClassA;
        }
    #ifndef QT_NO_DATASTREAM
        static void Save(QDataStream &stream, const void *t)
        {
            stream << *static_cast<const ClassA*>(t);
        }

        static void Load(QDataStream &stream, void *t)
        {
            stream >> *static_cast<ClassA*>(t);
        }
    #endif // QT_NO_DATASTREAM
    };
}

ClassA가 QDataStream에 대해 연산자 << 및 연산자 >>를 지원하지 않으면 저장 및로드 본문을 주석 처리하십시오. 그렇지 않으면 컴파일러 오류가 발생합니다.


아파치에서 FieldUtils 사용해 FieldUtils commons-lang3 :

FieldUtils.readField(object, fieldName, true);




c++ qt reflection