on windows can't export from a dll a class inheriting from QList<QVariant>



  • Well in short I can't export from dll symbols that are perfectly valid otherwise.
    I realized an application under Linux:

    • using mostly Qt as library;
    • consisting of several libraries and a main gui application.

    And as I'm making my code compliant to Visual Studio 2015's compiler I stumbled on the following dllexport problem (it is in fact a snipped code):

    #include <QVariant>
    #include <QList>
    
    class SNIPPEDSHARED_EXPORT Snipped : public QList<QVariant>
    {
    
    public:
      Snipped();
    };
    

    SNIPPEDSHARED_EXPORT being the classical:

    #if defined(SNIPPED_LIBRARY)
    #  define SNIPPEDSHARED_EXPORT __declspec(dllexport)
    #else
    #  define SNIPPEDSHARED_EXPORT __declspec(dllimport)
    #endif
    

    That code generating:

    C:\Qt\5.7\msvc2015_64\include\QtCore\qhashfunctions.h:110: erreur : C2665: 'qHash': none of the 22 overloads could convert all the argument types
    C:\Qt\5.7\msvc2015_64\include\QtCore\qhashfunctions.h:110: erreur : C2056: illegal expression
    

    The error message is explicit enough and I even found people that have defined their own qHash:

    What is troubling me:

    • I'm using QList and not qHash direcly; I'm well aware that QList may use qHash internally but the code runs smoothly with gcc: so it seems that Qt is providing what is required;
    • when I build the exact class in a regular binary (not a library one), all runs perfectly (either windows or linux).
    • when I removed the __declspec, then the library is building perfectly

    I took time to read documentations about exporting templates as it seems the real problem (I even took time to read Qt source code :

    But the described problem doesn't seem to apply. I tried all kind of combinations but none did the trick (and it would have been a fluke).

    The code being so short and simple, I think that I missed something basic (sorry: it is not really an interesting question and it is fustrating).

    Has someone an idea?
    P.S.: Full technical data:

    • Visual Studio 2015 Community Edition, with the last update: 14.0.25424.00 Update 3
    • Visual C++ 2015 00322-20000-00000-AA285
    • Using the Windows Kit 10: 10.0.10586.0
    • creation of an library project through QtCreator:
      • snipped.pro
        QT       -= gui
        TARGET   = snipped
        TEMPLATE = lib
        DEFINES += SNIPPED_LIBRARY
        SOURCES += snipped.cpp
        
      • snipped.cpp
        #include "snipped.h"
        #include <QDebug>
        
        Snipped::Snipped()
        {
          qDebug("Coucou !");/*some code is required otherwise the build will generate nothing*/
        }
        
      • snipped.h
        #ifndef SNIPPED_H
        #define SNIPPED_H
        
        #include "snipped_global.h"
        #include <QVariant>
        #include <QList>
        
        class SNIPPEDSHARED_EXPORT Snipped : public QList<QVariant>
        {
        public:
          Snipped();
        };
        
        #endif // SNIPPED_H
        
      • snipped_global.h
        #ifndef SNIPPED_GLOBAL_H
        #define SNIPPED_GLOBAL_H
        
        #include <QtCore/qglobal.h>
        
        #if defined(SNIPPED_LIBRARY)
        #  define SNIPPEDSHARED_EXPORT __declspec(dllexport)
        #else
        #  define SNIPPEDSHARED_EXPORT __declspec(dllimport)
        #endif
        
        #endif // SNIPPED_GLOBAL_H
        


  • does using QVariantList instead of QList<QVariant> work?



  • That change nothing (and I did try previously any kind of typedefs :-) ).



  • Ok, the problem is that, QList::toSet() needs qHash to work.
    you just need to declare your own QHash function

    snipped.h:

    #ifndef SNIPPED_H
    #define SNIPPED_H
    
    #include "snipped_global.h"
    #include <QVariant>
    #include <QList>
    
    class SNIPPEDSHARED_EXPORT Snipped : public QList<QVariant>
    {
    public:
      Snipped();
    };
    uint qHash(const QVariant& val,uint seed);
    #endif // SNIPPED_H
    
    

    snipped.cpp

    #include "snipped.h"
    #include <QDebug>
    
    Snipped::Snipped()
    {
      qDebug("Coucou !");/*some code is required otherwise the build will generate nothing*/
    }
    uint qHash(const QVariant& val,uint seed){
        return qHash(&val,seed);
    }
    


  • Thanks for your time, but I did found such a « solution » (see in my first post) and I keep thinking it is not the right solution.

    Why should I need that ? Why the code builds perfectly if there is no export at all ?
    Why when there is no export the compiler found a qHash implementation ?

    I would really not make « magic code » and then later have bizarre errors (Windows and the dll complexity).

    Furthermore I should have an understanding of the problem, don't you think so ?

    P.S. : I'm be off 2 hours from now.



  • Why the code builds perfectly if there is no export at all ?

    It does not find it, but if you don't export it doesn't care. Try removing the export but then call Snipped::toSet() somewhere and it will not compile either

    just as much as:

    // This compiles
    class A{
    int a;
    public:
    A():a(0){}
    void noBody();
    void methodA(int val){a=val;}
    }
    
    // This doesn't
    class A{
    int a;
    public:
    A():a(0){}
    void noBody();
    void methodA(int val){a=val; noBody();}
    }
    

    Why when there is no export the compiler found a qHash implementation ?

    No there isn't, see http://doc.qt.io/qt-5/qhash.html, no QVariant there

    « magic code »

    it is not magic code, it's the requirement to make QHash and QSet work. Since there is a generic qHash template for pointers I leveraged it



  • I don't quite get it; If you are right, then why this code runs perfectly (either on Windows or Linux) :

    • snipped_main
      QT += core
      QT -= gui
      
      CONFIG += c++11
      
      TARGET = snipped_main
      CONFIG += console
      CONFIG -= app_bundle
      
      TEMPLATE = app
      
      SOURCES += main.cpp snipped.cpp
      HEADERS += snipped.h
      
    • main.cpp
      #include <QCoreApplication>
      #include "snipped.h"
      
      int main(int argc, char *argv[])
      {
        QCoreApplication a(argc, argv);
        Snipped toto;
        toto.append(QString("TOTO"));
        return EXIT_SUCCESS;//a.exec();
      }
      
    • snipped.cpp
      #include "snipped.h"
      #include <QDebug>
      
      Snipped::Snipped()
      {
        qDebug("Coucou !");/*some code is required otherwise the build will generate nothing*/
      }
      
    • snipped.h
      #ifndef SNIPPED_H
      #define SNIPPED_H
      
      #include <QVariant>
      #include <QList>
      
      class Snipped : public QList<QVariant>
      {
      public:
        Snipped();
      };
      
      #endif // SNIPPED_H
      


  • I further tried to use my first example (the one as a library) and altered snipped.cpp:

    #include "snipped.h"
    #include <QDebug>
    
    Snipped toto;
    Snipped::Snipped()
    {
      qDebug("Coucou !");
    }
    

    and it build too without any problem.

    I understand the problem of exporting symbols, but why MS compiler needs all methods defined when gcc has no problem or MS compiler even builds the library correctly when there is no export and no call to ::toSet ?



  • I found why I had this problem on « Microsoft Specific »: https://msdn.microsoft.com/en-us/library/81h27t8c.aspx
    I quote:

    When you declare a class dllexport, all its member functions and static data members are exported. You must provide the definitions of all such members in the same program.  Otherwise, a linker error is generated.
    

    And in order to be complete:

    The one exception to this rule applies to pure virtual functions, for which you need not provide explicit definitions. However, because a destructor for an abstract class is always called by the destructor for the base class, pure virtual destructors must always provide a definition. Note that these rules are the same for nonexportable classes.
    
    If you export data of class type or functions that return classes, be sure to export the class.
    

    It will teach me not to glance over the documentation.



  • Thanks VRonin for your help.
    Sorry I have insisted, but I always prefer to have « proof ».


Log in to reply
 

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