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:
- http://www.qtforum.org/article/19733/qmultihash-qvariant-compiler-error-c2665.html
- http://stackoverflow.com/questions/17208813/qvariant-as-key-in-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 :
- using Explicit template instantiation: https://anteru.net/blog/2008/11/19/318/ and https://msdn.microsoft.com/en-us/library/by56e477.aspx
- digging up some history bit: How to export an instantiation of a Standard Template Library (STL) class and a class that contains a data member that is an STL object : https://support.microsoft.com/en-us/kb/168958
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
- snipped.pro
-
Ok, the problem is that,
QList::toSet()
needs qHash to work.
you just need to declare your own QHash functionsnipped.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
andQSet
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
- snipped_main
-
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.