2012-12-29 1 views
14

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

На стороне С ++ У меня есть набор классов (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» заменить с «ОПЭ» в именах макросов:

#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 FRONTEND указатель на экземпляр 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 его в коде C++ с использованием библиотеки, так что я не изменить код на всех), QML не в состоянии понять тип возвращаемого Game::player():

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

    oae::Player(0x10b4ae0) 
    

Мой вывод до сих пор является то, что moc не рассматривает пространство имен я ставлю вокруг класса. Моя первая догадка: Эй, moc не знает, что я определить пространство имен при вызове g++, что это то, что я делаю в файле .pro:

DEFINES += OAE_NAMESPACE=oae 

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

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

  • При отключение пространства имен и используя тип возвращаемого 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.

+0

Я добавлю некоторую информацию о том, какой код создавался moc (например, была проанализирована подпись слота). – leemes

+1

Использует ли qmlRegisterType <> имя класса с именами? – mlvljr

+0

@mlvjr Спасибо за подсказку, но я уже пробовал это. На самом деле это ничего не меняет, поскольку это спецификатор типа C++, но это о том, как 'moc' анализирует файл заголовка. Он не обнаруживает, что 'Player' в пространстве имен фактически ссылается на класс' oae :: Player' при использовании вне пространства имен. Также я считаю, что это поведение было другим в Qt 5.0.0 alpha и beta1. – leemes

ответ

8

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

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

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

+0

Спасибо за ваши усилия. Я буду проверять это завтра. Конечно, ошибка может быть на моей стороне, я не хочу исключать эту возможность;) – leemes

+0

Ну, похоже, что * не * работать в Qt 5.0.0 beta1 тоже. Я просто повторил это. Странно, что я вспомнил, что это сработало. Благодарим вас за то, что вы нашли свое время в поиске этого материала в коде Moc и поздравляете за свои баллы! – leemes