QVariant conversion doesn't recognize std::string called by my template - c++

I have a function in a class header ("frame.h") that's supposed to convert a QString to a generic type, initializing it to a default value if the QString is empty, shown below.
template <typename T>
static void setStat(T &val, QString &temp)
{
QVariant qv(temp);
if (temp == "")
val = T();
else
val = qv.value<T>();
}
When I call this (which has my only instance of a QVariant) I get the following two errors:
Type is not registered, please use the Q_DECLARE_METATYPE macro to make it
known to Qt's meta-object system (compiling source file item.cpp)
'qt_metatype_id': is not a member of 'QMetaTypeId<T>'
In the file mentioned in the first error ("item.cpp"), I call setStat() once and only once, in the code below, which is in the class constructor.
string temp1 = "";
Frame::setStat(temp1, vec[5]);
desc = temp1;
It's probably worth mentioning that this is in VS2017 with the Qt extension. As I understand it, the error is telling me that std::string is an unrecognized type. Is this normal behavior? How do I fix this?

Even with registration for QVariant, QString doesn't convert freely to QString.
You might add overload instead:
static void setStat(std::string &val, QString &temp)
{
val = temp.toStdString();
}

Related

QColor not a registered metatype?

This is a follow-on from this question.
That article should explain why I am using a quint16 to extract the variant type.
I have derived class MyVariant from QVariant and implemented the QDataStream read operator.
This allows constructs like:
MyVariant vt;
str >> vt;
This is the streaming implementation:
QDataStream& operator>>(QDataStream& str, MyVariant& vt)
{
vt.clear();
quint16 type;
str >> type;
const QMetaType vtype(type);
if (vtype.isValid()) {
vt.create(type, nullptr);
if (!QMetaType::load(str, type, const_cast<void *>(vt.constData()))) {
Q_ASSERT_X(false, "MyVariant", qPrintable(QString("Cannot load type %u").arg(type)));
str.setStatus(QDataStream::ReadCorruptData);
}
}
else {
Q_ASSERT_X(false, "MyVariant", qPrintable(QString("Type %1 is not supported").arg(type)));
}
return str;
}
When the stream comes across a QColor (67), this code fails to create a valid QMetaType for it. QMetaType::isValid() returns false.
What could I possibly have forgotten to do?
Not sure if it matters, but I have added QT += gui to my .pro file.
Edit
I have added...
int type = qRegisterMetaType<QColor>("QColor");
... to my main function.
It returns 67, yet when I hit my streaming function the QMetaType creation still fails.
Okay, I solved this one by pure luck.
In my main, I register the type using...
qRegisterMetaTypeStreamOperators<QColor>("QColor");
And now it works!
Let me know in the comments if I did the right thing or not.

qvariant_cast<std::string> returns empty string

I am using QT 5.7 and MVSC - 2010 (yes, it is old, but I have to due to project)
I have a template function
template<T>
T foo (QString qs)
{
return qvariant_cast<T>(qs);
}
If I make
void main()
{
std::string str = "42"
QString qss = QString::fromStdString(str);
std::string another_str = foo<std::string>(qss );
}
Then another_str will be "";
While toStdString method works perfectly. What is the problem of qvariant_cast?
P.S. I have declared qt meta type, so qvariant_cast is compling, but returning empty string.
According to the manual call qss.canConvert<std::string>() to find out whether a type can be converted. If the value cannot be converted, a default-constructed value will be returned. It is your case, the empty std::string is returned.

How to get a compiler error when expected type changes

I have some code in my app which in simplified form goes:
QVariantMap deviceMap;
deviceMap.insert("Model", pDevice->Type());
QJsonDocument jsonDoc = QJsonDocument::fromVariant(deviceMap);
QString str = jsonDoc.toJson(QJsonDocument::Compact);
I've just found a bug where someone changed the function Type() from:
QString Type() const;
to:
int Type() const;
Obviously Qt was fine with this and just converted it to JSON which caused the bug. But I'd rather get a compiler error when a type is changed like this. How can I change this so I get a compiler error if the return of a function changes in future?
The simplest workaround would be to include a static_assert for the returned value of Type() in the code. But of course, you do not want to do that everywhere.
static_assert( std::is_same<decltype(pDevice->Type()), QString>::value,
"Type mismatch, expected QString" );
deviceMap.insert("Model", pDevice->Type());
You could use free-standing functions:
void insertModel(QVariantMap& deviceMap, const QString& str)
{
deviceMap.insert("Model", str);
}
insertModel(deviceMap, pDevice->Type());
or more generic:
void insertString(QVariantMap& deviceMap, const char* key, const QString& str)
{
deviceMap.insert(key, str);
}
insertString(deviceMap, "Model", pDevice->Type());
In both cases code will work as long as Type() returns QString or anything that can be converted to a QString. You may also static_assert that Type() returns QString.

