[SOLVED] How can a plugin that is dynamically loadded access main application objects and data?
-
Hi there
I have an application that loads plugins in run time. I want to write a singleton pattern (and also later Factory pattern) and able to access from loadded plugins. I added header files for singleton class to plugin project. But when I try load plugins in main application, I get undefined symbol error and library can't be loadded. If I add implementation file(singleton.cpp) to plugin , loadded plugin use own singleton code not the main application. I prepared simple program to explain my problem. You can see the codes under.
loadplugin main program Project File
@
QT += coreQT -= gui
TARGET = loadplugin
CONFIG += console
CONFIG -= app_bundleTEMPLATE = app
SOURCES += main.cpp
singleton.cppHEADERS +=
moduleinterface.h
singleton.h
@Main Application Singleton header
@
#ifndef SINGLETON_H
#define SINGLETON_H
class Singleton
{
static Singleton* sInstance;
public:
int property;
Singleton(){property=1; Singleton::sInstance=0;}
static Singleton& instance();
~Singleton();
};#endif // SINGLETON_H
@
Main application singleton implementation file
@
#include "singleton.h"
Singleton* Singleton::sInstance=0;Singleton::~Singleton()
{}
Singleton& Singleton::instance()
{
if(sInstance == 0)
{
sInstance = new Singleton();
}
return *(sInstance);
}@
Main application main.cpp file
@
#include <QCoreApplication>
#include <QPluginLoader>
#include <QDebug>
#include <moduleinterface.h>
#include <singleton.h>int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);QString moduleName = "testplugin"; QString functionName = "command"; Singleton::instance().property=3; const QString *libPath = new QString("/home/ckurdu/Documents/my_works/C++/qt/qtbin/qtcms_modules/"+moduleName+"/libqt"+moduleName+".so"); QPluginLoader pluginLoader(*libPath); pluginLoader.load(); if(pluginLoader.isLoaded()) { QObject *plugin = pluginLoader.instance(); if(plugin) { ModuleInterface *moduleInt = qobject_cast<ModuleInterface *>(plugin); if(moduleInt) { qDebug()<<"Module loaded"<<endl; qDebug()<<moduleInt->execute(functionName); }else{ qDebug()<<"Module not converted"<<endl; } }else{ qDebug()<<"Module couldn't resolve"<<endl; } }else{ if(!QLibrary::isLibrary(*libPath)) { qDebug()<<"File is not a type of library" <<endl; } qDebug()<<"Module not loaded"<<endl; qDebug()<<pluginLoader.errorString()<<endl; } return a.exec();
}
@
moduleinterface.h file for plugin creation.
@
#ifndef MODULEINTERFACE_H
#define MODULEINTERFACE_H
#include <QString>
#include <QObject>
class ModuleInterface
{
public:
virtual ~ModuleInterface() {}
virtual QString display(QString) = 0;
virtual QString execute(QString) = 0;
};QT_BEGIN_NAMESPACE
#define ModuleInterface_iid "proje.tc.ModuleInterface"
Q_DECLARE_INTERFACE(ModuleInterface, ModuleInterface_iid)
QT_END_NAMESPACE
#endif // MODULEINTERFACE_H
@
*
Example plugin TestPlugin pro file.
*
@
QT -= gui
CONFIG += pluginTARGET = TestPlugin
TEMPLATE = libDEFINES += TESTPLUGIN_LIBRARY
SOURCES += testplugin.cpp
../../loadplugintest/loadplugin/singleton.cppHEADERS += testplugin.h
testplugin_global.h
../../loadplugintest/loadplugin/moduleinterface.h
../../loadplugintest/loadplugin/singleton.hTARGET = $$qtLibraryTarget(qttestplugin)
DESTDIR = /home/ckurdu/Documents/my_works/C++/qt/qtbin/qtcms_modules/testplugin/@
plugin header file
@
#ifndef TESTPLUGIN_H
#define TESTPLUGIN_H#include <QObject>
#include <QtPlugin>
#include <QString>
#include "../../loadplugintest/loadplugin/moduleinterface.h"
#include "../../loadplugintest/loadplugin/singleton.h"class TestPlugin : public QObject, ModuleInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "proje.tc.ModuleInterface")
Q_INTERFACES(ModuleInterface)
int yesno=false;
public:
QString display(QString command);
QString execute(QString command);
TestPlugin(QObject *parent=NULL);
};#endif // TESTPLUGIN_H
@
TestPlugin implementation
@
#include "testplugin.h"
#include <QDebug>TestPlugin::TestPlugin(QObject* parent):QObject(parent)
{
}QString TestPlugin::display(QString command)
{
return command;
}QString TestPlugin::execute(QString command)
{
qDebug()<<Singleton::instance().property<<endl;
return command;
}@
If I add singleton.cpp file to my simple test plugin I get singleton::property value as 1. If singleton code of main program runs, singleton::property value must be 3.
Sorry for my english.
-
Hi,
You may consider defining an interface, e.g. AbstractSingleton, for the singleton and derive your Sİngleton class from this interface. You can add a setter function to your plugin interface that takes a pointer or reference to an AbstractSingleton, e.g
@void setSingleton(AbstractSingleton *singleton)@ -
Hi ckakman
I tried with abstract class. I get error message when Main application loads the library. "undefined symbol". If I remove the line "this->singleton = singleton" in setSingleton method of plugin, Program loads plugin (library) successfully. But of course it is not a solution.
-
Hi again,
Unfortunately I couldn't understand where and how the error occurs. Pure abstract classes completely decouple declarations and implementations therefore I wouldn't expect much issues. Moreover an error like "undefined symbol" smells like a missing header include somewhere.
Could you please post here the following listings?
- definition of the singleton interface class
- new definition of the Singleton class
- new definition of the plugin interface
- implementation of the setSingleton() member of the concrete plugin class implementing the plugin interface
- the code segment where you call the setSingleton() on the plugin
-
bq. definition of the singleton interface class
@
#ifndef SINGLETONINTERFACE
#define SINGLETONINTERFACE
class SingletonInterface
{
public:
static int property;
virtual int getProperty() = 0;
virtual void setProperty(int value) = 0;
static SingletonInterface& instance();
};
#endif // SINGLETONINTERFACE
@bq. new definition of the Singleton class
@#ifndef SINGLETON_H
#define SINGLETON_H
#include "singletoninterface.h"
class Singleton:public SingletonInterface
{
static Singleton* sInstance;
public:
int property;
Singleton(){property=1; Singleton::sInstance=0;}
static SingletonInterface& instance();
int getProperty(){ return this->property;}
void setProperty(int value){this->property = value;}
~Singleton();
};#endif // SINGLETON_H@
bq. new definition of the plugin interface
@#ifndef MODULEINTERFACE_H
#define MODULEINTERFACE_H
#include <QString>
#include <QObject>
#include "/home/ckurdu/Documents/my_works/C++/qt/tests/loadplugintest/loadplugin/singletoninterface.h"
class ModuleInterface
{
public:
virtual ~ModuleInterface() {}
virtual QString display(QString) = 0;
virtual QString execute(QString) = 0;
virtual void setSingleton(SingletonInterface *ptr) =0;
};QT_BEGIN_NAMESPACE
#define ModuleInterface_iid "proje.tc.ModuleInterface"
Q_DECLARE_INTERFACE(ModuleInterface, ModuleInterface_iid)
QT_END_NAMESPACE
#endif // MODULEINTERFACE_H@
bq. implementation of the setSingleton() member of the concrete plugin class implementing the plugin interface
@#include "testplugin.h"
#include <QDebug>TestPlugin::TestPlugin(QObject* parent):QObject(parent)
{
}QString TestPlugin::display(QString command)
{
return command;
}QString TestPlugin::execute(QString command)
{
qDebug()<<this->psing->instance().property<<endl;
return command;
}void TestPlugin::setSingleton(SingletonInterface *ptr)
{
this->psing = ptr;
}@@the code segment where you call the setSingleton() on the plugin@
@#include <QCoreApplication>
#include <QPluginLoader>
#include <QDebug>
#include <moduleinterface.h>
#include <singleton.h>int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);QString moduleName = "testplugin"; QString functionName = "command"; Singleton &singleton = dynamic_cast<Singleton&>(Singleton::instance()); singleton.property=3; const QString *libPath = new QString("/home/ckurdu/Documents/my_works/C++/qt/qtbin/qtcms_modules/"+moduleName+"/libqt"+moduleName+".so"); QPluginLoader pluginLoader(*libPath); pluginLoader.load(); if(pluginLoader.isLoaded()) { QObject *plugin = pluginLoader.instance(); if(plugin) { ModuleInterface *moduleInt = qobject_cast<ModuleInterface *>(plugin); if(moduleInt) { qDebug()<<"Module loaded"<<endl; moduleInt->setSingleton(&singleton); qDebug()<<moduleInt->execute(functionName); }else{ qDebug()<<"Module not converted"<<endl; } }else{ qDebug()<<"Module couldn't resolve"<<endl; } }else{ if(!QLibrary::isLibrary(*libPath)) { qDebug()<<"File is not a type of library" <<endl; } qDebug()<<"Module not loaded"<<endl; qDebug()<<pluginLoader.errorString()<<endl; } return a.exec();
}@
setSingleton is called on Line 28 . But program never reachs this line.
Error Message
"Cannot load library /home..../libqttestplugin.so : undefined symbol: _Zn18SingletonInterface8propertyE"I also solved the problem with different way.
I created a plugin that have singleton and factory methods. I load this plugin from main and testplugin. It works. I don't understand how but
library have same data between main and loadded testplugin. -
Hi,
The point of using an interface is that you don't need to link to a concrete implementation. You should not add a non-virtual method to an interface. Also you shouldn't have a, static or not, a data member in an interface.
You are calling the static instance() member on the SingletonInterface pointer you've passed to the plugin. This is the problem. You shouldn't do that. Just call the non-static virtual members via the pointer.
To make it clearer, look at this line of code in the TestPlugin
@qDebug()<<this->psing->instance().property<<endl;@Here you are trying to access a concrete member. Of course the runtime will complain because it needs to read the "property" directly. You should have used the virtual getProperty() accessor:
@qDebug()<<this->psing->getProperty()<<endl;@Put the static instance() member and the static property data member into Singleton. This way, you won't need to dynamic_cast and your plugin won't need a concrete SingletonInterface implementation.
-
[quote author="CKurdu" date="1420317414"]It worked perfectly. Thank you. Which method do you suggest?
1 . Making a library for factory methods and load this librarry into main application and for each plugins.
- Your solution.
[/quote]
I would suggest my solution. Not because it is my solution :), but because you'd need to take care of one library less.
Kolay gelsin.