4
votes

I am working with Qt 5.7 (C++).

Inside the cpp file of one class I am using an anonymous namespace to create a class (some utility) that I will use only in that file.

However, I got Linking errors if the utility-class is derived from a Qt class. I think that the problem is at the Q_OBJECT macro, if I don't add it I don't get the errors. But in any Qt derived class is imperative/recommended to have the Q_OBJECT macro.

How can I avoid this isue? Is there any other approach to have a utility-class with file-scope?

Simple example to show errors: the class CMyClass uses a utility class (named CUtility) that derives from QWidget.

Thank you.

CMyClass.h

class CMyClass
{
public:
   CMyClass();
   void someMethod();
};

CMyClass.cpp

#include <QtWidgets>
#include "CMyClass.h"

namespace
{
   class CUtility : public QWidget
   {
      Q_OBJECT
   public:
      CUtility(QWidget *p_parent = 0) : QWidget(p_parent){qDebug() << "CUtility constructor";}
      void utilityMethod() {qDebug() << "This is CUtility::utilityMethod()";}
   };
}


CMyClass::CMyClass()
{
   qDebug() << "CMyClass constructor.";
}

void CMyClass::someMethod()
{
   qDebug() << "This is CMyClass::someMethod().";
   CUtility p_myUtil;
   p_myUtil.utilityMethod();
}

The errors are:

LNK2001: unresolved external symbol "public: virtual struct QMetaObject const * __cdecl `anonymous namespace'::CUtility::metaObject(void)const " (?metaObject@CUtility@?A0x27a8253c@@UEBAPEBUQMetaObject@@XZ)

LNK2001: unresolved external symbol "public: virtual void * __cdecl `anonymous namespace'::CUtility::qt_metacast(char const *)" (?qt_metacast@CUtility@?A0x27a8253c@@UEAAPEAXPEBD@Z) sin resolver

LNK2001: unresolved external symbol "public: virtual int __cdecl `anonymous namespace'::CUtility::qt_metacall(enum QMetaObject::Call,int,void * *)" (?qt_metacall@CUtility@?A0x27a8253c@@UEAAHW4Call@QMetaObject@@HPEAPEAX@Z) sin resolver

2
Have you tried the usual #include "CMyClass.moc" at the end of CMyClass.cpp, then rerunning qmake+make?peppe

2 Answers

3
votes

It doesn't work with the Q_OBJECT macro because the macro add members to your class, members which are defined in the C++ code generated by the moc (generally in moc_CMyClass.cpp making it incompatible with a file-scope).

One possible solution is to skip the Q_OBJECT macro, it is not mandatory and you may not need it. The drawback is that you will lose introspection information about your class and cannot declare signals and slots.

The other solution is, as suggested by @KubaOber, to include the generated cpp file at the end of your own copy file. In this case qmake will detect it and will not compile the moc cpp file on its own.

6
votes

This has nothing to do with anonymous namespaces at all. They are a non sequitur, in fact.

Recall that moc generates the implementations of a few methods, including signals, and some static data. For this to work, the class declaration must be visible to moc output. It is visible at the end of the .cpp file.

Thus, to have a Q_OBJECT class inside a foo.cpp file, you must #include "foo.moc" at the end of that file. Then just re-build if using cmake, or, for qmake, re-run qmake first and then build the project. That's all.

In the complete example below, the Utility class can be in the anonymous namespace, but doesn't have to be. The anonymous namespace isn't "really" a namespace: it has a special meaning that limits the scope of the contained identifiers to the translation unit. It's like static, except it can be also applied to types, not only functions and variables.

// main.cpp
#include <QObject>

namespace {
   class Utility : public QObject {
      Q_OBJECT
   public:
      Utility(QObject *parent = {});
   };
}

Utility::Utility(QObject *parent) : QObject(parent) {}

int main() {
  Utility utility;
}

#include "main.moc"