[Interest] Creating a QVariant of QList<QObject*> knowing only the class name

Ch'Gans chgans at gna.org
Wed Feb 22 11:40:37 CET 2017


On 22 February 2017 at 20:58, Thiago Macieira <thiago.macieira at intel.com> wrote:
> On terça-feira, 21 de fevereiro de 2017 18:20:50 PST Ch'Gans wrote:
>> Hi there,
>>
>> I can create a valid QVariant that contains a MyObject pointer with
>> just the class name (ie, QString("MyObject")) and no access to the
>> class declaration. MyObject is derived from QObject.
>>
>> This QVariant has the following properties:
>> - type() == QVariant::UserType
>> - userType() == QMetaType::type("MyObject*")
>
> This variant will have no content. Its isNull() method will return true.
>
> You can call the QVariant constructor that takes a void* object to the data,
> so it will copy when creating the object. This is easy because "MyObject*" is,
> like its name shows, a pointer. We all know how to copy pointers.

I forgot to say that in the above case, the QVariant was created with
a pointer to MyObject*, which in turn pointed to a valid MyObject.

>
>> The consumer of this variant, which has access to the original class
>> declaration, can obtain a valid pointer with
>> variant.value<MyObject*>()
>
> Correct.
>
>> Now I need to create a QVariant that contains a QList<MyObject*> with
>> just the class name (ie, QString("MyObject")) and no access to the
>> class declaration.
>>
>> Again the resulting variant needs to have the following properties:
>> - type() == QVariant::UserType
>> - userType() == QMetaType::type("QList<MyObject*>")
>
> Ok, same thing, same solution.
>
>> My question is how do I create and "populate" such a variant without
>> using template involving MyObject*?
>
> Forget templates. They don't exist at the level of QVariant. Only concrete
> types do.
>
> For the sake of the argument, let's call it "MyObjectList". It's not a pointer
> and it's not a built-in type.
>
>> QSequentialIterable allows one to iterate on such a variant, but only
>> provide a const access, so it is useless in my case.
>>
>> I seem to have found a solution, but i'm not sure it is correct and safe:
>> QList<QObject*> list;
>> // populate the list with MyObject* items
>> int desiredMetaTypeId = QMetaType::type("QList<MyObject*>")
>> return QVariant(desiredMetaTypeId, &list);
>
> It's not correct. Whether it's safe or not, I won't answer, because you
> shouldn't be doing it in the first place. Qt has a few hacks that cast a QList
> of one type to a QList of another type (a concept called "covariance"), but
> this is a very ugly hack, should never be done like that, and only works in Qt
> because Qt itself controls QList and we can know whether the structure layout
> is compatible. But from the strict C++ point of view, it's invalid and a
> future compiler could just break that code.
>
> So, don't write it.
>
> If you're going to call the QVariant constructor with a pointer, you MUST pass
> a pointer to the correct, concrete object. Forget that it was a template: just

Well that's my problem, i cannot instanciate and assign the instance
to a variable of the concrete type since i don't have access to the
class declaration, not even to a forward declaration.

> think of it as "MyObjectList" that has absolutely no relationship with
> QObjectList.

I understand that, i got caught when i tried to handle QVariant
holding a QStringList as a QVariant holding a QList<QString>...

>
>> Does anyone know a "proper" solution for this? Or is my approach
>> correct and safe?
>
> Your approach is to redesign and think the problem from another angle.

Well, what is the point of Qt meta-type/object then? Is it an abuse to
do introspection and dynamic creation? Doesn't Qt (to some extent)
brings to C++ what Java and C# provides natively?

'MyObject*' is registered to the QMetaType system, 'MyObject' class is
'registered' to the QMetaObject system. Qt can do "magic" stuff (C++
<-> QML & JS), but i cannot do the same myself?

My problem is that i want to create a tree of QObject-based objects
out of an XML file, and the only headers i have access to are the Qt
ones, that is: QObject, QVariant, the QMetaType familly and the Qt
container familly.

I can already create non-container of QObject-based objects "out of
thin air", if these QObject-based classes have a Q_INVOKABLE default
constructor. Althought, I will likely remove this Q_INVOKABLE
requirement and instead require to be provided with a factory that can
create QObject's given a class name. But it won't solve the container
problem...

Well unless the factory creates these "container QVariant" and as well
provide the "populate" helpers that go with it, eg:

const QString typeName = "QList<MyObject*>"
bool isList = factory->isList(typeName)
if (isList)
{
  QVariant variantContainer = factory->create(typeName);
  for (...)
  {
    QObject *item = factory->createItemForContainer(typeName);
    [...] // set item's properties
    factory.addItemToContainer(item, variantContainer);
  }
}

This might work, but i'm trying to find a solution that wouldn't
require to write lot of boiler-plate code, b/c with the above solution
one will have to write boiler-plate code (create object, create list
of objects, create map of objects, create set of objects, create hash
of objects...) for every single object type...

Tanks for all your inputs, but I'm still puzzled, I can't believe this
is not achievable in a generic way...

Chris

>
> --
> Thiago Macieira - thiago.macieira (AT) intel.com
>   Software Architect - Intel Open Source Technology Center
>
> _______________________________________________
> Interest mailing list
> Interest at qt-project.org
> http://lists.qt-project.org/mailman/listinfo/interest



More information about the Interest mailing list