суббота, 15 октября 2016 г.

Когда Q_DECLARE_METATYPE недостаточно

Недавно столкнулся с ситуацией, когда макроса из заголовка оказывается недостаточно для полноценной работы с рефлексией и слотами в Qt.


Допустим, необходимо организовать вызов метода или слота для наследника от QObject по имени, ну и получить результат, само собой в QVariant.

Обычный способ это сделать - получить метаметод объекта, который представляет такую возможность и вызвать его с соответствующими аргументами. Единственный момент - QVariant необходимо преобразовать в QGenericArgument или QGenericReturnArgument по ситуации. Это обходят таким образом, как показано здесь:


Такое решение взято отсюда . Это решение корректно и работает, отчасти потому, что стандартные макросы Q_ARG  и Q_RETURN_ARG  в глубине исходного кода разворачиваются в подобные вызовы.

Но здесь есть один тонкий момент и это вызов:


QVariant returnValue(QMetaType::type(metaMethod.typeName()), static_cast(NULL));


По сути данный код предлагает сконструировать QVariant, который содержит внутри значение возвращаемого типа для метаметода, которое создаётся по умолчанию. И это будет работать для всех базовых типов или типов Qt. Но, так возможно, что там окажется пустое значение.

Рассмотрим пример:

В данном примере у нас слот возвращает указатель на наследника класса QObject. И данный слот потом вызывается посредством метаметода.

Как ни странно, после вызова программы окажется, что возвращаемое значение пусто и невалидно, хотя метод выполнился без проблем. Как так?

Рассмотрим внимательно код выше. Имя типа возвращаемого значения передаётся в QMetaType::type(), который возвращает ID типа для конструирования. Так вот: в коде выше он равен 0. Получается, что тип не зарегистрирован. Быстрый поиск по методам, связанным с QMetaType даёт нам решение:

qRegisterMetaType<Test2>("Test2*");

Этот код надо добавить в самое начало main, после чего код будет работан верно.

Замечу, что это немного странно, так как документация четко говорит, что лучше всегда использовать Q_DECLARE_METATYPE, а этот код нужен только для алиасов типов.

Данное замечание проверялось для версий Qt ветки 4 и Qt 5.5.

Комментариев нет:

Отправить комментарий