I am working on a big project where I am trying to load dynamically a shared library with QLibrary, I was able to reproduce the run-time link error (undefined symbol: staticMetaObject) in the following example:
Folder structure:
root\
-- src\
---- TestLib\
------ TestLib.pro
------ Derived.h
------ Derived.cpp
------ TestLibModuleBridge.h
------ TestLibModuleBridge.cpp
---- TestLibCommon\
------ IBase.h
---- TestLibManager\
------ TestLibManager.pro
------ main.cpp
--lib\
TestLib files:
# TestLib.pro
QT -= gui
TARGET = TestLib
TEMPLATE = lib
QMAKE_CXXFLAGS += -Wall
DEFINES += TESTLIB_LIBRARY
SOURCES += Derived.cpp \
TestLibModuleBridge.cpp
HEADERS += Derived.h \
TestLibModuleBridge.h
INCLUDEPATH += ../TestLibCommon
unix {
target.path = ../../lib
INSTALLS += target
}
-
// Derived.h
#ifndef DERIVED_H
#define DERIVED_H
#include "IBase.h"
#include <iostream>
class Derived : public IBase
{
Q_OBJECT
public:
Derived();
virtual ~Derived();
public:
virtual void methodA();
virtual void methodB();
};
#endif // DERIVED_H
-
// Derived.cpp
#include "Derived.h"
Derived::Derived()
{
}
Derived::~Derived()
{
}
void Derived::methodA()
{
std::cout << "methodA()" << std::endl;
}
void Derived::methodB()
{
std::cout << "methodB()" << std::endl;
}
-
// TestLibModuleBridge.h
#ifndef TESTLIBMODULEBRIDGE_H
#define TESTLIBMODULEBRIDGE_H
#include "IBase.h"
#ifdef __cplusplus
extern "C" {
#endif
IBase* getModuleInterface();
#ifdef __cplusplus
}
#endif
#endif // TESTLIBMODULEBRIDGE_H
-
// TestLibModuleBridge.cpp
#include "TestLibModuleBridge.h"
#include "Derived.h"
IBase* getModuleInterface()
{
return new Derived();
}
TestLibManager Files:
// TestLibManager.pro
QT += core
QT -= gui
TARGET = TestLibManager
CONFIG += console
CONFIG -= app_bundle
QMAKE_CXXFLAGS += -Wall
TEMPLATE = app
SOURCES += main.cpp
INCLUDEPATH += ../TestLibCommon
-
// main.cpp
#include <QCoreApplication>
#include <QLibrary>
#include <QDebug>
#include "IBase.h"
typedef IBase* (*ModuleGetterFunction) (void);
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QLibrary testLib("../../lib/libTestLib.so");
if (!testLib.load())
{
qDebug() << "Error : " << testLib.errorString();
exit(EXIT_FAILURE);
}
else
{
ModuleGetterFunction getModuleInterfaceFunc = (ModuleGetterFunction) testLib.resolve("getModuleInterface");
if (getModuleInterfaceFunc)
{
IBase* obj = getModuleInterfaceFunc();
obj->methodA();
obj->methodB();
}
}
return a.exec();
}
TestLibCommon Files
// IBase.h
#ifndef IBASE_H
#define IBASE_H
#include <QObject>
class IBase : public QObject
{
Q_OBJECT
protected:
virtual ~IBase() {}
public:
virtual void methodA() = 0;
virtual void methodB() = 0;
};
#endif // IBASE_H
The testLib.load() fails with Error : "Cannot load library ../../lib/libTestLib.so: (../../lib/libTestLib.so: undefined symbol: _ZN5IBase16staticMetaObjectE)"
How can I fix this, removing Q_OBJECT macro from IBase.h will fix the error but in the production project that interface contains signals and slots, and it is from a project we're not allowed to change.
It looks like moc has not been run before compiling the testlib.
moc is responsible for generating the staticMetaObject for classes which contain the Q_OBJECT Macro and are listed under the HEADERS section in the pro file.
Adding TestLibCommon\IBase.h to the HEADERS in TestLib.pro should probably fix this problem. (Untested).
Possible improvement of your solution:
Instead of using QLibrary take a look at QPluginLoader.
QPluginLoader will give you a QObject which you can then cast to any interface you want using qobject_cast<T*>(pluginloader.instance())
Here's an example using QPluginLoader:
http://doc.qt.io/qt-5/qtwidgets-tools-plugandpaint-example.html
Related
I need to capture emited signals from a QProcess for testing purposes.
Since I am using a console application, I resolved to create a class in my main.cpp file called myObj using mainly this example:
#include <QCoreApplication>
#include <QLoggingCategory>
#include <QTextStream>
#include <QProcess>
#include <QString>
#include <QVariant>
#include <QDebug>
#include <QObject>
class myObj : public QObject
{
Q_OBJECT
public:
myObj(QObject *parent = 0);
// virtual ~Communicate();
~myObj();
public slots:
void registerFinished(int signal);
void registerAboutToClose();
void registerChannelReadyRead(int signal);
void registerReadChannelFinished();
void registerReadyRead();
void registerReadyReadStandardOutput();
void registerStarted();
};
myObj::myObj(QObject *parent)
: QObject(parent) <--- LINE 72 Error
{
}
//virtual myObj::~Communicate(){
//}
myObj::~myObj(){ <--- LINE 81 Error
}
void myObj::registerFinished(int signal){
qDebug() << "exit code = " << QString::number(signal);
}
void myObj::registerAboutToClose(){
qDebug() << "aboutToClose";
}
void myObj::registerChannelReadyRead(int signal){
qDebug() << "channelReadyRead = " << QString::number(signal);
}
void myObj::registerReadChannelFinished(){
qDebug() << "readChannelFinished";
}
void myObj::registerReadyRead(){
qDebug() << "exit code";
}
void myObj::registerReadyReadStandardOutput(){
qDebug() << "exit code";
}
void myObj::registerStarted(){
qDebug() << "started";
}
myObj *myO;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
myO = new myObj();
//....
}
Problem:
main.cpp:72: error: undefined reference to `vtable for myObj'
main.cpp:81: error: undefined reference to `vtable for myObj'
I have looked at a number of SO pages e.g here and here and here and various others, yet had not found a solution
I have tried/done:
added the Q_Object Macro
ran qmake
rebuilt
checked the #include
.pro file
QT += core
QT -= gui
CONFIG += c++11
TARGET = serv_app
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += main.cpp
Any suggestions?
You have two options:
#eyllanesc solution works for sure.
add the line #include "main.moc" before or after you main() function.
When you put the class into its own header file, qmake will generate the proper moc file.
But when you put the class into a .cpp file, the moc code is not generated unless you put the line I said before.
Update #1
In the Qt tutorial about writing a Unit Test we can find the following info:
Note that if both the declaration and the implementation of our test
class are in a .cpp file, we also need to include the generated moc
file to make Qt's introspection work.
So this is another example where we need to include the moc file.
I have linking problems by creating a Qt C++ plugin to extend the functionality of my application. The code compiles and everithing works, but only as long as I use the Qt library classes, like QString for example. The moment I give to the plugin's class a reference to an object from the classes defined in the host app, the project is not linked anymore. Creating the plugin I follow the procedure from the Qt documentation and take into consideration the given examples - echo and plug&paint. However, such case is not covered there.
update
Here is the error:
myPlugin.obj:-1: error: LNK2019: unresolved external symbol "public: class QString __cdecl myClass::answer(void)const " (?answer#myClass##QEBA?AVQString##XZ) referenced in function "public: virtual class QString __cdecl myPlugin::echo(class myClass *)" (?echo#myPlugin##UEAA?AVQString##PEAVmyClass###Z)
And here is the project that causes it:
plugtest.pro
TEMPLATE = subdirs
SUBDIRS += \
host \
plugin
host.pro
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = host
TEMPLATE = app
SOURCES += main.cpp\
MainWindow.cpp \
myClass.cpp
HEADERS += MainWindow.h \
myClass.h \
plugInterface.h
FORMS += MainWindow.ui
MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QMessageBox>
#include <QPluginLoader>
#include <QDir>
#include "myClass.h"
#include "plugInterface.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_button_clicked();
private:
bool loadPlugin();
Ui::MainWindow *ui;
plugInterface *m_interface;
myClass *m_class;
};
#endif // MAINWINDOW_H
myClass.h
#ifndef MYCLASS_H
#define MYCLASS_H
#include <QObject>
class myClass : public QObject
{
Q_OBJECT
public:
explicit myClass(QObject *parent = 0);
QString answer() const;
void setAnswer(const QString &str);
private:
QString m_answer;
};
#endif // MYCLASS_H
plugInterface.h
#ifndef PLUGINTERFACE_H
#define PLUGINTERFACE_H
#include <QString>
#include "myClass.h"
class plugInterface
{
public:
virtual ~plugInterface() {}
virtual QString echo(myClass *value) = 0;
};
#define PlugInterface_iid "example.suite.app.PluginInterface"
Q_DECLARE_INTERFACE(plugInterface, PlugInterface_iid)
#endif // PLUGINTERFACE_H
MainWindow.cpp
#include "MainWindow.h"
#include "ui_MainWindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
if (!loadPlugin())
{
QMessageBox::information(this, "Error", "Could not load the plugin");
}
m_class = new myClass(this);
m_class->setAnswer("Good!");
}
MainWindow::~MainWindow()
{
delete ui;
}
bool MainWindow::loadPlugin()
{
QDir pluginsDir(qApp->applicationDirPath());
if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release")
pluginsDir.cd("plugins");
foreach (QString fileName, pluginsDir.entryList(QDir::Files))
{
QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
QObject *plugin = pluginLoader.instance();
if (plugin) {
m_interface = qobject_cast<plugInterface *>(plugin);
if (m_interface)
return true;
}
}
return false;
}
void MainWindow::on_button_clicked()
{
ui->lineResult->setText(m_interface->echo(m_class));
}
myClass.cpp
#include "myClass.h"
myClass::myClass(QObject *parent) : QObject(parent)
{
}
QString myClass::answer() const
{
return m_answer;
}
void myClass::setAnswer(const QString &str)
{
m_answer = str;
}
plugin.pro
TEMPLATE = lib
CONFIG += plugin
QT += widgets
INCLUDEPATH += ../host
HEADERS = myPlugin.h
SOURCES = myPlugin.cpp
TARGET = $$qtLibraryTarget(myPlugin)
DESTDIR = ../plugins
myPlugin.h
#ifndef MYPLUGIN_H
#define MYPLUGIN_H
#include <QObject>
#include "plugInterface.h"
class myPlugin : public QObject, plugInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "example.suite.app.PluginInterface")
Q_INTERFACES(plugInterface)
public:
QString echo(myClass *value) Q_DECL_OVERRIDE;
};
#endif // MYPLUGIN_H
myPlugin.cpp
#include "myPlugin.h"
QString myPlugin::echo(myClass *value)
{
return value->answer();
}
The solution that worked for me is to make the myClass a part of a library and include it dynamically in both the plug-in and the host. However, I am curious if this could be done without creating a library.
I have a project in Qt, with a class defined of the form
Project MyP --- file p.h
#ifndef P_H
#define P_H
#include <QImage>
class PP
{
public:
static QImage P1(const QImage& a);
}
#endif
Project MyP --- file p.cpp
#include "p.h"
QImage PP::P1(const QImage& a)
{
QImage b;
return b;
}
I created a test project for the class above
project test, file test.h
#ifndef TEST_H
#define TEST_H
#include <QtTest/QtTest>
class Test : public QObject
{
Q_OBJECT
private slots:
void TestSomething();
};
#endif
#include "test.h"
#include "p.h"
void Test::TestSomething()
{
QImage sourceImage = QImage("source.png");
QImage newImage = PP::P1(sourceImage);
int result = 1*2;
QVERIFY(result == 2);
}
QTEST_MAIN(Test)
doing the same thing in c++ works but there I can easily add a lib dependency)... but in Qt i get an error
error: undefined reference to `_imp___ZN6PPP1ENS_12P1MethodERK6QImage'
I tried to add library dependency like i saw here
http://qt-project.org/wiki/How_to_create_a_library_with_Qt_and_use_it_in_an_application
I added in the MyP header file
#if defined MYP_LIB
#define MYP_COMMON_DLLSPEC Q_DECL_EXPORT
#else
#define MYP_COMMON_DLLSPEC Q_DECL_IMPORT
#endif
and changed the class declaration to
class MYP_COMMON_DLLSPEC Dither
{
....
}
I added in the pro file ( I will post the entire file for ease)
TEMPLATE = lib
VERSION = 1.0.0.0
CONFIG += staticlib debug
debug {
DESTDIR = bin/debug
}
release {
DESTDIR = bin/release
}
HEADERS += \
pp.h
SOURCES += \
pp.cpp
DEFINES += MYP_LIB
then in the test project, I added in the pro file
DEPENDPATH += . ../myp
INCLUDEPATH += ../myp
win32:LIBS += ../myp/bin/debug/libmyp.a
LIBS+= -L../myp/bin/debug -lmyp
I have been unable to create a lib file, or a dll file, and have been unable to get the projects to work together. Note: they are all in the same solution.
How can I properly run the test from a separate project ?
You have to tell the linker to import and export (respectively) a symbol from or to a DLL, so you have to declare your class like:
Project MyP --- file p.h
#ifndef P_H
#define P_H
#if defined(MyP_LIBRARY)
# define MyPSHARED_EXPORT Q_DECL_EXPORT
#else
# define MyPSHARED_EXPORT Q_DECL_IMPORT
#endif
#include <QImage>
MyPSHARED_EXPORT class PP
{
public:
...
And add
DEFINES += MyP_LIBRARY
to your MyP.pro file
more information at
http://qt-project.org/doc/qt-4.8/sharedlibrary.html
i ‘m trying to create a plugin(not for Qt creator),
i created an empty project and added the following files :
but i’m getting the following errors:
1.
C:\Qt\Qt5.2.0\5.2.0\mingw48_32\include\QtCore\qglobal.h:666: erreur : invalid application of ‘sizeof’ to incomplete type ‘QStaticAssertFailure’ enum {Q_STATIC_ASSERT_PRIVATE_JOIN(q_static_assert_result, COUNTER) = sizeof(QStaticAssertFailure)} ^
2.
D:\MyFiles\Projects\QtProjects\pluginTest2\plugintest.cpp:9: erreur : expected initializer before ‘PluginTest’ QString PluginTest::name() const ^
PluginTest2.pro (project name)
CONFIG += plugin
TARGET = PluginTest
CONFIG += plugin release
VERSION =1.0.0
TEMPLATE = lib
SOURCES += \
plugintest.cpp
HEADERS += \
Interface.h \
plugintest.h
interface.h
#ifndef INTERFACE_H
#define INTERFACE_H
#include <QString>
class Interface
{
public:
virtual QString name() const =0;
};
Q_DECLARE_INTERFACE(Interface,"interface /1.0.0")
#endif // INTERFACE_H
plugintest.h
#ifndef PLUGINTEST_H
#define PLUGINTEST_H
#include <QObject>
#include <QString>
#include<QtPlugin>
#include "Interface.h"
class PluginTest:public QObject,public Interface
{
Q_OBJECT
Q_INTERFACES(Interface)
public:
PluginTest();
QString name() const;
};
#endif // PLUGINTEST_H
plugintest.cpp
#include "plugintest.h"
PluginTest::PluginTest()
{
}
Q_EXPORT_PLUGIN2(PluginTest,PluginTest)
QString PluginTest::name() const
{
return "pluginTest";
}
The problem is this line:
Q_EXPORT_PLUGIN2(PluginTest,PluginTest)
This is a Qt 4 feature, so you would need to either remove it, or put behind a version checking macro as follows:
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
Q_EXPORT_PLUGIN2(PluginTest,PluginTest)
#endif
For completeness, it seems you do not specify the meta data for the plugin. You would need to add that either unconditionally or conditionally with a version checking macro if you wish to support both Qt 4 and Qt 5 as follows:
class PluginTest:public QObject,public Interface
{
Q_OBJECT
Q_INTERFACES(Interface)
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
Q_PLUGIN_METADATA(IID "your-string-here" FILE "file-here-but-can-be-empty")
#endif
...
};
Also, note that you append the "plugin" CONFIG item twice in your project file.
I am starting to be very nervous with my problem, I will try to desribe my need, hope that somebody understands.
Imagine that I have project which generates one executable and some plugins( runtime loaded library ). That executable is some kind of daemon/service which looks for ist suitable plugins at the start. So these plugins should provide some abstract communication layer.
I have looked at the Qt Doc and found QPluginLoader class which should provide interface loading from .so, but the interface cannot have signal/slot, must be pure virtual. So I was thinking about somethink which will return QObject based objects...
!!! Please dont get scared, its just 2 interfaces and 2 implementations :)
My Project layout and contents:
./Daemon/Interfaces/PluginInterface.h
#include <QObject>
#include "PluginInterface.h"
class PluginInterface {
public:
virtual ~PluginInterface() = 0;
virtual ProtocolInterface* getInterface() = 0;
virtual int getPluginId() const = 0;
};
Q_DECLARE_INTERFACE( PluginInterface, "com.porta.protocol.PluginInterface/1.0")
./Daemon/Interfaces/ProtocolInterface.h
#include <QObject>
#include "turnstile.h"
class ProtocolInterface : public QObject {
Q_OBJECT
public:
ProtocolInterface( QObject *parent = 0 ) : QObject( parent ) {}
virtual QWidget* getConfigureGUI() = 0;
virtual void init() = 0;
virtual void start() = 0;
signals:
void someSignal();
};
./Daemon/ProtocolHander.cpp(&h) <- just plugin loading and some logic
./Daemon.pro
QT += core gui
TARGET = porta_daemon
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += main.cpp \
protocolhandler.cpp
HEADERS += protocolhandler.h \
Interfaces/protocolinterface.h \
Interfaces/protocolloader.h \
Interfaces/turnstile.h
./Plugins/Dummy/DummyPluginInterface.h
#include "protocolloader.h"
#include <QObject>
class DummyPluginInterface : public QObject, PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
public:
ProtocolInterface* getInterface();
int getPluginId() const;
};
./Plugins/Dummy/DummyPluginInterface.cpp
#include "DummyPluginInterface.h"
#include "DummyProtocolInterface.h"
ProtocolInterface *DummyPluginInterface::getInterface() {
return new DummyProtocolInterface();
}
int DummyPluginInterface::getPluginId() const {
return 1;
}
Q_EXPORT_PLUGIN2(dummyplugin, DummyPluginInterface)
./Plugins/Dummy/DummyProtocolInterface.h
#include "protocolinterface.h"
#include <QObject>
class DummyProtocolInterface : public ProtocolInterface {
public:
void init();
QWidget* getConfigureGUI();
void start();
int getPluginId() { return 1; }
};
./Plugins/Dummy/DummyProtocolInterface.cpp
#include "DummyProtocolInterface.h"
QWidget* DummyProtocolInterface::getConfigureGUI() {
return 0;
}
void DummyProtocolInterface::start() {
}
void DummyProtocolInterface::init() {
emit someSignal(); /// !!! this is important for me
}
./Plugins/Dummy/Dummy.pro
TEMPLATE = lib
CONFIG += plugin
QT += network
INCLUDEPATH += ../../Daemon/Interfaces/
HEADERS += ****
SOURCES += ****
TARGET = *****
DESTDIR = *****
My promblem is that I am getting linking errors or runtime unresolved symbols( mostly somethink from QObject ) or my signals cannot by connected... ProtocolHandler should be the one, who connects signals/slots..
Can anybody tell me how to make this approach right? Qt examples are not covering such think..
THANK YOU!
Adam
So adding ProtocolInterface.h into HEADERS += section of Plugin.pro file SOLVED THE PROBLEM :)