Tech Programming

Custom qtplugin part II

Custom qtplugin part II

Let’s learn to use QPluginLoader class to load custom plugins.

Introduction

In our part I of this tutorial we learnt how to create a qt plugin using Qt low level API to extend our application. In this tutorial, we will learn how to load/use the plugin we created using QPluginLoader class.

Qt docs also states that “QPluginLoader cannot be used if your application is statically linked against Qt. In this case, you will also have to link to plugins statically. You can use QLibrary if you need to load dynamic libraries in a statically linked application.”

Motivation

As we have used Qt’s low level API to create our plugin, we have an option to use QPluginLoader class to load/use the plugin we created. Benefits? QPluginLoader provides convenient functions to access Qt plugin. Also, eliminates the use of platform specific loading e.g. to load a .dll file in windows we need to use "LoadLibraryA()" and in case of Linux we use "dlopen()" and so on. Although, we can use another Qt convenient class QLibrary but that would be a completely another tutorial.

Tutorial

In this short tutorial, we will learn how to load/use a Qt plugin using QPluginLoader class.

// plugin_loader
#include <QObject>
#include <QPluginLoader>
#include "interfaces/Device.h"

class PluginLoader : public QObject
{
        Q_OBJECT

    public:

        explicit PluginLoader(QObject* parent = nullptr);

        Device* load(const QString& path);

    private:

        QPluginLoader* m_pluginLoader;
};

We create a PluginLoader class as a wrapper to load our plugins. Our implementation looks like this:


#include <QDebug>
#include "pluginloader.h"

PluginLoader::PluginLoader(QObject* parent) :
    QObject(parent),
    m_pluginLoader(nullptr)
{
}

Device* PluginLoader::load(const QString& path)
{
    m_pluginLoader = new QPluginLoader(path, this);

    qInfo() << "Loading plugin...";

    if(m_pluginLoader->load())
    {
        qInfo() << "Plugin loaded successfully...";

        Device* device = dynamic_cast<Device*>(m_pluginLoader->instance());
        return device;
    }

    qWarning() << "Plugin " << path << "failed to load..." << m_pluginLoader->errorString();
    return nullptr;
}

Our load(const QString& path) function takes in an argument as a path to a plugin (.so/.dll). To be loadable, the file’s suffix must be a valid suffix for a loadable library in accordance with the platform, e.g. .so on Unix, - .dylib on macOS and iOS, and .dll on Windows.

Qt docs also states that “We recommend omitting the file’s suffix in the file name, since QPluginLoader will automatically look for the file with the appropriate suffix.”

Before loading, we check to see if m_pluginLoader is already loaded. If not, we try to load the plugin using load() and the get the instance of the plugin. In our case:

Device* device = dynamic_cast<Device*>(m_pluginLoader->instance());

would evaluate to look something like this (check part I for PluginA):

Device* device = new PluginA();

On successful return of an instance, we can call the functions available in the plugin on the fly.

Note: If the plugin has additional dependencies on other third party libraries, errorString() might throw an error as “Cannot load library: The specified module could not be found” unless the plugin is in the same folder as in .exe.

Conclusion

This sample project can be found on my GitHub page.

Feel free to share this blog, if you feel it helped you. If you have any comments or suggestions, post it in the comments section below.

Photo by Markus Spiske on Unsplash

comments powered by Disqus