connect: External "C" function
-
@JonB Almost there...
My
shared library
(plugin) have a Interface Class calledPlugin_API
as you can see below:class PLUGIN_EXPORT Plugin : public Plugin_API { Q_OBJECT Q_PLUGIN_METADATA(IID "com.rotortest.plugin") Q_INTERFACES(Plugin_API) ... }
So, inside my
main application
I have the samePlugin_API
Interface...not thePlugin
class.
I think that theextern
variable declaration must bePluginAPI
type in both Qt projects (plugin
andmain application
), right?#include <iostream> #include "plugin.h" #include "dialog.h" extern Plugin_API *pluginInstance; // Changed from 'Plugin' to 'Plugin_API' Plugin::Plugin(QObject* parent) { } void Plugin::receivedMsg(int msgType, char* msg) { } void fortranMsg(int msgType, char* msg) { std::cout << msgType << " | " << msg << std::endl; if (pluginInstance) pluginInstance->receivedMsg(msgType, msg); }
In my
main application
project, I opened the main GUI window Widget (called:App.cpp
) and tried to add this line below:#include "app.h" #include "ui_app.h" Plugin_API *pluginInstance = nullptr; App::App(QWidget *parent) : QMainWindow(parent) , ui(new Ui::App) { ui->setupUi(this); ... }
But I got the same run time error:
libLobular-Bearing.so: undefined symbol: pluginInstance
I tried to change the pointer value inside the
App:loadPlugins()
method (as you can see below) but without success too.void App::loadPlugins(const QStringList pluginsList) { foreach(QString file, pluginsList) { QPluginLoader loader(file); // Error checking: if (!loader.load()) { sendErrorMsg(tr("%1: ").arg(loader.errorString())); continue; } // Get and display the plugin file name: const QStringList pathSplited = loader.fileName().split("/"); const QString fileName = pathSplited.last(); sendStatusMsg(tr("Loading plugin: %1").arg(fileName)); // Cast plugin interface: Plugin_API* plugin = qobject_cast<Plugin_API*>(loader.instance()); // Error chekcing: nullptr if (!plugin) { sendErrorMsg(tr("Wrong Plugin API! Could not cast: %1").arg(loader.fileName())); continue; } // ===== CONNECT ALL SIGNALS / SLOTS ===== // bool conn1 = connect(plugin, &Plugin_API::sendMsg, this, &App::receivedMsg); bool conn2 = connect(plugin, &Plugin_API::savePluginSettings, this, &App::receivedPluginSettings); bool allConnections = conn1 & conn2; if (!allConnections) { sendErrorMsg(tr("Unable to connect: %1").arg(plugin->getName())); continue; } pluginInstance = plugin; // TRY TO USE THE POINTER // Store the plugin in a map: _pluginsMapMap[plugin->getType()][plugin->getName()] = plugin; } }
Could you please help me a little bit more?
How can I fix it? -
@fem_dev said in connect: External "C" function:
In my main application project, I opened the main GUI window Widget (called: App.cpp) and tried to add this line below:
This does not make sense as pluginInstance is declared in the plug-in NOT in the application! You do not need that in main application at all - simply include the header file which contains extern Plugin_API *pluginInstance; - this is what I actually suggested before.
To make it short:- Add extern Plugin_API *pluginInstance; to the header file inside plug-in NOT cpp file
- Add Plugin_API *pluginInstance = nullptr; to the corresponding cpp file inside plug-in
- Do NOT add Plugin_API *pluginInstance = nullptr; in main application
- Read about "extern", for example https://riptutorial.com/cplusplus/example/28730/extern
But, I'm wondering why you don't simply add pluginInstance as static member to Plugin_API...
-
@fem_dev
Briefly, I had slightly misunderstood, and taken your word thatpluginInstance
was to be defined in your main program. From @jsulm I now understand it is to work the other way round: you will define it in your shared library, and export it (extern
) to be accessible from your main. The explanation aboutextern Plugin_API *pluginInstance
declaration can appear many times (e.g. in a.h
file) while thePlugin_API *pluginInstance
definition must appear just once in a.cpp
file remains the same.You should follow exactly what @jsulm has instructed. If as he says you do not need to access
pluginInstance
outside of the shared library at all, you may be able to just define it asstatic
there. -
@fem_dev said in connect: External "C" function:
Because each plugin is loaded by the main application using
QPluginLoader
.
So, I think that I can't use this line above inside the Plugin class. Am I right?You are right, the example code that I wrote must be placed in your main application, not your plugin.
Note: That example expects a single plugin instance. If you want to load multiple plugins at the same time, then you'll need a vector of pointers, not just a single pointer.
A first thought that comes to mind is that I should try to make this connection entirely within the
shared library
(plugin) project, without resorting to the main application project.I don't think that's a good design. A plugin should not be aware of the main application, so a plugin shouldn't be expected to know that
fortranMsg()
exists.The main application should load the plugin and make all necessary "connections" to the plugin.
Or is there a better way to "connect" via the
main application
?Let's step back a bit. Ignore the Fortran code for now.
How do you currently pass any data from your main application into your plugin objects?
-
@jsulm thank you...I'm almost there...
Here are my modifications:Here is my
plugin.h
header file:#include "dialog.h" #include "plugin_global.h" #include "plugin_qt_api.h" extern Plugin_API *pluginInstance; // FIRST MODIFICATION // Fortran interface extern "C" { void fortranMsg(int msgType, char* msg); } class PLUGIN_EXPORT Plugin : public Plugin_API { Q_OBJECT Q_PLUGIN_METADATA(IID "com.rotortest.plugin") Q_INTERFACES(Plugin_API) private: Dialog _ui; public: explicit Plugin(QObject* parent = nullptr); ~Plugin(); ... }
Here is my
plugin.cpp
implementation file:#include "plugin.h" #include <iostream> #include "dialog.h" Plugin_API* pluginInstance = nullptr; // SECOND MODIFICATION Plugin::Plugin(QObject* parent) { Q_UNUSED(parent) } // Other Plugin method implementations...
And inside the
plugin.cpp
I have:void fortranMsg(int msgType, char* msg) { std::cout << msgType << " | " << msg << std::endl; // PRINT THE MSG IN THE Qt CONSOLE OK if (pluginInstance) { pluginInstance->sendStatusMsg(QString(msg)); } else { std::cout << "pluginInstance is NULL" << std::endl; // ALWAYS NULLPTR } }
I do NOT declared `pluginInstance` in my `main application` // LAST MODIFICATION
I got a clean build. No errors, no warnings! All good!
And I don't got any run-time erros, BUT I got always the same console message:pluginInstance is NULL
After that, I tried too:
Plugin_API* pluginInstance = nullptr; // SECOND MODIFICATION Plugin::Plugin(QObject* parent) { Q_UNUSED(parent) pluginInstance = new Plugin; // CREATE A VALID INSTANCE }
In this case, the
pluginInstance is NULL
console message disapears (the pointer is valid), BUT it is not calling correctly thePlugin::sendStatusMsg()
method as I want.
So, I don't know why, I don't get the sent plugin message in mymain application
.@JKSH here is how I communicate my own
Plugin
with mymain application
:
First, theplugin_qt_api.h
:#ifndef PLUGIN_QT_API_H #define PLUGIN_QT_API_H #include <QtPlugin> #include <QString> #include "message_types.h" class Plugin_API : public QObject { Q_OBJECT public: Plugin_API() {} virtual ~Plugin_API() = default; ... virtual void sendStatusMsg(const QString& msg) = 0; signals: void sendMsg(QString source, MSG_TYPE msgType, QString msg); }; Q_DECLARE_INTERFACE(Plugin_API, "com.rotortest.plugin") #endif // PLUGIN_QT_API_H
Second, the
plugin.h
:#include "plugin_global.h" #include "plugin_qt_api.h" extern Plugin_API *pluginInstance; // Fortran subroutines extern "C" { void fortranMsg(int msgType, char* msg); } class PLUGIN_EXPORT Plugin : public Plugin_API { Q_OBJECT Q_PLUGIN_METADATA(IID "com.rotortest.plugin") Q_INTERFACES(Plugin_API) public: explicit Plugin(QObject* parent = nullptr); ~Plugin(); ... void sendStatusMsg(const QString &msg) override; };
Finally, the
Plugin::sendStatusMsg()
method implementation inside theplugin.cpp
file:void Plugin::sendStatusMsg(const QString& msg) { emit sendMsg("My Plugin", MSG_TYPE::STATUS_MSG, msg); }
This plugin method works good if I call it from a method inside the
Plugin
class. No problems here!
My problem is ONLY when I have to call this method from thefortranMsg()
function. Because it is a function and NOT aPlugin
method.So, when my Fortran send a message to the C++ side, I got the message correctly in the
fortranMsg()
function (I can see it on Qt console), but I don't know how to pass this received message to thePlugin::sendStatusMsg()
method.I want to do that to enable my plugins always use the same plugin method to send messages from the plugin side to my main application.
As you can see above, inside my
main application
, I have a method calledApp::loadPlugin()
.
Inside of it, I have:@fem_dev said in connect: External "C" function:
bool conn1 = connect(plugin, &Plugin_API::sendMsg, this, &App::receivedMsg);
Could you help me?
-
@fem_dev Thank you @JKSH @jsulm and @JonB for all help in this topic! I really need to say "thank you"! You all are great!
With your help I finally solve this problem.
Here is my
plugin.h
header file:extern Plugin_API *pluginInstance; // <-- 1. CREATE A GLOBAL INTERFACE CLASS POINTER extern "C" { void fortranMsg(int msgType, char* msg); } class PLUGIN_EXPORT Plugin : public Plugin_API { Q_OBJECT Q_PLUGIN_METADATA(IID "com.rotortest.plugin") Q_INTERFACES(Plugin_API) public: ... void sendStatusMsg(const QString &msg) override; }
Here is my
plugin.cpp
implementation file:Plugin_API* pluginInstance = nullptr; // <-- 2. Init the global pointer to nullptr Plugin::Plugin(QObject* parent) { pluginInstance = this; // <-- 3. Point to the current object }
Now, I can use the global variable
pluginInstance
inside a pure function too.
With this pointer, I can call anyPlugin
method from any pure function. Like:void fortranMsg(int msgType, char* msg) { pluginInstance->sendStatusMsg(QString(msg)); }
That's it!
Thank you all again!
-
@JKSH said in connect: External "C" function:
only loads 1 instance of the plugin
No @JKSH ...my
main application
should load many plugins...What part of my code above made you think that my main application only loads a single plugin?
Did I write something wrong?
-
@fem_dev said in connect: External "C" function:
No @JKSH ...my
main application
should load many plugins...What part of my code above made you think that my main application only loads a single plugin?
You only have 1
pluginInstance
pointer. That meansfortranMsg()
can only send the message to 1 plugin. -
@JKSH said in connect: External "C" function:
You only have 1 pluginInstance pointer.
Yes! That's right!
Inside my plugin, I have only one
pluginInstance
pointer.Each plugin is a isolated Qt
shared library
project. So inside of each plugin I must have only your ownpluginInstance
pointer.My
main application
will load all compiled plugins after that.