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 :)
Related
The following situation:
I have a class (Parent) in DLL A which has a signal (which is final). I have a class (Child) in DLL B that inherits from A. I import both DLLs to my project. Then I have a class in my project get an instance of the Child class and try to connect the signal of the Child instance to the current object. Somehow this connection fails. Running the whole thing in debug mode only gives me: QObject::connect: signal not found in Child. Checking the dumpbin shows me that the signal is in DLL A but not in DLL B. Both the class Parent and the class Child have the proper export statements but somehow the the signal for the Child class doesn't get exported. Here is the whole thing as code:
a.dll:
class DLLA_SHAREDEXPORT ParentInterface{
public:
~ParentInterface(){}
virtual void test() = 0;
};
Q_DECLARE_INTERFACE(ParentInterface, "ParentInterface")
class DLLA_SHAREDEXPORT Parent : public QObject,
public ParentInterface{
Q_OBJECT
Q_INTERFACES(ParentInterface)
...
signals:
void test();
}
b.dll:
class DLLB_SHAREDEXPORT Child : public Parent{
Q_OBJECT
...
}
class in project:
class SomeClass : public QObject{
Q_OBJECT
public:
...
void someFunction(){
Parent* c = getChildObject(); //some call to get the Object of class Child
QObject::connect(c, &Parent::test,
this, &SomeClass::handle_test);
//QObject::connect: signal not found in Child
}
}
public slots:
void handle_test(){
//do something
}
}
So the problem seems to be somewhere is the proper export of everything in the DLL B but I can't figure out how to do it. Any help would be appreciated.
I'm sorry, but I can't reproduce this issue on either Windows 10.0 with MSVC2015 and Qt 5.7 or OS X 10.10. Please try this project verbatim and see if it works for you. If it does, you're doing something else wrong and will need to produce a test case and edit it into the question.
The project is available from: https://github.com/KubaO/stackoverflown/tree/master/questions/sigslot-dll-39149263
sigslot-dll-39149263.pro
TEMPLATE = subdirs
SUBDIRS += lib1 lib2 main
main.depends += lib1 lib2
lib2.depends += lib1
lib1/lib1.pro
QT = core
CONFIG += c++11
TEMPLATE = lib
HEADERS += lib1.h
win32:DEFINES += LIB1_EXPORT=__declspec(dllexport)
lib1/lib1.h
#ifndef LIB1_H
#define LIB1_H
#include <QObject>
#ifdef WIN32
#ifndef LIB1_EXPORT
#define LIB1_EXPORT __declspec(dllimport)
#endif
#else
#define LIB1_EXPORT
#endif
class LIB1_EXPORT Parent : public QObject {
Q_OBJECT
public:
Q_SIGNAL void test();
};
#endif
lib2/lib2.pro
QT = core
CONFIG += c++11
TEMPLATE = lib
HEADERS += lib2.h
win32:DEFINES += LIB2_EXPORT=__declspec(dllexport)
win32:CONFIG(debug,release|debug) LIBSUBPATH=/debug
win32:CONFIG(release,release|debug) LIBSUBPATH=/release
LIBS += -L../lib1$$LIBSUBPATH -llib1
INCLUDEPATH += ..
DEPENDPATH += ..
lib2/lib2.h
#ifndef LIB2_H
#define LIB2_H
#include "lib1/lib1.h"
#ifdef WIN32
#ifndef LIB2_EXPORT
#define LIB2_EXPORT __declspec(dllimport)
#endif
#else
#define LIB2_EXPORT
#endif
class LIB2_EXPORT Child : public Parent {
Q_OBJECT
};
#endif
main/main.pro
QT = core
CONFIG += c++11
TEMPLATE = app
SOURCES += main.cpp
win32:CONFIG(debug,release|debug) LIBSUBPATH=/debug
win32:CONFIG(release,release|debug) LIBSUBPATH=/release
LIBS += -L../lib1$$LIBSUBPATH -llib1 -L../lib2$$LIBSUBPATH -llib2
INCLUDEPATH += ..
DEPENDPATH += ..
main/main.cpp
#include "lib1/lib1.h"
#include "lib2/lib2.h"
int main() {
int counter = 0;
Child child;
Parent * parent = &child;
QObject::connect(parent, &Parent::test, [&]{ counter++; });
emit parent->test();
emit parent->test();
Q_ASSERT(counter == 2);
}
So the issue seems to be somewhere with the usage of the Interface. We solved the issue for now by moving everything from the ParentInterface into the Parent class and removing the ParentInterface. This feels more like a workaround than a proper solution though.
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've read somewhere the Inversion of Control (IoC) is (kind of) a principle of a framework.
Is it correct to (taking advantage of that) say I designed a framework for X just because the IoC pattern is employed?
May I ask for a minimal framework example (C++ code please)?
Or instead, I can try to be more specific, providing some code (a complete Qt project) and asking if there is a framework there.
The class FrameworkForX (below, at the end) might be called a framework?
framework.pro
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = framework
TEMPLATE = app
SOURCES += main.cpp\
widget.cpp \
frameworkforx.cpp \
solvemyproblem.cpp
HEADERS += widget.h \
frameworkforx.h \
solvemyproblem.h
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
class QLineEdit;
class QLabel;
class SolveMyProblem;
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
private:
QLineEdit *lineEdit;
QLabel *label;
SolveMyProblem *solveMyProblem;
public slots:
void doit();
void onResult(int result);
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "QVBoxLayout"
#include "QPushButton"
#include "QLineEdit"
#include "QLabel"
#include "solvemyproblem.h"
Widget::Widget(QWidget *parent)
: QWidget(parent), solveMyProblem(new SolveMyProblem)
{
QVBoxLayout *vLayout = new QVBoxLayout(this);
lineEdit = new QLineEdit;
vLayout->addWidget(lineEdit);
QPushButton *bt = new QPushButton("Do It!");
connect(bt, &QPushButton::clicked, this, &Widget::doit);
vLayout->addWidget(bt, 1, Qt::AlignRight);
label = new QLabel("The result will be shown here.");
connect(solveMyProblem, &SolveMyProblem::result, this, &Widget::onResult);
vLayout->addWidget(label);
resize(400, 100);
setWindowTitle("Is it (FrameworkForX) a framework?");
}
Widget::~Widget()
{
}
void Widget::doit()
{
QString text = lineEdit->text();
solveMyProblem->doSomething(text);
}
void Widget::onResult(int result)
{
QString text = QString::number(result);
label->setText(text);
}
solvemyproblem.h
#ifndef SOLVEMYPROBLEM_H
#define SOLVEMYPROBLEM_H
#include "frameworkforx.h"
class SolveMyProblem : public FrameworkForX
{
public:
SolveMyProblem();
int doSpecificJob(const QString &text);
};
#endif // SOLVEMYPROBLEM_H
solvemyproblem.cpp
#include "solvemyproblem.h"
SolveMyProblem::SolveMyProblem()
{
}
int SolveMyProblem::doSpecificJob(const QString &text)
{
int i = text.size();
return i;
}
frameworkforx.h
#ifndef FRAMEWORKFORX_H
#define FRAMEWORKFORX_H
#include "QObject"
class QString;
class FrameworkForX : public QObject
{
Q_OBJECT
public:
FrameworkForX();
void doSomething(const QString &text);
protected:
virtual int doSpecificJob(const QString &text) = 0;
signals:
void result(int i);
};
#endif // FRAMEWORKFORX_H
frameworkforx.cpp
#include "frameworkforx.h"
FrameworkForX::FrameworkForX()
{
}
void FrameworkForX::doSomething(const QString &text)
{
int value = doSpecificJob(text);
emit result(value);
}
Your example is close to a framework. And it's close to IoC. But in my opinion, it is not the exact pattern because of this part:
Widget::Widget(QWidget*)
: ... solveMyProblem(new SolveMyProblem)
The Widget still has control over its strategy. It even constructs it. Therefore, there is no inversion. A proper IoC implementation would take the strategy e.g. as a parameter. Then, it makes only sense to require the base class or interface:
Widget::Widget(QWidget*, FrameworkForX* mySolver)
: ... solveMyProblem(mySolver)
//of course, the declaration of solveMyProblem has to be adapted
So far for the IoC part. Let's come to the framework part. A possible definition of a framework is the following:
A framework is a software component with extension points and variation points.
A software component could be anything. It could be a class, a library, a whole set of libraries... What IoC offers is a variation point. You can vary the functionality of a class (in this case of Widget) by supplying a custom implementation. You can plug that implementation into the framework to build a fully functional software component (whereas the framework is usually only a kind of skeleton).
So: Yes. In my opinion, every IoC implementation can be seen as a framework. In your concrete example, the framework would consist of Widget and FrameworkForX (where FrameworkForX could even be just an interface). SolveMyProblem is a custom implementation which is plugged into this framework.
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
I have one function declared in Foo class:
Q_INVOKABLE void setImageUrl(const QString &imageUrl);
However I cannot get the function index of that method:
Foo* foo = new Foo();
const QMetaObject* metaObject = foo->metaObject();
QString functionNameWithparameter("setImageUrl(QString)");
int functionIndex = metaObject->indexOfMethod(functionNameWithParameter.toStdString().c_str());
if (functionIndex >= 0) {
// never the case
}
What am I missing?
Apart from the two compiler errors, your approach seems to be correct. I assume that you had some changes that required to rerun moc, but you have not actually done so. This is the working code for me.
main.cpp
#include <QMetaObject>
#include <QDebug>
#include <QString>
class Foo : public QObject
{
Q_OBJECT
public:
explicit Foo(QObject *parent = Q_NULLPTR) : QObject(parent) {}
Q_INVOKABLE void setImageUrl(const QString &) {}
};
#include "main.moc"
int main()
{
Foo* foo = new Foo();
const QMetaObject* metaObject = foo->metaObject();
QString functionNameWithParameter("setImageUrl(QString)");
qDebug() << metaObject->indexOfMethod(functionNameWithParameter.toStdString().c_str());
return 0;
}
main.pro
TEMPLATE = app
TARGET = main
QT = core
CONFIG += c++11
SOURCES += main.cpp
Build and Run
qmake && make && ./main
Output
5