Can plugins use headers across library boundary?



  • Hi all,

    I created an application plugin that the QPluginLoader recognized just fine. I used one class Result in two files result.h and result.cpp.

    The only change I did was create a new project (lib template) as utils and moved the class to that library.
    It compiles and deploys fine. But when I want to load the plugin, the QPluginLoader does not recognize it as a plugin, while it did when the Result class was part of the plugin project.

    Compiling utils either as dll or as static, in both cases the plugin does not get recognized. I even had create_prl and link_prl in my CONFIG.

    Anyone know what might be wrong here? Is it not possible to use a header from a different library in a plugin?

    P.S. the Result class is not part of the plugin interface.


  • Moderators

    Hi,

    How did try to load the plugin, and how did you realize that "QPluginLoader does not recognize it as a plugin"?

    How did you include the class in your library? You can add the headers to your INCLUDEPATH, but you cannot copy the headers directly into your project.



  • QPluginLoader
    Well, from the C++ function (method actually) I print the status, so I know whether the loader recognized the plugin as a plugin or not. Also, if it does, the next step is to determine whether it really is my interface.

    It fails to recognize the plugin as a plugin. I checked with the debugger and traced it to the spot where it indeed does not recognize the plugin. Unfortunately I do not get to see how the loader determines whether it is not a plugin or I would know what was wrong.

    Include
    Ofcourse, when I create the library I add the relevant INCLUDEPATH and DEPENDPATH statements to my .pro file, plus adding to LIBS.
    Anyway, working with the libraries is not the problem. But the cooperation between the plugin, the subordinate library and the loader has me stumped. I reversed the header and cpp file back to the plugin project and -- voila -- everything works.


  • Lifetime Qt Champion

    Hi,

    Maybe a silly question but did you setup your plugin like described in e.g. the "Plug & Paint Basic Tools Example":http://doc.qt.io/qt-5/qtwidgets-tools-plugandpaintplugins-basictools-example.html ?



  • Yes, I did. The point is, it works properly, until I move any part of the implementation that I refer to , to a different library.

    If I refer to the implementation using a class that is not the plugin itself, everything is hunky dory. But as soon as I refer from the plugin class to any implementation outside the plugin library, then the QPluginLoader does no longer recognize it as a plugin.

    Backward reasoning
    Reasoning backwards from experience. I suspect the plugin class is meant to be standalone and not depend on any external implementation, which makes sense as you might develop a plugin independently. Referring from the plugin class to a declaration in a different library is no problem. Like a header with only pure virtual methods, or all implementation in the header file.

    Indirectly referring to an implementation in a different library from a non-plugin class in the plugin library is no problem either.

    Conclusion
    Specifically, the plugin class is only allowed to depend on the plugin library.

    What do you think? True or not true? Or maybe half true? Please comment.

    Edit 2: I realised that I do refer to a class with implementation in a different library, but only through returning its pointer. While using a reference makes QPluginLoader refuse it, returning a pointer does not. Hmmm, not sure whether this is documented anywhere, but I would certainly be interested what the official rules are concerning plugins.

    Edit 1: @SGaist Thanks for the example. I only studied the simple example and this one shows how to
    implement multiple interfaces in one plugin. Like it. But I don't think it refers to my specific problem, though.


  • Lifetime Qt Champion

    I'm not sure I'm following you correctly. Can you share an image or a bit of code that shows what you are trying to do



  • Edit: If you want to see the complete code, I can send you the github location.

    Okee, here goes. The program the team (3 ppl) is creating is a designer based on QtQuick2. It designs NoC (Network on Chip) based on some primitives from a research paper. The design needs to connect to pure C++ verification tools, for which we chose to create plugins that are able to run verification threads or processes either timed or on request. Below I first show the interface definition, then the code for the plugin.

    There are 2 libraries involved: 1. interfaces 2. syntaxcheckerplugin.
    The interfaces lib contains the Logger class which it also implements. The interface lib also contains the Result class, which I reduced to implementing only in the header file.

    Now the interesting thing is, that both Logger and Result are defined in interfaces (not plugin) and Logger does not prevent recognition, while Result does. If I split the declaration and definition of Result like I do for Logger, the QPluginLoader does not recognize the plugin library as a plugin.

    I now realize (thank you for requesting the source), that there is one additional difference. I use the Result object in a slot that is not defined in the interface. The interface cannot contain a slot (can it?).
    Still, that is the only difference I can think of.

    So apparantly, as long as a class is referenced in the interface itself, it can also be used in the plugin class, even if not implemented in the plugin library. But referencing a class that the interface does not reference, even if the method is not inside the interface definition (the slot), causes the QPluginLoader to not recognize the plugin.

    Well, I wonder if these rules are documented somewhere. I certainly couldn't find it.

    The interface definition is:

    @#ifndef VTPLUGININTERFACE_H
    #define VTPLUGININTERFACE_H

    #include <map>

    #include <QtPlugin>
    #include <QColor>
    #include <QMap>

    #include "xmas.h"
    #include "logger.h"

    class VtPluginInterface
    {

    public:
    virtual ~VtPluginInterface() {}

    virtual QString name() = 0;
    virtual void name(QString name) = 0;
    
    virtual QMap<QString, QString> parameters() = 0;
    virtual void parameters(QMap<QString, QString> paramMap) = 0;
    
    virtual void start(const QString &json) = 0;
    
    virtual Logger *logger() = 0;
    

    };

    #define VtPluginInterface_iid "nl.ou.xmd.VtPluginInterface/1.0"

    Q_DECLARE_INTERFACE(VtPluginInterface, VtPluginInterface_iid)

    #endif // VTPLUGININTERFACE
    @

    The plugin is:

    @#ifndef PLUGINTHREAD_H
    #define PLUGINTHREAD_H

    #include <chrono>

    #include <QString>
    #include <QMap>
    #include <QThread>

    #include "xmas.h"
    #include "logger.h"
    #include "result.h"
    #include "vtplugininterface.h"

    class SyntaxCheckerPlugin : public QObject, public VtPluginInterface
    {
    Q_OBJECT
    Q_PLUGIN_METADATA(IID VtPluginInterface_iid FILE "vtplugin.json")
    Q_INTERFACES(VtPluginInterface)

    public:
    SyntaxCheckerPlugin(QObject *parent = 0);

    virtual ~SyntaxCheckerPlugin();
    
    virtual QString name() override;
    virtual void name(QString name) override;
    
    virtual QMap<QString, QString> parameters() override;
    virtual void parameters(QMap<QString, QString> paramMap) override;
    
    virtual void start(const QString &json) override;
    
    virtual Logger *logger() override;
    

    public slots:
    virtual void handleResults(const Result &result);
    signals:
    void operate(const QString &json);

    private:

    /**
     * @brief m_name the name of the verification tool.
     */
    QString m_name;
    /**
     * @brief m_paramMap the parameters for the verification tool in a QMap.
     */
    QMap<QString, QString> m_paramMap;
    /**
     * @brief m_logger A specific logger for this verification tool.
     */
    Logger *m_logger;
    /**
     * @brief m_workerThread The thread that will run the verification tool
     */
    QThread m_workerThread;
    

    };@



  • It just got weirder. All the above works ..... for Linux. Doing the same from Windows, it won't even compile, stating that it misses vtable and stuff. Certainly indicative of library problems. Here's part of the output:

    @release/syntaxcheckerplugin.o:syntaxcheckerplugin.cpp:(.text+0x9d5): undefined reference to vtable for Logger' release/syntaxcheckworker.o:syntaxcheckworker.cpp:(.text+0x4e): undefined reference toXMASComponent::valid()'
    release/syntaxcheckworker.o:syntaxcheckworker.cpp:(.text+0x3c9): undefined reference to vtable for Result' release/syntaxcheckworker.o:syntaxcheckworker.cpp:(.text+0x3df): undefined reference tobitpowder::lib::MemoryPool::MemoryPool()'
    Makefile.Release:84: recipe for target 'release\syntaxcheckerplugin.dll' failed@

    Apparantly, Logger and Result are both a problem in finding the implementation, even though Result has all in the header.

    Well, I am stumped. Don't know what to say. Works on LInux without a hitch. Windows certainly points to the plugin need to have implementation inside the plugin library.

    Anyone know what the rules really are?


  • Lifetime Qt Champion

    Generally when you have vtable errors it means that you added/removed the Q_OBJECT macro and did not re-run qmake before compiling


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.