Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

connect: External "C" function



  • I developed a Qt GUI C++ shared library (plugin) that contains a Fortran 90 static library inside of it.

    To enable to receive the Fortran data back to C++ side, I created a Fortran/C++ interface and it works great.

    In the C++ side I have:

    external "C" {
        void callFortran(...input arguments ....);
        void fortranMsg(int msgType, char* msg);
    }
    

    So, when Fortran send a message to C++, I already receive that inside of this external C function below:

    void fortranMsg(int msgType, char* msg)
    {
        // Messages from Fortran subroutine to C++ side
        // It is already working!
    }
    

    I would like to connect this fortranMsg() function to a plugin method, like:

    Plugin::Plugin(QObject* parent)
    {
        // WRONG SINTAX: JUST A EXAMPLE
        bool conn = connect(fortranMsg, this, &Plugin::receivedMsg);
    }
    
    Plugin::receivedMsg(int msgType, char* msg)
    {
        // Get that Fortran message inside a plugin method
    }
    

    I don't know if connect is a good way to do that...it is just an example of what I need to do...

    How can I send the data (msgType and msg) from fortranMsg() to a Plugin::receivedMsg()?

    Thank you,



  • @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 any Plugin method from any pure function. Like:

    void fortranMsg(int msgType, char* msg)
    {
        pluginInstance->sendStatusMsg(QString(msg));    
    }
    

    That's it!

    Thank you all again!


  • Moderators

    @fem_dev said in connect: External "C" function:

    How can I send the data (msgType and msg) from fortranMsg() to a Plugin::receivedMsg()?

    Since fortranMsg() is a function that is called by Fortran, you cannot connect it like a signal. Instead, you must add extra code inside fortranMsg():

    void fortranMsg(int msgType, char* msg)
    {
        // Messages from Fortran subroutine to C++ side
        // It is already working!
    
        exportData(msgType, msg); // <-- Add a function call here to pass the data out
    }
    

    There are many ways you could implement the exporting function. For example, you could store a pointer to the plugin object as a global variable and call receivedMsg() directly:

    Plugin *pluginInstance = nullptr; // This pointer can be updated when you construct your plugin object
    
    void fortranMsg(int msgType, char* msg)
    {
        // Messages from Fortran subroutine to C++ side
        // It is already working!
    
        if (pluginInstance)
             pluginInstsance->receivedMsg(msgType, msg);
    }
    


  • Thank you @JKSH for your quick response...

    @JKSH said in connect: External "C" function:

    There are many ways you could implement the exporting function.

    I think that I need some help to understand in which scope should I write this lines...

    The problem is: Only inside the main application I can write something like this:

    Plugin *pluginInstance = nullptr; // This pointer can be updated when you construct your plugin object
    

    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?

    Here is my plugin.h header file:

    #include "dialog.h"
    #include "plugin_global.h"
    #include "plugin_qt_api.h"
    
    // Fortran subroutines
    extern "C" {
        void callFortran(...input arguments ....);
        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();
    
        //... All Methods override
    
        void receiveMsg(int msgType, char* msg);
    };
    

    Here is my plugin.cpp implementation:

    #include <iostream>
    #include "plugin.h"
    #include "dialog.h"
    
    Plugin::Plugin(QObject* parent)
    {
    }
    
    Plugin::~Plugin()
    {
    }
    
    void Plugin::receivedMsg(int msgType, char* msg)
    {
    
    }
    
    void fortranMsg(int msgType, char* msg)
    {
        std::cout << msgType << " | " << msg << std::endl;
    
        // SEND DATA TO A PLUGIN METHOD
        // HOW?
    }
    

    Could you help me pass this data from the fortranMsg () function to the Plugin :: receivedMsg () method?

    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.
    Or is there a better way to "connect" via the main application?


  • Lifetime Qt Champion

    @fem_dev said in connect: External "C" function:

    // SEND DATA TO A PLUGIN METHOD
    // HOW?

    Like @JKSH suggested.
    Add a header file and put

    extern Plugin *pluginInstance;
    

    there. Then define pluginInstance in one of your cpp files (main.cpp for example). Include the header file in the cpp file where you define fortranMsg() and you can call pluginInstance as suggested.



  • @jsulm Thank you...I'm almost there...

    Here is what I did:

    #include <iostream>
    #include "plugin.h"
    #include "dialog.h"
    
    extern Plugin *pluginInstance;
    
    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);
    }
    

    The shared library (plugin) compiles without any error or warning. All ok!

    But when my main application tries to load this shared library (using QPluginLoader) I got this run-time error:

    Cannot load library libLobular-Bearing.so: undefined symbol: pluginInstance
    

    What is missing? How can I fix it?

    Thank you,



  • @fem_dev
    You are missing where @jsulm / @JKSH said:

    Then define pluginInstance in one of your cpp files (main.cpp for example)

    You need the line reading

    Plugin *pluginInstance = nullptr; // This pointer can be updated when you construct your plugin object
    

    in your main application. Once, in one of its .cpp files.. Note that is without the extern. Where do you have this?

    Explanation:

    Your shared library has extern Plugin *pluginInstance; in it. That means it says "there is/must be a pluginInstance out there, which I can use". That is what extern means. So it compiles.

    When you come to load at run-time, your program needs to have defined this pluginInstance, to provide this variable. Without it you get undefined symbol. The statement Plugin *pluginInstance, without the extern, defines the symbol. That is needed in some one (and only one) .cpp file of the main application loading your shared library.



  • @JonB Almost there...

    My shared library (plugin) have a Interface Class called Plugin_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 same Plugin_API Interface...not the Plugin class.
    I think that the extern variable declaration must be PluginAPI type in both Qt projects (plugin and main 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?


  • Lifetime Qt Champion

    @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 that pluginInstance 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 about extern Plugin_API *pluginInstance declaration can appear many times (e.g. in a .h file) while the Plugin_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 as static there.


  • Moderators

    @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 the Plugin::sendStatusMsg() method as I want.
    So, I don't know why, I don't get the sent plugin message in my main application.

    @JKSH here is how I communicate my own Plugin with my main application:
    First, the plugin_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 the plugin.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 the fortranMsg() function. Because it is a function and NOT a Plugin 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 the Plugin::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 called App::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 any Plugin method from any pure function. Like:

    void fortranMsg(int msgType, char* msg)
    {
        pluginInstance->sendStatusMsg(QString(msg));    
    }
    

    That's it!

    Thank you all again!


  • Moderators

    @fem_dev, you're welcome. Happy coding! :)

    I'm curious though: It looks like your app only loads 1 instance of the plugin, is that right? If that's the case, what is the purpose of having a Plugin API?



  • @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?


  • Moderators

    @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 means fortranMsg() 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 own pluginInstance pointer.

    My main application will load all compiled plugins after that.


Log in to reply