Сначала я хочу упомянуть, что до Qt 5.0.0 beta 1 работало нормально (может быть, beta 2 и RC тоже, не знаю), но в финальной версии Qt 5.0.0 происходит сбой. Я только хочу сослаться на результаты, увиденные в финальной версии Qt 5.0.0. Так что, скорее всего, это как-то связано с недавними изменениями в Qt5.
На стороне C ++ у меня есть набор классов (производных от 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
это в коде 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
который содержит тип возврата метода:
когда отключение пространства имен и используя тип возврата 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.
Это действительно работало в 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)
Я думаю, что «мы» можем назвать это ошибкой (или недосмотром)
Других решений пока нет …