Подтвердить что ты не робот

Использовать С++ - слот в QML, который возвращает тип в пространстве имен

Прежде всего хочу упомянуть, что следующее работало отлично до Qt 5.0.0 beta 1 (может быть, бета-2 и RC тоже, не знаю), но не удается в финальной версии Qt 5.0.0. Я хочу только обратиться к результатам, представленным в окончательной версии Qt 5.0.0. Скорее всего, это связано с недавними изменениями в Qt5.

На стороне С++ у меня есть набор классов (QObject-производный) в пространстве имен (который необязательно запускается с флагами компилятора, классы находятся в отдельной библиотеке, и библиотека оставляет использование пространства имен в качестве опции для пользователя библиотеки). Класс, здесь Game, может выглядеть так (выдержка):

OAE_BEGIN_NAMESPACE

// forward-declarations:
class Player;    // Player is just another class in the same library

class Game : public QObject
{
    Q_OBJECT

public:
    explicit Game(...);

public slots:
    Player *player() const;  // <-- the quesion is about such slots
};

OAE_END_NAMESPACE

Макросы OAE_BEGIN/END_NAMESPACE расширяются до namespace OAE_NAMESPACE {... } или ничего, так же, как Qt делает это в <qglobal.h>, только "QT" заменяется на "OAE" в именах макросов:

#ifndef OAE_NAMESPACE

# define OAE_PREPEND_NAMESPACE(name) ::name
# define OAE_USE_NAMESPACE
# define OAE_BEGIN_NAMESPACE
# define OAE_END_NAMESPACE
# define OAE_BEGIN_INCLUDE_NAMESPACE
# define OAE_END_INCLUDE_NAMESPACE
# define OAE_BEGIN_MOC_NAMESPACE
# define OAE_END_MOC_NAMESPACE
# define OAE_FORWARD_DECLARE_CLASS(name) class name;
# define OAE_FORWARD_DECLARE_STRUCT(name) struct name;
# define OAE_MANGLE_NAMESPACE(name) name

#else /* user namespace */

# define OAE_PREPEND_NAMESPACE(name) ::OAE_NAMESPACE::name
# define OAE_USE_NAMESPACE using namespace ::OAE_NAMESPACE;
# define OAE_BEGIN_NAMESPACE namespace OAE_NAMESPACE {
# define OAE_END_NAMESPACE }
# define OAE_BEGIN_INCLUDE_NAMESPACE }
# define OAE_END_INCLUDE_NAMESPACE namespace OAE_NAMESPACE {
# define OAE_BEGIN_MOC_NAMESPACE OAE_USE_NAMESPACE
# define OAE_END_MOC_NAMESPACE
# define OAE_FORWARD_DECLARE_CLASS(name) \
    OAE_BEGIN_NAMESPACE class name; OAE_END_NAMESPACE \
    using OAE_PREPEND_NAMESPACE(name);

# define OAE_FORWARD_DECLARE_STRUCT(name) \
    OAE_BEGIN_NAMESPACE struct name; OAE_END_NAMESPACE \
    using OAE_PREPEND_NAMESPACE(name);

# define OAE_MANGLE_NAMESPACE0(x) x
# define OAE_MANGLE_NAMESPACE1(a, b) a##_##b
# define OAE_MANGLE_NAMESPACE2(a, b) OAE_MANGLE_NAMESPACE1(a,b)
# define OAE_MANGLE_NAMESPACE(name) OAE_MANGLE_NAMESPACE2( \
        OAE_MANGLE_NAMESPACE0(name), OAE_MANGLE_NAMESPACE0(OAE_NAMESPACE))

namespace OAE_NAMESPACE {}

# ifndef OAE_BOOTSTRAPPED
# ifndef OAE_NO_USING_NAMESPACE
   /*
    This expands to a "using OAE_NAMESPACE" also in _header files_.
    It is the only way the feature can be used without too much
    pain, but if people _really_ do not want it they can add
    DEFINES += OAE_NO_USING_NAMESPACE to their .pro files.
    */
   OAE_USE_NAMESPACE
