Question

I first want to mention that the following worked fine up to Qt 5.0.0 beta 1 (maybe beta 2 and RC too, don't know), but fails in Qt 5.0.0 final release version. I only want to refer to the results seen in Qt 5.0.0 final release version. So most probably this has something to do with recent changes in Qt5.

On C++ side I have a set of classes (QObject-derived) in a namespace (which is optionally triggered with compiler flags; the classes are in a separate library and the library leaves the usage of a namespace as an option to the user of the library). A class, here Game, might look like this (excerpt):

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

The macros OAE_BEGIN/END_NAMESPACE expand to either namespace OAE_NAMESPACE { ... } or nothing, in the same way Qt does it in <qglobal.h>, just "QT" replaced with "OAE" in the macro names:

#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 */

In the following, when saying "enabling the namespace", I mean I declared the macro OAE_NAMESPACE, in this case with the value oae.

Among others, I access instances of this class and the Player class as returned by player() from within QML for the user interface of my application. For this, I register the classes as follows:

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

I provide the QML frontend a pointer to an instance of a Game, called theGame within QML:

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

Within QML, I use this as usual. A small example should print a pointer address of the player():

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

I get the following results, depending on whether I set a OAE_NAMESPACE or not (by the way: I use the same setting for both the library and the application using it):

  • When disabling the namespace, everything works as expected and QML prints me the pointer:

    Player(0x10b4ae0)
    
  • When enabling the namespace (and using it in the C++ code using the library, so I don't change the code at all), QML fails to understand the return type of Game::player():

    Error: Unknown method return type: Player*
    
  • When changing the return type of Game::player() to oae::Player*, everything works fine again:

    oae::Player(0x10b4ae0)
    

My conclusion so far is that moc doesn't consider the namespace I put around the class. My first guess was: Hey, moc doesn't know that I define the namespace when calling g++, which is what I do in the .pro file:

DEFINES += OAE_NAMESPACE=oae

However, when changing the return type to OAE_NAMESPACE::Player*, it still works, so moc does know of the OAE_NAMESPACE macro, but it doesn't either expand the OAE_BEGIN/END_NAMESPACE macros or it doesn't parse namespaces at all anymore.

moc produces the following "stringdata" for Player * Game::player() const which containes the method's return type:

  • When disabling the namespace and using the return type Player*:

    "player\0Player*\0"
    
  • When enabling the namespace and using the return type Player*:

    "player\0Player*\0"
    
  • When enabling the namespace and using the return type OAE_NAMESPACE::Player*:

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

On the other side, moc prepends class names as returned by QMetaObject::className() with the namespace if enabled.

My conclusion now is that I could fix this by writing OAE_NAMESPACE::ClassName instead of ClassName whenever using these types in the signatures of QObject meta methods. (Well, there is the better macro OAE_PREPEND_NAMESPACE). Since this would look horrible in the code, and to me it even seems wrong because the method already is in the namespace, is there a better solution?

Now there also is OAE_BEGIN/END_MOC_NAMESPACE (analogous to QT_BEGIN/END_MOC_NAMESPACE), so maybe I need those anywhere? I don't know where/how they are used in Qt, so I should use them accordingly in my library, since I want to use the same optional namespace feature as Qt does.

Was it helpful?

Solution

Did it really worked in 5.0.0a?

I browsed through Qt 5.0.0 source code and looked where methods are parsed, especially the return type (fyi, 5.0.0\qtbase\src\tools\moc\moc.cpp:L160) and there are no namespace check (neither on the arguments, so player(Player* p) won't work either). Whereas, it's done for the class def (5.0.0\qtbase\src\tools\moc\moc.cpp:L620 & L635)

I think "we" can call this a bug (or an oversight)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top