Unresolved External Symbols Because of __declspec



  • I use MSVC 2014, MinGw-w64 4.2.0 and Qt 5.6.0 and qmake for creating my build Makefiles.

    My project consists of several .dll files and depends itself on 3rd party dlls. I would like to declare a class in one of my dlls that inherits from QObject and from another class from one of the 3rd party dlls.

    If I add "__declspec(dllexport)" to some methods of my class, build the dll and link other dlls against that dll, I get the following linker errors:

    ... unresolved external symbol ... MyClass::metaObject(void)const...
    ... unresolved external symbol ... MyClass::qt_metacast(char const *)...
    ... unresolved external symbol ... MyClass::qt_metacall(enum QMetaObject::Call,int,void * *)...

    This seems to have nothing to do with my project configuration (build.pro), since when I add "__declspec(dllexport)" to the class instead of some of its methods, the linker errors disappear. To make it clear:

    This snippet leads to the linker error:


    class MyClass: public QObject, ...
    {
    Q_OBJECT

    public:

    __declspec(dllexport) MyClass();
    

    ...


    while this snippet works great:


    class __declspec(dllexport) MyClass: public QObject, ...
    {
    Q_OBJECT

    public:
    MyClass();
    ...


    Therefore it seems to me as if the respective symbols "metaObject", "qt_metacast" and "qt_metacall" are not exported from the dll as long as I do not tell the compiler to export the whole class. This annoys me, since it might be impossible to export the whole class. Let's assume that it inherits from another 3rd party dll class which is not completely exported, but has just some exported methods. In this case I cannot export the whole child class as far as I know. The only thing I can do is selectively export child class methods. But in this case these linker errors appear, since the Qt symbols are not exported.

    Am I understanding the issue correctly? What is the standard way to deal with that? Is there an easy way to not export the whole class, but to export the Qt symbols?

    Thank you very much!


  • Qt Champions 2016

    hi
    Its not an option to use
    http://doc.qt.io/qt-5/plugins-howto.html
    The Low-Level API: Extending Qt Applications

    and use interfaces classes?


  • Lifetime Qt Champion

    Hi,

    Export/import declaration are a bit more complex than that. See the Creating Shared Libraries chapter of Qt's documentation.



  • @SGaist But isn't this exactly what I have done?! Q_DECL_EXPORT will just expand to something like __declspec(dllexport)! Or did you want to tell me to export the whole class always?



  • @mrjj Thanks for your reply, but I guess this is no option, if I have understood you correctly. The exact type hierarchy is necessary, since I want to use the classes inside a given framework. But on the other hand... perhaps another class could emit the Qt signal, therefore I could perhaps simplify my type hierarchies... I will think about it. Thank you!


  • Qt Champions 2016

    @Bihlerben
    You have to export the whole class, how else the members generated by the moc (the Q_OBJECT macro) will be found? Additionally, only your public members are exported from the class, so this begs the question, why would you want to export only some of the public methods?

    And a note: Exporting symbols is only relevant for Windows, on Linux Q_DECL_EXPORT would simply expand to nothing. Linux exports the whole public interface of the class by default.

    Note 2:
    You should use Q_DECL_EXPORT and Q_DECL_IMPORT wrapped in the preprocessor ifdef (as in the link @SGaist sourced), so when including the headers from the user application/libarary you get the __declspec(dllimport) specifier, which is required for Windows.


  • Moderators

    Hi,

    @kshegunov said:

    You have to export the whole class, how else the members generated by the moc (the Q_OBJECT macro) will be found?

    @Bihlerben said:

    it seems to me as if the respective symbols "metaObject", "qt_metacast" and "qt_metacall" are not exported from the dll as long as I do not tell the compiler to export the whole class.

    That's correct, for classes that inherit QObject.

    To clarify @kshegunov's post, the Q_OBJECT macro expansion includes additional members like metaObject, qt_metacast, etc.. If you don't export the class, then these members don't get exported since you can't add __declspec to the individual members.

    This annoys me, since it might be impossible to export the whole class. Let's assume that it inherits from another 3rd party dll class which is not completely exported

    Are you planning to create a class that mulitply-inherits a partially-exported 3rd party class as well as QObject?



  • @JKSH Yes, this was my intention. But if this is not easily possible, the easiest workaround will be to simplify my type hierarchy. And this should be possible here. Therefore I consider this issue as solved. Thanks to all for the answers.



  • @kshegunov Just a small remark: if I have understood it correctly, also g++ has introduced visibility that allows preventing export of all members (search for gcc and visibility).


  • Qt Champions 2016

    @Bihlerben

    Just a small remark: if I have understood it correctly, also g++ has introduced visibility that allows preventing export of all members (search for gcc and visibility).

    Yes, the feature had been around for ages. However, by default (as I mentioned before) you have full visibility. Additionally, this is supported in Qt through Q_DECL_HIDDEN not by Q_DECL_EXPORT/Q_DECL_IMPORT.

    Also, by the way, I don't see a reason for you not to be able to derive from multiple types (not that it's a good practice), and why hiding part of the interface would be important.



  • @kshegunov My explanation was bad (the topic is quite complicated for my poor brains).

    A better explanation is this:
    I use Open CASCADE, a free CAD kernel. They have some sophisticated RTTI mechanisms which they implement with macros. Now these macros sometimes add an export hint to the methods defined by themselves (like DEFINE_STANDARD_RTTIEXT for those who know it). This is okay, since in Open CASCADE classes are never completely exported, but only methods are exported.

    Now I want to subclass Open CASCADE classes and I want to use their macros to have the RTTI mechanisms available. But if I export my whole subclass and the macro adds export hints to the macro-defined methods, MSVC gives error C2487 "member of dll interface class may not be declared with dll interface". I have not found a way to suppress this.


  • Qt Champions 2016

    @Bihlerben
    Then, delegate the interface. You're not required to subclass both classes anyways. For example hold a reference to one of the CAD classes in your QObject objects and inline the calls you're going to use, or better yet have an access method that returns the CAD object.

    class SomeCadClassThatComesFromDll;
    
    class MyQObjectDelegatingToCAD : public QObject
    {
        Q_OBJECT
    
    public:
        MyQObject(int someParameterForInitialization, QObject * parent = NULL)
            : QObject(parent), cadObjectInstance(someParameterForInitialization)
        {
        }
    
        SomeCadClassThatComesFromDll & cadObject()
        {
            return cadObjectInstance;
        }
    
        const SomeCadClassThatComesFromDll & cadObject() const
        {
            return cadObjectInstance;
        }
    
    private: 
        SomeCadClassThatComesFromDll cadObjectInstance;
    };
    


  • @kshegunov You are right! This is also what I had meant with "simplifying the type hierarchy". Thank you very much for your help!


  • Qt Champions 2016

    @Bihlerben

    This is also what I had meant with "simplifying the type hierarchy".

    If you ask me, this should be the preferred approach anyway. I particularly dislike the idea of having multiple-inheritance, especially when there doesn't seem to be a good reason for it.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.