# endif
# endif

#endif /* user namespace */

В дальнейшем, говоря "включение пространства имен", я имею в виду, что я объявил макрос OAE_NAMESPACE, в этом случае со значением oae.

В частности, я обращаюсь к экземплярам этого класса и класса Player, возвращаемым player() из QML для пользовательского интерфейса моего приложения. Для этого я регистрирую классы следующим образом:

qmlRegisterType<Game>();
qmlRegisterType<Player>();

Я предоставляю QML-интерфейс указателю на экземпляр Game, называемый theGame внутри QML:

view.engine()->rootContext()->setContextProperty("theGame",
        QVariant::fromValue<Game*>(game));

В QML я использую это, как обычно. Небольшой пример должен напечатать адрес указателя player():

Rectangle {
    width: 100; height: 100
    Component.onCompleted: console.log(theGame.player())
}

Я получаю следующие результаты, в зависимости от того, установлен ли я OAE_NAMESPACE или нет (кстати: я использую тот же параметр как для библиотеки, так и для приложения, используя ее):

  • При отключении пространства имен все работает так, как ожидалось, и QML печатает мне указатель:

    Player(0x10b4ae0)
    
  • Когда разрешено пространство именusing в коде С++, используя в библиотеке, поэтому я вообще не меняю код), QML не удается понять тип возврата Game::player():

    Error: Unknown method return type: Player*
    
  • Если изменить тип возврата Game::player() на oae::Player*, все снова работает отлично:

    oae::Player(0x10b4ae0)
    

До сих пор я пришел к выводу, что moc не рассматривает пространство имен, которое я помещал в класс. Мое первое предположение: Hey, moc не знает, что я определяю пространство имен при вызове g++, что я и делаю в файле .pro:

DEFINES += OAE_NAMESPACE=oae

Однако при изменении типа возврата на OAE_NAMESPACE::Player* он все равно работает, поэтому moc знает о макросе OAE_NAMESPACE, но он не расширяет макросы OAE_BEGIN/END_NAMESPACE или не анализирует пространства имен в все больше.

moc выводит следующие "stringdata" для Player * Game::player() const, который содержит тип возвращаемого метода:

  • Если отключить пространство имен и использовать тип возврата Player*:

    "player\0Player*\0"
    
  • Когда разрешает пространство имен и использует тип возврата Player*:

    "player\0Player*\0"
    
  • Когда разрешает пространство имен и использует тип возврата OAE_NAMESPACE::Player*:

    "player\0oae::Player*\0"
    

С другой стороны, moc добавляет имена классов, возвращаемые QMetaObject::className(), с пространством имён, если он включен.

Мое заключение заключается в том, что я мог бы исправить это, написав OAE_NAMESPACE::ClassName вместо ClassName всякий раз, когда они используют эти типы в подписях мета-методов QObject. (Ну, есть лучший макрос OAE_PREPEND_NAMESPACE). Поскольку это выглядело бы ужасно в коде, и для меня это даже кажется неправильным, потому что метод уже находится в пространстве имен, есть лучшее решение?

Теперь также есть OAE_BEGIN/END_MOC_NAMESPACE (аналогично QT_BEGIN/END_MOC_NAMESPACE), , так что, возможно, мне нужны те где-нибудь? Я не знаю, где/как они используются в Qt, поэтому я должен использовать их соответственно в своей библиотеке, так как я хочу использовать ту же опционную функцию пространства имен, что и Qt.

4b9b3361

Ответ 1

Действительно ли он работал в 5.0.0a?

Я просмотрел исходный код Qt 5.0.0 и посмотрел, где разбираются методы, особенно тип возврата (fyi, 5.0.0\qtbase\src\tools\moc\moc.cpp: L160), и нет проверки пространства имен (ни по аргументам, поэтому player(Player* p) тоже не будет работать). Принимая во внимание, что это сделано для класса def (5.0.0\qtbase\src\tools\moc\moc.cpp: L620 и L635)

Я думаю, что "мы" можем назвать это ошибкой (или недосмотром)