Object "is not a structure or union" if temporary is passed to constructor

I've not seen this specific oddity of C++ before and it's causing me a bit of confusion.
I have the following class:
class KeyValuesParser
{
public:
explicit KeyValuesParser(const QByteArray &input);
QJsonDocument toJsonDocument(QString* errorString = nullptr);
// ...
};
And I'm trying to use it like so in a Qt unit test:
const char* testData = "...";
KeyValuesParser parser(QByteArray(testData));
QJsonDocument doc = parser.toJsonDocument();
This gives the following compile error:
Member reference base type 'KeyValuesParser(QByteArray)' is not a structure or union.
However, if I create the byte array on the stack and then pass it in, instead of passing in a temporary, everything compiles fine:
const char* testData = "...";
QByteArray testByteArray(testData)
KeyValuesParser parser(testByteArray);
QJsonDocument doc = parser.toJsonDocument();
I thought this might have been some weird black magic that required the explicit keyword (which is why I added it), but that didn't make any difference. Can anyone explain what's going on here?
EDIT: I've been referred to another question as being a duplicate and I think it almost is, but there's some most-vexing-parse confusion on this question that I think merits the extra discussion.
NB. This is addition to Jonas' answer near me.
What are you trying to do is to bind rvalue to lvalue reference, this is not allowed in C++ standard. In your second example - you correctly pass lvalue via reference, that's why it's working.
Binding rvalue to lvalue reference is Visual C++ extension, g++ cannot do it at all, clang can do it with -fms-extensions
The line
KeyValuesParser parser(QByteArray(testData));
is not an object declaration. It’s a function declaration of a function returning a KeyValuesParser and taking a QByteArray argument.
This is called Most Vexing Parse (link goes to wikipedia, you’ll find plenty on StackOverflow too). To keep it short, when in doubt, the C++ standard prefers a function declaration, because otherwise it would be hard to declare functions at all.
As mentioned, this is a "most vexing parse" problem. c++11 brought us two solutions: auto and brace initialisation:
// use auto to turn the expression unambiguously into an rvalue
// without having to mention the class name twice
int main()
{
const char* testData = "...";
auto parser = KeyValuesParser(QByteArray(testData));
auto doc = QJsonDocument(parser.toJsonDocument());
}
// or use brace initialisation to avoid the parse ambiguity
//
int main2()
{
const char* testData = "...";
KeyValuesParser parser{ QByteArray(testData) };
QJsonDocument doc{ parser.toJsonDocument() };
}
// another solution:
const char* testData = "...";
auto doc = QJsonDocument(KeyValuesParser(QByteArray(testData)).toJsonDocument());
// yet another
const char* testData = "...";
auto doc = QJsonDocument { KeyValuesParser { QByteArray(testData) }.toJsonDocument() };

How to invoke QMetaMethod with an array of QStrings?

QMetaObject system provides some basic reflection API. What I want to do is to call given slot or method that's available through this system.
However, the arguments and names of method are initially all QString values. The documentation shows an example:
QString retVal;
QByteArray normalizedSignature = QMetaObject::normalizedSignature("compute(QString, int, double)");
int methodIndex = obj->metaObject()->indexOfMethod(normalizedSignature);
QMetaMethod method = obj->metaObject()->method(methodIndex);
method.invoke(obj,
Qt::DirectConnection,
Q_RETURN_ARG(QString, retVal),
Q_ARG(QString, "sqrt"),
Q_ARG(int, 42),
Q_ARG(double, 9.7));
If the "compute" slot does not take exactly one QString, one int and
one double in the specified order, the call will fail.
Emphasis mine. Now with this example, I'm pretty clueless. It's clearly using templates, which are useless in dynamic context. I don't really see a purpose in using this API statically, when I could just as well call the method directly.
So let's say I have this function:
void invokeSlot(QObject* target, const QString& slotName, const QStringList& arguments) {
// This is how I can get list of overloads by given name
const QMetaObject* obj = target_->metaObject();
QList<QMetaMethod> overloads;
for (int i = 0, l = obj->methodCount(); i < l; ++i) {
const QMetaMethod method = obj->method(i);
if( method.name() == name )
overloads.append(method);
}
// Now how to convert the arguments?
}
You can assume that I have prepared this method for type conversion:
QVariant convertType(const QString& source, const QVariant::Type requiredType);
But how to pass the arguments even if I chose correct overload and convert strings to QVariants of required type?
You can prepare this function:
QGenericArgument convertType(const QString& source, const QVariant::Type requiredType);
By using switches and Q_ARG inside it.
Then a big switch on the argument count with invokes in it.