Недавно столкнулся с ситуацией, когда макроса из заголовка оказывается недостаточно для полноценной работы с рефлексией и слотами в Qt.
Допустим, необходимо организовать вызов метода или слота для наследника от QObject по имени, ну и получить результат, само собой в QVariant.
Обычный способ это сделать - получить метаметод объекта, который представляет такую возможность и вызвать его с соответствующими аргументами. Единственный момент - QVariant необходимо преобразовать в QGenericArgument или QGenericReturnArgument по ситуации. Это обходят таким образом, как показано здесь:
Такое решение взято отсюда . Это решение корректно и работает, отчасти потому, что стандартные макросы Q_ARG и Q_RETURN_ARG в глубине исходного кода разворачиваются в подобные вызовы.
Но здесь есть один тонкий момент и это вызов:
По сути данный код предлагает сконструировать QVariant, который содержит внутри значение возвращаемого типа для метаметода, которое создаётся по умолчанию. И это будет работать для всех базовых типов или типов Qt. Но, так возможно, что там окажется пустое значение.
Рассмотрим пример:
В данном примере у нас слот возвращает указатель на наследника класса QObject. И данный слот потом вызывается посредством метаметода.
Как ни странно, после вызова программы окажется, что возвращаемое значение пусто и невалидно, хотя метод выполнился без проблем. Как так?
Рассмотрим внимательно код выше. Имя типа возвращаемого значения передаётся в QMetaType::type(), который возвращает ID типа для конструирования. Так вот: в коде выше он равен 0. Получается, что тип не зарегистрирован. Быстрый поиск по методам, связанным с QMetaType даёт нам решение:
Допустим, необходимо организовать вызов метода или слота для наследника от 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.
Комментариев нет:
Отправить комментарий