Search code examples
c++qt

Qt C++: Multiple Q_NAMESPACE for the same namespace in different files


  • I have two enums. They are in the same namespace, but different header files.
  • To make them available to Qt meta type system, I try this:
//C1.h
#include <QObject>
namespace SW
{
Q_NAMESPACE
enum class Enum1 {A, B};
Q_ENUM_NS(Enum1)
}
//C2.h
#include <QObject>
namespace SW
{
Q_NAMESPACE
enum class Enum2 {A, B};
Q_ENUM_NS(Enum2)
}
//main.c
#include <QDebug>
#include <QMetaEnum>

#include "C1.h"
#include "C2.h"

int main(int argc, char *argv[]) {
    auto me1 = QMetaEnum::fromType<SW::Enum1>();
    qDebug() << "me1 valid:" << me1.isValid();

    auto me2 = QMetaEnum::fromType<SW::Enum2>();
    qDebug() << "me2 valid:" << me2.isValid();
}
  • With the above I get duplicate symbol linker error. Because both moc_C1.o and moc_C2.o defined the staticMetaObject that result from Q_NAMESPACE
  • I found that namespace in both headers must contain Q_NAMESPACE. Otherwise moc complains "Error: Namespace declaration lacks Q_NAMESPACE macro."
  • If I have only one of C1.h or C2.h, it builds and works fine.
  • If I move content of C2.h to C1.h, it also works, and would print:
me1 valid: true
me2 valid: true
  • If I move content of C2.h to main.cpp (with or without the Q_NAMESPACE), it compiles but malfunctions at runtime:
me1 valid: true
me2 valid: false

Question: is there no way to use Q_NAMESPACE for a namespace spread over multiple files?

Ways that work around this problem I can think of:

  • Enclose my enum in Q_OBJECT or Q_GADGET class
  • Have all my enum in the same file

Solution

  • There is a corresponding issue in the official Qt issue tracker at QTBUG-68611. However the issue is closed there as being out of scope, because due to the design of the Qt meta object compiler (moc), there is nothing that can be done about it.

    In short, Qt can not support multiple header files that each have the Q_NAMESPACE macro. It is not possible.

    Alternatively, you could (although I do not advise) have an in-between with a file structure such as this:

    // internal/C1.h
    #include <QObject>
    enum class Enum1 {A, B};
    Q_ENUM_NS(Enum1)
    
    //internal/C2.h
    #include <QObject>
    enum class Enum2 {A, B};
    Q_ENUM_NS(Enum1)
    
    // C.h
    #include <QObject>
    namespace SW
    {
    Q_NAMESPACE
    #include internal/C1.h
    #include internal/C2.h
    }