Unsolved QMetaType::convert failed
-
Source code:
... ids[j] = QMetaType::type(parameterTypes[j]); // parameterTypes[j] is a enum of QMetaType if (ids[j] != QMetaType::UnknownType) { types[j] = QMetaType::create(ids[j]); QJsonValue value = model.value(parameterNames[j]); // model is a QJsonObject if (!QMetaType::convert(&value, QMetaType::QVariant, types[j], ids[j])) { qDebug() << "convert to type: " << ids[j] << " failed"; } ... }
After the failed of QMetaType::convert, I tested the functions below:
qDebug() << QMetaType::hasRegisteredConverterFunction(QMetaType::QJsonValue, QMetaType::QString); qDebug() << QMetaType::hasRegisteredConverterFunction(QMetaType::QVariant, QMetaType::QVariant); qDebug() << QMetaType::hasRegisteredConverterFunction(QMetaType::Int, QMetaType::Int);
All results were false.
So, why did these happened? I tested them in Qt 5.9 and Qt 5.7.
-
Sorry, I made a mistake. The 7th line in the first code block should be
if (!QMetaType::convert(&value, QMetaType::QJsonValue, types[j], ids[j]))
-
@wxfred said in QMetaType::convert failed:
So, why did these happened?
Probably there aren't any conversions between those types. What are you trying to do? Give us some context here.
-
@kshegunov Ok. What I suppose to accomplish is something like model binding within actions of .net mvc. The basic procedure is:
firstly, extracting parameters in url to a QJsonObject;
secondly, using QMetaMethod::parameterTypes() and QMetaMethod::parameterNames() to get the info of parameters of my action function;
thanks to Qt's reflection, finally, matching the parameters in QJsonObject to my action function's parameters.In the last step, for example, if I extract a QJsonObject from url like this:
model = { username: "wxfred", password: "123456" }
and my action function is something like this:
Q_INVOKABLE QJsonObject login(const QString &username, const QString &password) { ... }
then, I will use the code below to pair the parameters and invoke my action function:
int count = metaMethod.parameterCount(); // get the total num of my action function's parameters QList<QByteArray> parameterTypes = metaMethod.parameterTypes(); // parameterTypes[0] will be "QString", parameterTypes[1] will also be "QString" QList<QByteArray> parameterNames = metaMethod.parameterNames(); // [0] is "username", [1] is "password" QVector<int> ids; // the meta type id of each parameter QVector<void*> instances; // the instance of each parameter for (int j = 0; j < count; ++j) { ids[j] = QMetaType::type(parameterTypes[j]); // get meta type id, for "QString" the id is QMetaType::QString instances[j] = QMetaType::create(ids[j]); // dynamically create instance by id QJsonValue value = model.value(parameterNames[j]); // get contents of parameters in model, in my example, the first parameter's name in action function is "username", and "username" is the key of "wxfred" in model, finally, the value will be a QJsonValue(QString("wxfred")) if (!QMetaType::convert(&value, QMetaType::QJsonValue, instances[j], ids[j])) // convert, this step is always failed { qDebug() << "convert to type: " << ids[j] << " failed"; }
If QMetaType::convert is function well as I expect, I can invoke my action function with these instances of parameters by QMetaObject.invokeMethod. But it dose not.
What is even more frustrating is that, although Qt supports QMetaType::registerConverter(MemberFunction function) to register converter function, you can't register the converter of two basic meta types. For example,int method(QJsonValue value) { ... } QMetaType::registerConverter<QJsonValue, int>(&method);
build these code will get error:
D:\Qt\Qt5.9.0\5.9\msvc2015_64\include\QtCore\qmetatype.h:636: error: C2338: QMetaType::registerConverter: At least one of the types must be a custom type.
That's weird, because if Qt dose not implement the converter between basic meta types, why dose it fobidden user to implement them?
-
I don't have an answer for you currently, sorry. I'll need to check Qt's sources, but I'm quite busy now, I hope someone will pitch in and add some suggestion. I assume that Qt does the conversion internally, so that's why there are no registered converters for its types.
-
@kshegunov Thanks for your reminding. I checked the source code of the QMetaType, and found this:
bool QMetaType::convert(const void *from, int fromTypeId, void *to, int toTypeId) { const QtPrivate::AbstractConverterFunction * const f = customTypesConversionRegistry()->function(qMakePair(fromTypeId, toTypeId)); return f && f->convert(f, from, to); } bool QMetaType::registerConverterFunction(const QtPrivate::AbstractConverterFunction *f, int from, int to) { if (!customTypesConversionRegistry()->insertIfNotContains(qMakePair(from, to), f)) { qWarning("Type conversion already registered from type %s to type %s", QMetaType::typeName(from), QMetaType::typeName(to)); return false; } return true; } bool QMetaType::hasRegisteredConverterFunction(int fromTypeId, int toTypeId) { return customTypesConversionRegistry()->contains(qMakePair(fromTypeId, toTypeId)); }
All these 3 methods use the same function named customTypesConversionRegistry. Literally, these methods only consider about the converters of user's custom types, but in Qt Assistant, their descriptions say nothing about "custom" things, how annoying!
I still can't find an API to convert a object of QJsonValue or QVariant to a preallocated space whose type is determined by a enum of QMetaType::Type. But, in the source code, Qt uses "switch/case" to handle the stream operations of meta types, so I borrowed it and made some change. Now it works fine, maybe.Qt's load method:
bool QMetaType::load(QDataStream &stream, int type, void *data) { if (!data || !isRegistered(type)) return false; switch(type) { ... case QMetaType::Long: { qlonglong l; stream >> l; *static_cast<long *>(data) = long(l); break; } case QMetaType::Int: stream >> *static_cast<int *>(data); break; ... }
my converter:
bool convert(const QVariant &variant, int type, void *data) { if (!data || !QMetaType::isRegistered(type)) return false; bool ok = false; switch (type) { ... case QMetaType::Long: *static_cast<long *>(data) = (long)variant.toLongLong(&ok); break; case QMetaType::Int: *static_cast<int *>(data) = variant.toInt(&ok); break; ... }