PDA

View Full Version : Plugin interface with slots and signals



folibis
9th September 2013, 10:15
Now I work on application with plugins.
So I have one interface and lots of plugins based on it:


class PluginWidgetInterface
{
public:
virtual ~PluginWidgetInterface() {}
virtual QWidget * GetWidget() = 0;
//---------------------------- SIGNALS ---------------------------
virtual void MessageSend() = 0;
};

#define PluginWidgetInterface_iid "myplugins.PluginWidgetInterface"
Q_DECLARE_INTERFACE(PluginWidgetInterface, PluginWidgetInterface_iid)


and plugin implementation:

#include "plugininterface.h"

class Plugin1 : public QObject,PluginWidgetInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "myplugins.Plugin1")
Q_INTERFACES(PluginWidgetInterface)
public:
~Plugin1() {}
QWidget * GetWidget();
//---------------------------- SIGNALS ---------------------------
void MessageSend();
};

but at this point i have troubles with connect(). Because the Interface class not derived from QObject I cant use it with connect();
If i cast it to QObject i get 0x0.


QPluginLoader loader("plugin.dll");
PluginWidgetInterface * plugin = qobject_cast<PluginWidgetInterface *>(loader.instance());
QWidget * widget = plugin->GetWidget();
QObject * o = dynamic_cast<QObject *>(plugin); // here o = NULL
connect(o,SIGNAL(MessageSend),this,SLOT(MessageRec eived));

I would like to do it in new style

connect(o,&PluginWidgetInterface::MessageSend,this,&MainWindow::MessageReceived);
but i got error:
no matching function for call to 'MainWindow::connect(QObject*&, void (PluginWidgetInterface::*)(), MainWindow* const, void (MainWindow::*)())


So what is right way to define interface with signals to connect inheritance to slots?

anda_skoa
9th September 2013, 13:16
Some things:
is the private inheritance of the interface class by accident?
you plugin implementation is missing the signals: section.
does GetWidget() return a valid widget pointer?
if yes, why don't you use that in the connect?

Cheers,
_

mcarter
9th September 2013, 13:32
Since you are requiring that all of the plugins have a MessageSend signal, just derived your initial plugin interface from QObject and include the signal there

class PluginWidgetInterface : public QObject
{
Q_OBJECT
public:
virtual ~PluginWidgetInterface() {}
virtual QWidget * GetWidget() = 0;
signals:
void MessageSend();
};

You will still need to use Q_OBJECT in your derived classes

class Plugin1 : public PluginWidgetInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "myplugins.Plugin1")
Q_INTERFACES(PluginWidgetInterface)
public:
~Plugin1() {}
QWidget * GetWidget();
};

folibis
9th September 2013, 22:45
Since you are requiring that all of the plugins have a MessageSend signal, just derived your initial plugin interface from QObject and include the signal there

