Пользовательский тип в QVariant преобразуется в пустую строку

Я пишу лексический сканер, который генерирует поток токенов из некоторого ввода. Эти токены имеют тип и значение. Поскольку я использую Qt, я решил хранить данные токена как QVariant. Это работает очень хорошо для данных токена нестандартного типа.

К сожалению, у меня есть несколько пользовательских типов, которые также хранятся внутри токенов. У токенов есть функция toString(), которая выводит описание токена (для отладки), но для всех токенов, имеющих данные пользовательского типа, эта функция выдает пустую строку. Код выглядит следующим образом:

Тест.ч:

struct Test
{
    QString value_;

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

Q_DECLARE_METATYPE(Test)

Токен.h:

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

Токен.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 содержит тестовый класс, и я ожидаю, что вариант напечатает его значение. К сожалению, это не работает, и я перепробовал множество решений, которые не сработали. Мой текущий обходной путь выглядит так:

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. (Ранее у меня было шаблонное решение, но я столкнулся с разными проблемами, поэтому мне понадобится пример, если это будет предложено).

?


person hochl    schedule 01.06.2014    source источник
comment
Куда вы звоните qRegisterMetatype?   -  person Dmitry Sazonov    schedule 02.06.2014
comment
Я не знаю, как указано в документации : использовать типа T в QVariant, достаточно использовать Q_DECLARE_METATYPE(). Чтобы использовать тип T в сигнальных и слотовых соединениях с очередями, qRegisterMetaType‹T›() необходимо вызвать до того, как будет установлено первое соединение. и: Внимание! Эта функция полезна только для регистрации псевдоним (typedef) для каждого другого варианта использования вместо этого следует использовать Q_DECLARE_METATYPE и qMetaTypeId(). В настоящее время у меня нет пользовательского интерфейса (пока!).   -  person hochl    schedule 02.06.2014


Ответы (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

person StefanQ    schedule 06.04.2018
comment
Я попробую это, когда вернусь к этой теме, что может быть более длительный период времени, я в настоящее время не использую Qt:/ - person hochl; 10.04.2018

Вам необходимо зарегистрировать преобразователь QString в систему метаобъектов для пользовательского типа Token

Для этого у вас есть два пути:

  1. Ваш токен пользовательского типа уже имеет метод 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();
}
  1. Функция toString() является внешней (не является методом пользовательского типа Token)

Затем вы можете зарегистрировать эту внешнюю функцию 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>()
person BluRay    schedule 01.02.2021