The Linux Page

unresolved external symbol "public: virtual struct QMetaObject const *__thiscall MyClass::metaObject[...]"

Got a problem with Qt today... and it took me a little time to figure out again and I'm sure some people have something about it, but I couldn't find the right answer anywhere.

Qt has those objects named QObject. You can derive them that class and to complete the object, you want to add a Q_OBJECT inside the class. Something like this:

  class MyClass : public QObject
  {
    Q_OBJECT
    [...your object definitions...]
  };

That Q_OBJECT is what makes your object visible in the Qt environment (i.e. a Find by name will find your object if you give it a name with setObjectName().

Now, the result of that construct often results in the following unresolved errors:

  my_class.obj : error LNK2001: unresolved external symbol "public: virtual struct QMetaObject const
     * __thiscall MyClass::metaObject(void)const " (?metaObject@MyClass@@UBEPBUQMetaObject@@XZ)
  my_class.obj : error LNK2001: unresolved external symbol "public: virtual void * __thiscall
     MyClass::qt_metacast(char const *)" (?qt_metacast@MyClass@@UAEPAXPBD@Z)
  my_class.obj : error LNK2001: unresolved external symbol "public: virtual int __thiscall
     MyClass::qt_metacall(enum QMetaObject::Call,int,void * *)" (?qt_metacall@MyClass@@UAEHW4Call@MyClass@@HPAPAX@Z)

The problem is actually very simple, but each time I run into it I spend 1h to find the solution! The header file needs to be "moc-ed". That means the Qt "moc" compiler needs to run against the header to generate whatever is missing (a .cxx file with the functions automatically added by that Q_OBJECT macro.)

Also, you may get a similar link error with Visual C++.

  my_class.obj : error LNK2001: unresolved external symbol "public: static struct QMetaObject const
        other_class::staticMetaObject"
        (?staticMetaObject@other_class@@2UQMetaObject@@B)
  moc_my_class.obj : error LNK2001: unresolved external symbol "public: static struct QMetaObject const
        other_class::staticMetaObject"
        (?staticMetaObject@other_class@@2UQMetaObject@@B)

As you can see, the problem appears in my_class and moc_my_class. In this case, the link fails because the linker cannot find other_class entries.

The other class definition is expected to be very similar to the MyClass definition appearing earlier. However, the Visual Studio compiler expects explicit import and export statements whenever a class is to be put in a DLL.

The definition generally becomes:

  class OTHER_CLASS_API OtherClass : public ... { ... };

The addition is OTHER_CLASS_API which is defined as a standard C macro. The usual is to test whether you're compiling the library (generating the DLL) in which case you want to Export the class. In all other cases you want to Import the class. The import prepends "__imp_?" to all the symbols being imported. If you look closely at the last errors I listed, you may notice that the "__imp_?" is missing.

What generally happens with the API definition is some misconfiguration that transforms the import into an export. When that happens you get the errors I last presented here.

  #ifdef OTHER_CLASS_EXPORTS1
  #define OTHER_CLASS_API __declspec(dllexport)
  #else
  #define OTHER_CLASS_API __declspec(dllimport)
  #endif

As you can see, when you defined OTHER_CLASS_EXPORTS on your C++ compiler command line (-DOTHER_CLASS_EXPORTS), it will export (i.e. you are compiling the DLL.) In all other cases you import.

The link works when exporting because the functions are expected to be defined in that DLL. They are viewed as local links if necessary.

However, from the outside, the functions are not visible unless you import them.

  • 1. Note that if you want this to work on other platforms, you probably need to include another test with an empty definition of OTHER_CLASS_API which is most often the way code working under Linux and MS-Windows works.

Re: unresolved external symbol "public: virtual struct ...

Thank you very much! I noticed that I should use import as well.

I used Q_DECL_EXPORT instead of xxxAPISHARED_EXPORT in all classes and bypass the QT's following defines. Now I use the original QT's ...APISHARED_EXPORT defines and the problem is gone.

#if defined(xxx_LIBRARY)
# define xxxAPISHARED_EXPORT Q_DECL_EXPORT
#else
# define xxxAPISHARED_EXPORT Q_DECL_IMPORT
#endif

(where xxx is the library name)