Ok, I derived interface class from QObject. Now, when I compile plugin dll I get lots of errors like:
error: undefined reference to `PluginWidgetInterface::qt_metacast(char const*)

I guess I have to put interface class in different library and link in statically with all plugins dlls but it looks too complicated

mcarter
10th September 2013, 04:15
As long as you are not linking against a statically built Qt then everythign should work fine.
The following code builds plugin lib in linux, and I think should build proper dll in windows


#ifndef _PLUGIN_WIDGET_INTERFACE_H_
#define _PLUGIN_WIDGET_INTERFACE_H_

#include <QtPlugin>
#include <QObject>

class PluginWidgetInterface : public QObject
{
Q_OBJECT
public:
virtual ~PluginWidgetInterface() {}
virtual QWidget * GetWidget() = 0;
signals:
void MessageSend();
};

#define PluginWidgetInterface_iid "myplugins.PluginWidgetInterface"
Q_DECLARE_INTERFACE(PluginWidgetInterface, PluginWidgetInterface_iid)

#endif


#ifndef _PLUGIN1_H_
#define _PLUGIN1_H_

#include "plugininterface.h"

class Plugin1 : public PluginWidgetInterface
{
Q_OBJECT
//Q_PLUGIN_METADATA(IID "myplugins.Plugin1")
Q_INTERFACES(PluginWidgetInterface)
public:
~Plugin1() {}
QWidget * GetWidget();
};

#endif

#include "plugin1.h"

QWidget * Plugin1::GetWidget()
{
return 0;
}

Q_EXPORT_PLUGIN2(plugin1, Plugin1);

TEMPLATE = lib
TARGET =
DEPENDPATH += .
INCLUDEPATH += .

# Input
HEADERS += plugin1.h plugininterface.h
SOURCES += plugin1.cpp

Can then be used with


QPluginLoader loader( qstrLibFilename );
PluginWidgetInterface * plugin = qobject_cast<PluginWidgetInterface *>(loader.instance());
QWidget * widget = plugin->GetWidget();
connect(plugin,SIGNAL(MessageSend()),this,SLOT(Mes sageReceived()));

folibis
10th September 2013, 05:08
Yes, as I see this code have to work fine. But if you have 20 plugins you duplicate code of PluginWidgetInterface in all of plugins
Just because of this problem I decided do do virtual base interface - in this case you need only include "plugininterface.h" into implementation.
If you derive base interface from QObject you have to organize it as dufferent lib or duplicate code in all of plugins. Correct me if I'am wrong.

Added after 36 minutes:

I guess I've found a solution.
The base interfase not derived from QObject but plugins do it, exactly as in my first post.
in main app I do that:

QPluginLoader loader("plugin.dll");
QObject * obj = loader.instance();PluginWidgetInterface * plugin = qobject_cast<PluginWidgetInterface *>(obj);
QWidget * widget = plugin->GetWidget();
connect(obj,SIGNAL(MessageSend),this,SLOT(MessageR eceived));

Not nice solution but at least it works.

"new style" of connect still not works

connect(obj,&PluginWidgetInterface::MessageSend,this,&MainWindow::MessageReceived);

anda_skoa
10th September 2013, 11:01
Not nice solution but at least it works.


Looks nice to me.
You get the plugin instance, you connect to its signal and you're done.

Cheers,
_

mcarter
10th September 2013, 17:25
Yes, as I see this code have to work fine. But if you have 20 plugins you duplicate code of PluginWidgetInterface in all of plugins
Just because of this problem I decided do do virtual base interface - in this case you need only include "plugininterface.h" into implementation.
If you derive base interface from QObject you have to organize it as dufferent lib or duplicate code in all of plugins. Correct me if I'am wrong.
What code needs to be duplicated from PluginWidgetInterface with the implementation that I showed? As with any derived class, you only need to implement any abstract methods and any code for the specific plugin. There is no extra qobject code that needs to be implemented. The code that uses the plugin still just needs the 'plugininterface.h'.

In your example, GetWidget is still abstract and needs to be implemented in all plugins. If you abstract MessageSend, then you will need that signal defined in all plugins, duplicating code there, and then the only way to access it will be thru the loader.instance() object since PluginWidgetInterface will not have that object signal definition/implementation, as you saw with connect(obj,...) and not connect(plugin,...).

What does not work with the new style of connect? Not sure you can use &PluginWidgetInterface::MessageSend because that is not defined an actual signal in plugininterface.h.

I would be interested to see your implementation.

folibis
11th September 2013, 05:12
I mean if the base interface derived from QObject so it must be a "real object", not just declaration in *.h file. So you must link all of plugins with this object
At least when I've tried your soluton i got lots of linker errors while plugin compiling like:
error: undefined reference to `PluginWidgetInterface::qt_metacast(char const*)'


Looks nice to me.
You get the plugin instance, you connect to its signal and you're done.
_

Yes, it works, but i would like to use "new" connect. As I understand it works a bit faster.
This does not work and I don't like the fact that I can't do what I want :)

wysota
11th September 2013, 06:51
As I understand it works a bit faster.
How did you measure it? How much impact performance of a connect() call has for efficiency of your code? Is it a bottleneck of your application? If someone pays you to do this, did you compare the amount of money he spent on you thinking about the problem with the possible benefit the users are going to have with a presumably faster connect() execution?

mcarter
12th September 2013, 11:58
I mean if the base interface derived from QObject so it must be a "real object", not just declaration in *.h file. So you must link all of plugins with this object. At least when I've tried your soluton i got lots of linker errors while plugin compiling like:
error: undefined reference to `PluginWidgetInterface::qt_metacast(char const*)'

You never create an instance of PluginWidgetInterface, but plugininterface.h must be included in HEADERS for all plugins and the main app, and you need to make sure that plugininterface.h is moc'd. I was able to verify this all works in windows, so it sounds like it might be your build process. Are you using mingw in windows or using the Qt addin for visual studio? What does your .pro file look like?

folibis
13th September 2013, 12:26
Ok, I've created simple test project. That is exactly what I need, mainly connect() in main.cpp
There are 2 subprojects - Main and Plugin/ I was not able to compile it since base interfase derived from QObject.