qtnamespaces

When, where and why use namespace when registering custom types for Qt


Similar questions have been raised multiple times, but I'm focussing on the namespace and pointer issues.

MyClass.h

namespace foo {
class MyClass {
   MyClass();
};

QDataStream &operator<<(QDataStream &out, const MyClass & myObj);
QDataStream &operator>>(QDataStream &in, MyClass &myObj);

} // namespace foo

Q_DECLARE_METATYPE(foo::MyClass) // #1
Q_DECLARE_METATYPE(foo::MyClass*) // #2

fooMyClass.cpp (so many permutations):

MyClass::MyClass()
{
  qRegisterMetaType<MyClass>("MyClass"); // #3
  qRegisterMetaType<MyClass*>("MyClass*"); // #4
  qRegisterMetaType<MyClass>("foo::MyClass"); // #5
  qRegisterMetaType<MyClass*>("foo::MyClass*"); // #6
  qRegisterMetaType<foo::MyClass>("foo::MyClass"); // #7
  qRegisterMetaType<foo::MyClass*>("foo::MyClass*"); // #8
  qRegisterMetaType<MyClass>(); // #9
  qRegisterMetaType<MyClass*>(); // #10
  qRegisterMetaType<foo::MyClass>(); // #11
  qRegisterMetaType<foo::MyClass*>(); // #12

  // same for qRegisterMetaTypeStreamOperators<T>();
}

So my question is, when and why is it required to provide the namespace and/or the pointer variant if I intend to use the custom objects for signals and slots (potentially as reference and pointer) inside as well as outside the namespace. Do I always have to fully qualify the namespace?


Solution

  • I'm referring to Qt5 in this answer. Qt4 doesn't go well with this use case.

    Data stream operators

    Data stream operators are not required for your type if you only intend to use it in signals and slots. They are required if you want to do some serialization.

    Pointers, references and values

    Qt considers MyClass and MyClass* two different unrelated types. You should declare, register and use them separately. Using const MyClass & argument type is compatible with MyClass in Qt meta-object system. Note that using MyClass and MyClass* meta types simultaneously in one program is unusual and can cause mistakes and confusion. You should choose one of the options and use it throughout the program. Also passing pointers to slots is not recommended because it causes unsolvable ownership problem. So I recommend to use passing by const reference (which sometimes will be converted to passing by value internally in Qt signal-slot system). If MyClass objects contain massive data, you should implement implicit data sharing using QSharedDataPointer.

    Declaring a meta type

    First of all, you always need to declare your meta type:

    Q_DECLARE_METATYPE(foo::MyClass)
    

    It works at compile time, so there are no limitations on how you refer to your class. The following code will work as well:

    using namespace foo;
    Q_DECLARE_METATYPE(MyClass)
    

    Registering a meta type

    Now you need to register your classes. Theoretically, you need to specify all strings that you want to use to refer to your type, i.e.:

    qRegisterMetaType<foo::MyClass>("MyClass");
    qRegisterMetaType<foo::MyClass>("foo::MyClass");
    

    It doesn't matter how you refer to MyClass in the template argument. The following code will work similarly:

    using namespace foo;
    qRegisterMetaType<MyClass>("MyClass");
    qRegisterMetaType<MyClass>("foo::MyClass");
    

    For example, the "MyClass" and "foo::MyClass" strings are used to identify argument types when you refer to your signals and slots like SIGNAL(signal1(MyClass)).

    New signal and slot syntax

    If you using new signal slot syntax with pointers to member functions, you need to do only one registration with arbitrary string argument. It seems that it is intended to make it work even without any registrations. This part of the docs instructs to only add Q_DECLARE_METATYPE, in opposite to this that requires qRegisterMetaType(). Unfortunately, now in my Qt installation it works only with direct connections. Queued connections still require at least one registration call.

    Implicit registration of class without namespace

    I was experimenting with some variants of registration in Qt 5.1 and found out that Qt automatically registers aliases without namespace. So if you write

    qRegisterMetaType<foo::MyClass>("foo::MyClass");
    

    , Qt will additionally automatically register "MyClass" alias. So, after executing this statement you will be able to refer to your type as MyClass and foo::MyClass. There is no information in the documentation about how Qt handles namespaces. We could assume that this behavior is intended and will not be removed in next versions but I wouldn't rely on that. The following code makes implicit registration obvious:

    qRegisterMetaType<foo::MyClass>("foo::MyClass");
    qRegisterMetaType<bar::MyClass>("MyClass");
    

    Qt 5.1 says:

    QMetaType::registerTypedef: Binary compatibility break -- Type name 'MyClass' previously registered as typedef of 'MyClass' [1030], now registering as typedef of 'bar::MyClass' [1032].

    Qt 4.8 works without error (it seems that this behavior is not yet introduced in this version).