Пользовательский тип в QVariant преобразуется в пустую строку
Я пишу лексический сканер, который генерирует поток токенов из некоторого ввода. Эти токены имеют тип и значение. Поскольку я использую Qt, я решил сохранить данные токена как QVariant
, Это очень хорошо работает с данными токенов нестандартного типа.
К сожалению, у меня есть несколько пользовательских типов, которые также хранятся внутри токенов. Жетоны имеют toString()
функция, которая выводит описание токена (для отладки), но для всех токенов, которые имеют данные пользовательского типа, эта функция выдает пустую строку. Код выглядит так:
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("");
}
Пример вывода со сканера:
"Token(TT_TEST, )"
"Token(TT_PLUS, +)"
"Token(TT_NUMBER, 5)"
"Token(TT_end, #)"
TT_TEST
токен содержит класс Test, и я ожидаю, что вариант выведет его значение. К сожалению, это не работает, и я пробовал много решений, которые не работали. Мой текущий обход выглядит так:
template <typename T>
bool writeToStringList(QStringList& sl, QVariant v)
{
if (!v.canConvert<T>()) return false;
sl << v.value<T>().toString();
return true;
}
и модифицированный toString()
функция:
sl << "Token(";
sl << ::toString(type_) << ", ";
if (!writeToStringList<Test>(sl, value_)) {
sl << value_.toString();
}
и я должен сделать это для всех моих пользовательских типов, которые просто кажутся довольно неуклюжими и неправильными.
Я считаю, что должно быть лучшее решение этой проблемы. Может ли кто-нибудь из вас:
- Подскажите как решить проблему с
QVariant
в лучшую сторону, или - предложить совершенно другое решение без
QVariant
, (У меня было шаблонное решение ранее, но я столкнулся с различными проблемами там, поэтому мне понадобится пример, если это будет предложено).
?
2 ответа
Q_DECLARE_METATYPE() на самом деле достаточно для включения агрегации пользовательского типа в QVariant. Это не охватывает такие аспекты, как неявное преобразование типов и сравнение в контексте QVariant. Предполагается, что Qt5 для облегчения неявного преобразования в QString вы можете сделать следующее:
#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;
}
Это, конечно, также возможно (и более экономно) с Q_DECLARE_METATYPE( MyType )
и непосредственная агрегация экземпляра Token в QVariant вместо указателя на Token.
Смотрите также этот пост на форуме Qt.
Вам необходимо зарегистрировать конвертер QString в системе метаобъектов для токена пользовательского типа.
Для этого у вас есть два способа:
- Токен пользовательского типа уже имеет метод toString() (или эквивалент)
Затем вы можете напрямую зарегистрировать этот метод как конвертер
#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();
}
- Функция toString() является внешней (не является методом токена пользовательского типа)
Затем вы можете зарегистрировать эту внешнюю функцию toString, используя унарную функцию
#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: Если хочешь сделать
qDebug() << value;
вам необходимо реализовать оператор QDebug в своем настраиваемом типе и зарегистрировать этот оператор для токена настраиваемого типа в системе метаобъектов.
QMetaType::registerDebugStreamOperator<Token>()