Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom type in QVariant converts to empty string

I'm writing a lexical scanner that generates a stream of tokens from some input. Those tokens have a type and a value. Since I'm using Qt I chose to store the token data as a QVariant. This works pretty well for token data that is of a non-custom type.

Unfortunately, I have several custom types that are stored inside of tokens as well. The tokens have a toString() function that outputs a token description (for debugging), but for all tokens that have data of a custom type this function gives an empty string. The code goes like this:

Test.h:

struct Test
{
    QString value_;

    Test(const QString& value = "");
    QString toString();
};

Q_DECLARE_METATYPE(Test)

Token.h:

struct Token
{
    TokenType type_;
    QVariant value_;
...
    virtual QString toString() const;
};

Token.cpp:

QString Token::toString() const
{
    QStringList sl;
    sl << "Token(" << ::toString(type_) << ", ";
    sl << value_.toString() << ")";
    return sl.join("");
}

Example output from scanner:

"Token(TT_TEST, )" 
"Token(TT_PLUS, +)" 
"Token(TT_NUMBER, 5)" 
"Token(TT_end, #)" 

The TT_TEST token contains a Test class and I would expect the variant to print it's value. Unfortunately this does not work, and I've tried a lot of solutions that did not work. My current workaround looks like this:

template <typename T>
bool writeToStringList(QStringList& sl, QVariant v)
{
    if (!v.canConvert<T>()) return false;
    sl << v.value<T>().toString();
    return true;
}

and a modified toString() function:

sl << "Token(";
sl << ::toString(type_) << ", ";
if (!writeToStringList<Test>(sl, value_)) {
    sl << value_.toString();
}

and I have to do this for all my custom types which just feels pretty clumsy and wrong.

I figure there must be a better solution to this problem. Can anyone of you:

  • Tell me how to solve the problem with the QVariant in a better way, or
  • suggest a totally different solution without a QVariant. (I had a template solution earlier but I ran into different problems there, so I would need an example if that is suggested).

?

like image 243
hochl Avatar asked Mar 20 '23 10:03

hochl


2 Answers

Q_DECLARE_METATYPE() is in fact sufficient to enable aggregation of a custom type in a QVariant. This does not cover aspects like implicit type conversion and comparison in context of the QVariant though. Qt5 assumed, to facilitate implicit conversion to QString you may do the following:

#include <QMetaType>

struct Token {
    QString _value;
};

Q_DECLARE_METATYPE( Token* );

QString tokenToString( Token* t ) {
   return t->_value );
}

int main(int argc, char* argv[]) {
    QMetaType::registerConverter<Token*,QString>( tokenToString );

    Token t = { QString("hello") };
    QVariant value;
    value.setValue( &t );
    std::cout << value << std::endl;
}

This is of course also possible (and more save) with Q_DECLARE_METATYPE( MyType ) and directly aggregating a Token instance in the QVariant instead of a pointer to Token.

See also this post from the Qt forum

like image 113
StefanQ Avatar answered Apr 17 '23 22:04

StefanQ


You need to register a QString converter to the meta-object system for the custom type Token

To do that, you have two ways:

  1. Your custom type Token has already a method toString() (or equivalent)

Then you can directly register this method as converter

#include <QDebug>
#include <QMetaType>
#include <functional>

struct Token
{
  QString toString() const
  {
    return _value;
  }

  QString _value;
};

Q_DECLARE_METATYPE( Token )

int main(int argc, char* argv[])
{
   qRegisterMetaType<Token>();

   QMetaType::registerConverter(&Token::toString);

   Token t {"hello"};

   QVariant value;
   value.setValue( t );
   qDebug() << value.toString();
}
  1. The toString() function is external (not a method of the custom type Token)

Then you can register this external toString function using unary function

#include <QDebug>
#include <QMetaType>
#include <functional>

struct Token
{
 QString _value;
};

Q_DECLARE_METATYPE( Token )

QString tokenToString(const Token &t)
{
 return t._value;
}

struct toQString : public std::unary_function<Token,QString>
{
 QString operator() (const Token &value) const
 {
   return tokenToString(value);
 }
};


int main(int argc, char* argv[])
{
  qRegisterMetaType<Token>();
  QMetaType::registerConverter<Token, QString, toQString>(toQString());

  Token t {"hello"};

  QVariant value;
  value.setValue( t );
  qDebug() << value.toString();
}

PS: If you want to do

qDebug() << value;

you need to implement the QDebug operator in your custom Type, and register this operator for the custom type Token to the meta-object system.

QMetaType::registerDebugStreamOperator<Token>()
like image 34
BluRay Avatar answered Apr 17 '23 23:04

BluRay