12

I'd like to avoid using qmake and .pro files. The problem is that I cannot get cmake to work nicely with Qt Plugins. I've included the code below showing the interface, plugin, loader that work correctly for the given .pro file but I cannot figure out how to transfer this functionality to cmake.

Plugin Interface

Pure virtual interface, known by the loader.

#include <QtCore/qglobal.h>

class HelloPluginInterface
{
public:
    virtual void DoSomething() const = 0;
};

Q_DECLARE_INTERFACE( HelloPluginInterface, "com.harbrick.Qt.HelloPluginInterface")

Plugin

Plugin that becomes a .so to be loaded by the loader.

#include <QtPlugin>
#include "helloplugin_global.h"

class HelloPlugin : public QObject, public HelloPluginInterface
{
    Q_OBJECT
    Q_PLUGIN_METADATA( IID "com.harbrick.Qt.HelloPluginInterface" )
    Q_INTERFACES( HelloPluginInterface )

public:
    void DoSomething() const;
};

Plugin Loader

void MainWindow::LoadPlugin( const QString& pathToLibrary )
{
    QPluginLoader loader( pathToLibrary );
    QObject* instance = loader.instance();

    if( instance )
    {
        HelloPluginInterface *plugin = qobject_cast< HelloPluginInterface* >( instance );
        if(plugin)
        {
            //do stuff
            ...
        }
        else
        {
            qDebug() << "Not a plugin: " << Filename << " = " << loader.errorString();
        }
    }
}

CMakeLists.txt

Can't get to work

project( HelloPlugin )
cmake_minimum_required( VERSION 2.8.11 )

set( CMAKE_INCLUDE_CURRENT_DIR ON )

find_package(Qt5Widgets)

include_directories( 
    ${CMAKE_CURRENT_SOURCE_DIR} 
)

set( INCLUDE
    cmakeplugin.h
    cmakeplugininterface.h
)

set( SOURCES
    cmakeplugin.cpp
)

add_executable(${PROJECT_NAME} ${INCLUDE} ${SOURCES} ${SRC_LIST})

add_library( cmakePlugin SHARED cmakeplugin.cpp )

QMake .pro

Works

QT       += widgets

TARGET = HelloPlugin
TEMPLATE = lib

SOURCES += helloplugin.cpp

HEADERS += helloplugin.h \
    helloplugin_global.h

CONFIG += plugin debug

INSTALLS += target
4
  • 1
    "Can't get to work" which errors do you get? Commented Jun 4, 2015 at 9:45
  • "Not a library" on the load. I've figured it out, will post a solution. Commented Jun 4, 2015 at 15:48
  • @DaveyLaser Seems to be a very complex solution when it is taking you years to post it. ;) Commented Nov 15, 2017 at 11:37
  • Wow @Nobody, haha! Thanks for pointing that out, I'll post a solution sometime in the next couple of years. Commented Nov 15, 2017 at 16:16

1 Answer 1

12

Qt generates meta code for plugins, add the following to CMakeLists.txt:

set(CMAKE_AUTOMOC ON)

Here's a complete working example:

CMakeLists.txt

project(test-plugin)
cmake_minimum_required(VERSION 3.2.0)

set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)

find_package(Qt5Widgets REQUIRED)

set(COMPILE_OPTIONS -std=c++11)

add_library(test-plugin SHARED test-plugin.cpp)
target_link_libraries(test-plugin PRIVATE Qt5::Widgets)
target_compile_options(test-plugin PRIVATE ${COMPILE_OPTIONS})

add_executable(test-plugin-exec main.cpp)
target_link_libraries(test-plugin-exec PRIVATE Qt5::Widgets)
target_compile_options(test-plugin-exec PRIVATE ${COMPILE_OPTIONS})

test-plugin-interface.hpp

#ifndef TEST_PLUGIN_INTERFACE
#define TEST_PLUGIN_INTERFACE
#include <QtPlugin>

class TestPluginInterface
{
public:
    virtual ~TestPluginInterface() {}
    virtual void doSomething() const = 0;
};

#define TestPluginInterface_iid "whatever.you.want.TestPluginInterface"
Q_DECLARE_INTERFACE(TestPluginInterface, TestPluginInterface_iid)
#endif // TEST_PLUGIN_INTERFACE

test-plugin.hpp

#ifndef TEST_PLUGIN_HPP
#define TEST_PLUGIN_HPP
#include <QObject>
#include <QtPlugin>
#include "test-plugin-interface.hpp"

class TestPlugin : public QObject, public TestPluginInterface
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID TestPluginInterface_iid)
    Q_INTERFACES(TestPluginInterface)

public:
    ~TestPlugin() override {}
    void doSomething() const override;
};
#endif // TEST_PLUGIN_HPP

test-plugin.cpp

#include <QDebug>
#include "test-plugin.hpp"

void TestPlugin::doSomething() const
{
    qDebug()<< "hot dog!";
}

main.cpp

#include <assert.h>
#include <QDebug>
#include <QPluginLoader>
#include "test-plugin.hpp"

constexpr auto ABSOLUTE_PATH_TO_PLUGIN = 
    "/path/to/build/libtest-plugin.so";

int main(int argc, char* argv[])
{
    assert(QLibrary::isLibrary(ABSOLUTE_PATH_TO_PLUGIN));
    QPluginLoader loader(ABSOLUTE_PATH_TO_PLUGIN);
    if (auto instance = loader.instance()) {
        if (auto plugin = qobject_cast< TestPluginInterface* >(instance)){
            plugin->doSomething();
        }
        else {
            qDebug()<< "qobject_cast<> returned nullptr";
        }
    }
    else {
      qDebug()<< loader.errorString();
    }
}
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks! I had all the code but was missing the MOC option. Checking the difference with nm revealed two added functions: qt_plugin_instance and qt_plugin_query_metadata
Does the interface need to be the same class or can it be just two classes that look the same? I'm asking because I'm thinking about having the plugin in a separate project then the one using the plugin so they cannot share the same interface file.
Yes, this works, the interface just need to look similar and have the same iid.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.