How do you export a QtWidget class from a library?
-
Does anybody know how to export a QtWidget class (for both WIndows and Linux)?
Like most people who write C++, I have some home grown macros that work for either Windows or Linux that allow me to export any class. (They use _declspec(dll[im,ex]port) in Windows, and equate to nothing in Linux). But they don't seem to work with Qt. I want to export a custom control in a library. It implements a MainWIndow::statusBar by combining all the individual controls; i.e., QLabel, QLineEdit, etc into one QWidget class. The class compiles but does not show up as an export when I look at the library with Depends. So the class is defined as:
class ZStatusBar: public QWidget
{
Q_OBJECT...
QLineEdit m_oThing1;
QLabel m_oThing2;
QPixmap m_oIcon1;
...
}Typically, in Windows one would use:
class AFX_EXT_CLASS ZStatusBar: public QWidget {
}where AFX_EXT_CLASS is a Microsoft macro that equates to _declspec(dllimport), or _declspec(dllexport) (depending on where the header is being included.
But this doesn't compile for me, so what does one do in Qt?
-
-
OK, I tried using the guidelines you pointed me to. (In fact I had already found them on my own, and had tried them.
So in a small header file dll_defns.h, I added these definitions:
#ifdef COMPILING_DLL_SOURCE
#define QTCLASS Q_DECL_EXPORT
#else
#define QTCLASS Q_DECL_IMPORT
#endifThen in my QtWidget class header I define my class using the QTCLASS macro
class QTCLASS ZStatusBar: public QWidget
{
Q_OBJECT
...
}Finally in the .pro file of the library I add the definition:
DEFINES += COMPILING_DLL_SOURCE
This is exactly what the export guidelines suggest, yet when I compile with these changes, I get many warnings, and 1 error:
The warnings all are of this type:
warning C4273: 'ZStatusBar::qt_static_metacall' : inconsistent dll linkageThe Error is:
debug\moc_public.cpp(146) : error C2491: 'ZStatusBar::staticMetaObject' : definition of dllimport static data member not
allowedIs there something else I'm missing?
-
Hi,
Did you do a full rebuild after adding the macro ?
-
@SGaist
I sure thought so, but I'll try again. Are you saying that the changes outlined above are adequate to export a class of this type? -
Yes it should.
I didn't realise, moc_public.cpp looks unusual. What part are you compiling ?
-
SGaist,
I put these definitions in dll_defns.h (I have changed some of the macro names to see if I've got some weird name conflict):
#ifdef COMPILING_QT_SOURCE
#define EXPORTQTCLASS Q_DECL_EXPORT
#else
#define EXPORTQTCLASS Q_DECL_IMPORT
#endifThen I include this header in my class definition header:
#include "dll_defns.h"
class EXPORTQTCLASS ZStatusBar: public QWidget
{
Q_OBJECT
...
}Finally, I put this define in my Library .pro file
DEFINES += COMPILING_QT_SOURCE
After wiping out the previous build files, and any previous output dll, the compile fails with the 1st error being:
D:\Year_2015\svn_nyfr_sw_1\SW\include\API_NYFR_Msg_ICD/public.hpp(75) : error C2470: 'ZStatusBar' : looks like a functio
n definition, but there is no parameter list; skipping apparent bodyBut if I remove EXPORTQTCLASS from the class definition, everything compiles and links. (But in that case, the class is not exported).
Is there perhaps some include required to define Q_DECL_{EX,IM}PORT? (Not that I'm seeing it complain about this)...
-
SGaist,
I put these definitions in dll_defns.h (I have changed some of the macro names to see if I've got some weird name conflict):
#ifdef COMPILING_QT_SOURCE
#define EXPORTQTCLASS Q_DECL_EXPORT
#else
#define EXPORTQTCLASS Q_DECL_IMPORT
#endifThen I include this header in my class definition header:
#include "dll_defns.h"
class EXPORTQTCLASS ZStatusBar: public QWidget
{
Q_OBJECT
...
}Finally, I put this define in my Library .pro file
DEFINES += COMPILING_QT_SOURCE
After wiping out the previous build files, and any previous output dll, the compile fails with the 1st error being:
D:\Year_2015\svn_nyfr_sw_1\SW\include\API_NYFR_Msg_ICD/public.hpp(75) : error C2470: 'ZStatusBar' : looks like a functio
n definition, but there is no parameter list; skipping apparent bodyBut if I remove EXPORTQTCLASS from the class definition, everything compiles and links. (But in that case, the class is not exported).
Is there perhaps some include required to define Q_DECL_{EX,IM}PORT? (Not that I'm seeing it complain about this)...
@Michael.R.LegakoL-3com.com said:
Is there perhaps some include required to define Q_DECL_{EX,IM}PORT? (Not that I'm seeing it complain about this)...
That's the downside of macros: If they're not defined, the compiler has absolutely no idea what they're meant to be.
Q_DECL_IMPORT
andQ_DECL_EXPORT
are defined in#include <QtGlobal>
: http://doc.qt.io/qt-5/qtglobal.html -
SGaist,
The class I'm compiling is a class that builds a statusbar for MainWindow::statusBar. It does have signals, and slots, and sets up a number of connect()'s to tie them together. Basically, when my app gets status messages, they get decoded into various things on the status bar the user needs to see; icons get selected for display, text is displayed, etc....So the class is a QWidget class that contains various Qt controls:
QLineEdit -- used for text output
QLabel -- used for icons
QPixmap -- used to hold the icon images
A timer is started to so I can display a clock with the current time in one of the text fields.Nothing particularly unusual. I've been using this status bar for some time in 3 separate GUI's, but I was also maintaining 3 separate sets of source code. The idea of creating a custom Widget to implement the status bar is basically my attempt to condense 3 sets of redundant code into one custom QWidget that works for all of them.
I confess I haven't had to understand the moc files since they have previously just worked....
Mike
-
sGaist,
I was missing the #include <QtGlobal> but adding it has not changed the result. I still getD:\Year_2015\svn_nyfr_sw_1\SW\include\API_NYFR_Msg_ICD/public.hpp(75) : error C2470: 'ZStatusBar' : looks like a functio
n definition, but there is no parameter list; skipping apparent bodyThe header skipping only fields of the same type is:
#ifndef ZSTATUSBAR_H
#define ZSTATUSBAR_H
#include <QtGlobal> /* Defines: Q_DECL_EXPORT, Q_DECL_IMPORT /
#include <QWidget>
#include <QStatusBar>
#include <QLineEdit>
#include <QLabel>
#include <QTimerEvent>
#include <API_NYFR_Msg_ICD/public_types.h> / Defines: StatusMsg_TYPE /
#include <Protos/common_macros.h> / Defines: LIN_SIZ */#include <Protos/dll_defns.h>
class EXPORTQTCLASS ZStatusBar: public QWidget
{
Q_OBJECT
public:
ZStatusBar(QWidget *parent=0);
virtual ~ZStatusBar();
int Create(QStatusBar *statusBar);void EmitValue(int item,int iSubItem,int iValue);
void EmitText(int item,char *sValue);
void EmitStatus(StatusMsg_TYPE Msg2,int bHeartBeat,int iRole);
void EmitInit(); / Used to put status bar status as 'unknown' */signals:
void updateStatusSignal(StatusMsg_TYPE *Msg2,int bHeartBeat,int iRole);
void updateValueSignal(int item,int iSubItem,int iValue);
void updateTextSignal(int item,char *sValue);
void initSignal(void);protected:
void timerEvent(QTimerEvent *event);private slots:
void updateValueSlot(int item, int iSubItem, int iValue);
void updateTextSlot(int item, char *sValue);
void updateStatusSlot(StatusMsg_TYPE *Msg2,int bHeartBeat,int iRole);
void initSlot();
private:
char *get_today(char *format1,char *sResult);
bool m_bCAC;
// Status Bar ContentQLabel m_oConnectivityIcon[8]; /* 8 LEDs showing system connectivity /
QLineEdit m_oNavTime;
/ PixMaps */
QPixmap m_oRC_Lock[2];int m_iNavTimerID;
char m_sPathFile[LIN_SIZ];
};#endif // ZSTATUSBAR_H
-
sGaist,
I was missing the #include <QtGlobal> but adding it has not changed the result. I still getD:\Year_2015\svn_nyfr_sw_1\SW\include\API_NYFR_Msg_ICD/public.hpp(75) : error C2470: 'ZStatusBar' : looks like a functio
n definition, but there is no parameter list; skipping apparent bodyThe header skipping only fields of the same type is:
#ifndef ZSTATUSBAR_H
#define ZSTATUSBAR_H
#include <QtGlobal> /* Defines: Q_DECL_EXPORT, Q_DECL_IMPORT /
#include <QWidget>
#include <QStatusBar>
#include <QLineEdit>
#include <QLabel>
#include <QTimerEvent>
#include <API_NYFR_Msg_ICD/public_types.h> / Defines: StatusMsg_TYPE /
#include <Protos/common_macros.h> / Defines: LIN_SIZ */#include <Protos/dll_defns.h>
class EXPORTQTCLASS ZStatusBar: public QWidget
{
Q_OBJECT
public:
ZStatusBar(QWidget *parent=0);
virtual ~ZStatusBar();
int Create(QStatusBar *statusBar);void EmitValue(int item,int iSubItem,int iValue);
void EmitText(int item,char *sValue);
void EmitStatus(StatusMsg_TYPE Msg2,int bHeartBeat,int iRole);
void EmitInit(); / Used to put status bar status as 'unknown' */signals:
void updateStatusSignal(StatusMsg_TYPE *Msg2,int bHeartBeat,int iRole);
void updateValueSignal(int item,int iSubItem,int iValue);
void updateTextSignal(int item,char *sValue);
void initSignal(void);protected:
void timerEvent(QTimerEvent *event);private slots:
void updateValueSlot(int item, int iSubItem, int iValue);
void updateTextSlot(int item, char *sValue);
void updateStatusSlot(StatusMsg_TYPE *Msg2,int bHeartBeat,int iRole);
void initSlot();
private:
char *get_today(char *format1,char *sResult);
bool m_bCAC;
// Status Bar ContentQLabel m_oConnectivityIcon[8]; /* 8 LEDs showing system connectivity /
QLineEdit m_oNavTime;
/ PixMaps */
QPixmap m_oRC_Lock[2];int m_iNavTimerID;
char m_sPathFile[LIN_SIZ];
};#endif // ZSTATUSBAR_H
@Michael.R.LegakoL-3com.com said:
D:\Year_2015\svn_nyfr_sw_1\SW\include\API_NYFR_Msg_ICD/public.hpp(75) : error C2470: 'ZStatusBar' : looks like a functio
n definition, but there is no parameter list; skipping apparent bodyWhat's at line 75?
Usually, that message means the compiler saw "
class EXPORTQTCLASS ZStatusBar: public QWidget
" but doesn't understand whatEXPORTQTCLASS
is. So, the compiler thought thatZStatusBar
is a function that returnsclass EXPORTQTCLASS
, and so it looks for the parameter list after the "function" name (e.g. "ZStatusBar(int param1, const QString& param2)
"). However, it couldn't find a parameter list, so it skips everything that comes after that.In short, the compiler is confused because your
EXPORTQTCLASS
doesn't eventually expand to_declspec(dllexport)
.Are you using Qt Creator? If so, hover your mouse cursor over
EXPORTQTCLASS
and see what it expands to. Also, try replacing all instances ofEXPORTQTCLASS
withQ_DECL_EXPORT
and see what happens. (Again, hover your mouse cursor overQ_DECL_EXPORT
and see what it expands to) -
@Michael.R.LegakoL-3com.com said:
D:\Year_2015\svn_nyfr_sw_1\SW\include\API_NYFR_Msg_ICD/public.hpp(75) : error C2470: 'ZStatusBar' : looks like a functio
n definition, but there is no parameter list; skipping apparent bodyWhat's at line 75?
Usually, that message means the compiler saw "
class EXPORTQTCLASS ZStatusBar: public QWidget
" but doesn't understand whatEXPORTQTCLASS
is. So, the compiler thought thatZStatusBar
is a function that returnsclass EXPORTQTCLASS
, and so it looks for the parameter list after the "function" name (e.g. "ZStatusBar(int param1, const QString& param2)
"). However, it couldn't find a parameter list, so it skips everything that comes after that.In short, the compiler is confused because your
EXPORTQTCLASS
doesn't eventually expand to_declspec(dllexport)
.Are you using Qt Creator? If so, hover your mouse cursor over
EXPORTQTCLASS
and see what it expands to. Also, try replacing all instances ofEXPORTQTCLASS
withQ_DECL_EXPORT
and see what happens. (Again, hover your mouse cursor overQ_DECL_EXPORT
and see what it expands to)@JKSH
I think you were right. The macro I was using to export with (EXPORTQTCLASS) is somehow not getting equated to Q_DECL_EXPORT. Replacing the class definition:class EXPORTQTCLASS ZStatusBar: public QWidget
with
class Q_DECL_EXPORT ZStatusBar: public QWidget
solves the issue and everything compiles and links. Of course that won't work when I include that header in the application that needs to use ZStatusBar, but that does narrow the issue down to figuring out why the macro is not assigned the correct value.
Thanks! Essentially I think this solves the issue...
Mike
-
@JKSH
I think you were right. The macro I was using to export with (EXPORTQTCLASS) is somehow not getting equated to Q_DECL_EXPORT. Replacing the class definition:class EXPORTQTCLASS ZStatusBar: public QWidget
with
class Q_DECL_EXPORT ZStatusBar: public QWidget
solves the issue and everything compiles and links. Of course that won't work when I include that header in the application that needs to use ZStatusBar, but that does narrow the issue down to figuring out why the macro is not assigned the correct value.
Thanks! Essentially I think this solves the issue...
Mike
OK, now I think I've isolated why the macro Q_DECL_EXPORT never gets assigned. It was never defined. SGaist suggested that the macros QDECL_EXPORT, and Q_DECL_IMPORT are given values by <QtGlobal>, but I have discovered that these two macros are actually assigned within qcompilerdetection.h. Maybe QtGlobal is supposed to include something that eventually is supposed to include qcompilerdetection.h??? The path to qcompilerdetection.h is C:/Qt/Qt5.2.0/5.2.0/msvc2012_64/include/QtCore/qcompilerdetection.h. I would think the file would be included using
#include <QtCore/qcompilerdetection.h>
But this doesn't seem to work. I think getting the two macros defined somehow is the issue. On the machine I'm on, MSVS2012 is the compiler, the machine is 64 bits, and 5.2.0 is the version of Qt in use.
-
OK, now I think I've isolated why the macro Q_DECL_EXPORT never gets assigned. It was never defined. SGaist suggested that the macros QDECL_EXPORT, and Q_DECL_IMPORT are given values by <QtGlobal>, but I have discovered that these two macros are actually assigned within qcompilerdetection.h. Maybe QtGlobal is supposed to include something that eventually is supposed to include qcompilerdetection.h??? The path to qcompilerdetection.h is C:/Qt/Qt5.2.0/5.2.0/msvc2012_64/include/QtCore/qcompilerdetection.h. I would think the file would be included using
#include <QtCore/qcompilerdetection.h>
But this doesn't seem to work. I think getting the two macros defined somehow is the issue. On the machine I'm on, MSVS2012 is the compiler, the machine is 64 bits, and 5.2.0 is the version of Qt in use.
@JKSH, and @SGaist ,
Thanks both of you. The problem wasn't your advice. <QtGlobal> DOES define Q_DECL_{EX,IM}PORT (both of them). The real issue was a fairly subtle mistake on my part. I was using the macro COMPILING_Q_LIB to switch between these two macros, but in my .pro there was a DEFINE statement using = following the DEFINE for COMPILING_Q_LIB (which also used the = operator). So the 2nd define statement wiped out all previous defines including the one for COMPILING_Q_LIB.(It would be a good thing to have the compiler detect when more than one DEFINE statement is using the = operator. I'm sure quite a number of the really subtle issues you are seeing are because of that one thing!)
-
@JKSH, and @SGaist ,
Thanks both of you. The problem wasn't your advice. <QtGlobal> DOES define Q_DECL_{EX,IM}PORT (both of them). The real issue was a fairly subtle mistake on my part. I was using the macro COMPILING_Q_LIB to switch between these two macros, but in my .pro there was a DEFINE statement using = following the DEFINE for COMPILING_Q_LIB (which also used the = operator). So the 2nd define statement wiped out all previous defines including the one for COMPILING_Q_LIB.(It would be a good thing to have the compiler detect when more than one DEFINE statement is using the = operator. I'm sure quite a number of the really subtle issues you are seeing are because of that one thing!)
Apparently my odyssey isn't over yet. Although I was able to get my QWidget to compile and link, and although depends does show that the object is exported, now I'm seeing an entirely new kind of issue when I try to link the new QWidget object in with an application. In the compiler output of the application, I now get the error:
debug\moc_public.cpp(148) : error C2491: 'ZStatusBar::staticMetaObject' : definition of dllimport static data member not allowed
And sure enough, looking at the depends exports I see the static meta object in the export list. In fact there is both a qt_static_metacall, and a staticMetaObject although the above error seems to point just to the latter.
Since I don't directly generate the files that create these things, how do I prevent them from being included when the object is exported?
-
I haven't got bitten by that one while building Qt based shared libraries. Can you try to make a small minimal library to see if it's still happening ?
-
Apparently my odyssey isn't over yet. Although I was able to get my QWidget to compile and link, and although depends does show that the object is exported, now I'm seeing an entirely new kind of issue when I try to link the new QWidget object in with an application. In the compiler output of the application, I now get the error:
debug\moc_public.cpp(148) : error C2491: 'ZStatusBar::staticMetaObject' : definition of dllimport static data member not allowed
And sure enough, looking at the depends exports I see the static meta object in the export list. In fact there is both a qt_static_metacall, and a staticMetaObject although the above error seems to point just to the latter.
Since I don't directly generate the files that create these things, how do I prevent them from being included when the object is exported?
@JKSH said:
debug\moc_public.cpp(148) : error C2491: 'ZStatusBar::staticMetaObject' : definition of dllimport static data member not allowed
The project that uses ZStatusBar should treat its header as an external file, and add the header to
INCLUDEPATH
. That project should not contain a copy of the header (in other words, that project should not have "HEADERS += public.h
").Think of it this way: You #include qwidget.h, but you don't copy qwidget.h into your project. You should treat your ZStatusBar header in the same manner.
If your project contains a copy of the header, the meta-object compiler will try to generate code for the QObjects in that header. However, this is illegal because the generated code already exists in your library.
-
@JKSH said:
debug\moc_public.cpp(148) : error C2491: 'ZStatusBar::staticMetaObject' : definition of dllimport static data member not allowed
The project that uses ZStatusBar should treat its header as an external file, and add the header to
INCLUDEPATH
. That project should not contain a copy of the header (in other words, that project should not have "HEADERS += public.h
").Think of it this way: You #include qwidget.h, but you don't copy qwidget.h into your project. You should treat your ZStatusBar header in the same manner.
If your project contains a copy of the header, the meta-object compiler will try to generate code for the QObjects in that header. However, this is illegal because the generated code already exists in your library.
@JKSH ,
Direct hit! Score 1 for JKSH.I guess in retrospect your suggestion is obvious since a class header is more than just prototypes, but can also contain implementations. Removing the ZStatusBar header from HEADERS yet making sure the path to the header is in INCLUDEPATH did the trick. Good Catch!
At this point, both the ZStatusBar library, and the application that uses it are compiling and linking, so this is pretty good evidence that the original issue of this thread has been solved.
Mike