Plugin and base class constructor - c++

In a Qt application I declare a base class:
class Base:
public QObject {
Q_OBJECT
public:
Base();
virtual ~Base();
virtual void doSomething();
};
Both the c-tor/d-tor and doSomething() have default implementations in a separate source file.
Furthermore, I provide an abstract interface to implement Qt's plugin scheme, like in the Echo Plugin Example.
class Interface {
public:
virtual ~Interface() {}
virtual Base *create() = 0;
};
#define Interface_iid "blag"
Q_DECLARE_INTERFACE(Interface, Interface_iid)
In a plugin that resides in a shared library, libPlugin.so I derive from this base class to provide custom implementations:
class Special:
public Base {
Q_OBJECT
public:
Special() { /* Implementation */ }
void doSomething() { /* Implementation */ }
};
Finally I also implement the plugin interface:
class Q_DECL_EXPORT Plugin:
public QObject,
public Interface {
Q_OBJECT
Q_PLUGIN_METADATA(IID Interface_iid)
Q_INTERFACES(Interface)
public:
Base *create() {
return new Special();
}
};
Now I can load this plugin via QPluginLoader and obtain access to its core object, which correctly identifies itself being a Plugin. But when I try to ThePlugin::create() the Special object, I get a linkage error:
symbol lookup error: libPlugin.so: undefined symbol: _ZN4BaseC2Ev
Beneath the name mangling, I suppose this refers to the Base::Base c-tor that is needed when constructing the derived Special object.
I'm tempted to compare to, e.g. a QStylePlugin: It basically does the same, returning some QStyle-derived class via an abstract interface. Please refer to the Style Plugin Example. Yet I guess the difference is that in the case of the style plugin, both the application (that provides the custom style plugin) and the style plugin itself are linked against the common Qt library that provides, e.g. the c-tors.
How could I resolve this?
What happens if link time optimization removes the c-tors because it cannot know they're needed by plugins?
Linking issue
When compiling and linking the plugin, the Base:Base() class' c-tor as well as the Base::doSomething() come out as unresolved symbol. The c-tor is called by the Special::Special() c-tor and cannot be resolved, of course. The other one is a virtual method
When compiling the application itself, it contains the implementations of the base class' methods. Obviously, they are not linked together when the plugin is loaded.
SSCCE
/********** Interface.h */
#ifndef INTERFACE_H
#define INTERFACE_H
#include <QtPlugin>
#include "Base.h"
class Interface {
public:
virtual ~Interface() { }
virtual Base *createWorker() = 0;
};
#define Interface_iid "blag"
Q_DECLARE_INTERFACE(Interface, Interface_iid)
#endif
/********** App.pro */
QT += core
TARGET = App
TEMPLATE = app
SOURCES += \
Base.cpp \
App.cpp
HEADERS += \
Base.h \
Interface.h \
/********** Base.h */
#ifndef BASE_H
#define BASE_H
#include <QObject>
class Base:
public QObject {
Q_OBJECT
public:
Base();
virtual ~Base();
virtual void doSomething();
};
#endif
/********** Base.cpp */
#include "Base.h"
Base::Base():
QObject() {
}
Base::~Base() { }
void Base::doSomething() { }
/********** App.cpp */
#include "Interface.h"
#include <QCoreApplication>
#include <QPluginLoader>
#include <QDebug>
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
QPluginLoader l("libPlugin.so");
qDebug() << l.load();
qDebug() << l.errorString();
return a.exec();
}
/********** Plugin.pro */
TARGET = Plugin
CONFIG += plugin
TEMPLATE = lib
SOURCES += \
Plugin.cpp \
Special.cpp \
HEADERS += \
Plugin.h \
Special.h \
Interface.h \
Base.h
/********** Special.h */
#ifndef SPECIAL_H
#define SPECIAL_H
#include <QObject>
#include "Base.h"
class Special:
public Base {
Q_OBJECT
public:
Special();
void doSomething();
};
#endif
/********** Special.cpp */
#include "Special.h"
Special::Special():
Base() {
}
void Special::doSomething() { }
/********** Plugin.h */
#ifndef PLUGIN_H
#define PLUGIN_H
#include <QObject>
#include "Interface.h"
class Q_DECL_EXPORT Plugin:
public QObject,
public Interface {
Q_OBJECT
Q_PLUGIN_METADATA(IID Interface_iid)
Q_INTERFACES(Interface)
public:
Base *createWorker();
};
#endif
/********** Plugin.cpp */
#include "Plugin.h"
#include "Special.h"
Base *Plugin::createWorker() {
return new Special();
}
Build:
# Separate files
qmake Plugin.pro && make
qmake App.pro && make
./App

I think I found what is wrong.
The construction I'd like to have linked is as follows:
A host application providing a base class with implementations
A plugin shared library that inherits from that base and re-implements some methods and/or accesses the base class' members
This basically boils down to the following in plain C:
A host application, providing some symbols
A plugin shared library, providing some more symbols and wanting to use some of the host's symbols
However, there is no generic way to achieve the latter.
A host can link against a library to use its functionality. That means the host gets method stubs that are dynamically resolved into against library once the host itself and the library are loaded. Contract between library and host may be some header file with declarations (not definitions).
When a library is dynamically loaded into a host's memory, symbols in the library in turn are not resolved against the host, though. Instead they stay unresolved.
This is exactly what I am seeing here. I tried to resolve the base class' functionality (that I did not implement in the plugin) against the host application that wants to load the plugin. Does-not-work.
This is that -rdynamic is for on ELF, refer to this post.

Related

Q_PROPERTY must be fully defined error in Qt 6

With Qt 5 it was possible to use code like this:
// Test.h
#include <QObject>
#include <QMetaObject>
class LanguageModel;
class Test : public QObject
{
Q_OBJECT
Q_PROPERTY(LanguageModel*, ...)
public:
};
// Test.cpp
#include "Test.h"
#include "LanguageModel.h"
// LanguageModel.h
#include <QObject>
#include <QMetaObject>
class LanguageModel : public QObject
{
Q_OBJECT
}
Now I am trying to convert the project to Qt 6 but the above code fails in the generated "debug\moc_Test.cpp" file with this error message:
C:\Qt\6.1.0\msvc2019_64\include\QtCore\qmetatype.h:778: error: C2338: Type argument of Q_PROPERTY or Q_DECLARE_METATYPE(T*) must be fully defined
Replacing the class forward definition with a direct header include works:
// Test.h
#include <QObject>
#include <QMetaObject>
#include "LanguageModel.h" //class LanguageModel;
class Test : public QObject
{
Q_OBJECT
Q_PROPERTY(LanguageModel*, ...)
public:
};
How can I keep using class defintions instead of including the headers in Qt 6?
Regards,
I've just asked here on Qt forum as I had the same issue today using Models.
It's problematic for me to have to include the header.
Two solutions were given to me by VRonin:
Q_MOC_INCLUDE to include the header only for the moc file:
// Test.h
#include <QObject>
#include <QMetaObject>
Q_MOC_INCLUDE("LanguageModel.h")
class Test : public QObject
{
Q_OBJECT
Q_PROPERTY(LanguageModel*, ...)
public:
};
Q_DECLARE_OPAQUE_POINTER: to tell Qt we want to keep an opaque pointer
// Test.h
#include <QObject>
#include <QMetaObject>
#ifndef OPAQUE_PTR_LanguageModel
#define OPAQUE_PTR_LanguageModel
Q_DECLARE_OPAQUE_POINTER(LanguageModel*)
#endif // OPAQUE_PTR_LanguageModel
class Test : public QObject
{
Q_OBJECT
Q_PROPERTY(LanguageModel*, ...)
public:
};
Personally I would prefer the second option, it could save some compilation time.
I've added the macro just in case you would use it in several objects of a same project.
I'm not an expert, but it looks like it's required since Qt 6.0 because of static assertion. Proof: https://embeddeduse.com/2021/01/17/migrating-a-harvester-hmi-from-qt-5-12-to-qt-6-0/ (chapter 'Error: invalid application of ‘sizeof’ to incomplete type ‘IntegerRangeModel’')

How to avoid circular dependency and undefined type error when creating classes that refer to each other?

I want to create some objects that can delegate some work to its nested subobjects, but it pushes me into the circular dependency issues. Using of #ifndef directive works fine if I have only two classes (ClassA and ClassB), but this pattern doesn't work when ClassC is added. Is it possible to achieve such type of structure as shown in the code below and don't get an "undefined type" errors?
ClassA.h
#pragma once
#include "CoreMinimal.h"
#include "ClassB.h"
class UClassB;
#include ClassA.generated.h
UCLASS()
class PROJ_API UClassA : public UObject
{
GENERATED_BODY()
UPROPERTY()
UClassB* ObjB;
public:
void DoDelegation()
{
auto* ThisInstance = this;
ObjB = NewObject<UClassB>();
ObjB->DoWorkClassB(ThisInstance);
}
}
ClassB.h
#pragma once
#include "CoreMinimal.h"
//works nice
#ifndef CLASSA_H
#define CLASSA_H
#include "ClassA.h"
class UClassA;
#endif
//trying to use similar pattern which works with ClassA.h and ClassB.h
#include "ClassC.h"
class UClassC;
#include ClassB.generated.h
UCLASS()
class PROJ_API UClassB : public UObject
{
GENERATED_BODY()
UPROPERTY()
UClassC* ObjC;
public:
void DoDelegation()
{
auto* ThisInstance = this;
ObjC = NewObject<UClassC>();
ObjC->DoWorkClassC(ThisInstance);
}
void DoWorkClassB(UClassA* &ObjectClassA)
{
// do some stuff with ObjectClassA
}
}
ClassC.h
#pragma once
#include "CoreMinimal.h"
//trying to use similar pattern which works with ClassA.h and ClassB.h
//got "undefined type" error
#ifndef CLASSB_H
#define CLASSB_H
#include "ClassB.h"
class UClassB;
#endif
#include ClassC.generated.h
UCLASS()
class PROJ_API UClassC : public UObject
{
GENERATED_BODY()
public:
void DoWorkClassC(UClassB* &ObjectClassB)
{
// do some stuff with ObjectClassB
}
}
creating classes that refer to each other
Is it possible to achieve such type of structure as shown in the code below and don't get an "undefined type" errors?
Certainly. Referring to an object (with a pointer for example) of some class only requires declaration of the other class, not definition.
Simple solution is to declare both classes before defining either of them.
It's still not fully clear for me, but at least i understood why forward declaration wasn't worked for the first time. Inline implementation of methods that do something with such referencing classes is strictly not recommended. It compiles well If function is implemented in cpp.

signal not correctly exported when inheriting from different dll

The following situation:
I have a class (Parent) in DLL A which has a signal (which is final). I have a class (Child) in DLL B that inherits from A. I import both DLLs to my project. Then I have a class in my project get an instance of the Child class and try to connect the signal of the Child instance to the current object. Somehow this connection fails. Running the whole thing in debug mode only gives me: QObject::connect: signal not found in Child. Checking the dumpbin shows me that the signal is in DLL A but not in DLL B. Both the class Parent and the class Child have the proper export statements but somehow the the signal for the Child class doesn't get exported. Here is the whole thing as code:
a.dll:
class DLLA_SHAREDEXPORT ParentInterface{
public:
~ParentInterface(){}
virtual void test() = 0;
};
Q_DECLARE_INTERFACE(ParentInterface, "ParentInterface")
class DLLA_SHAREDEXPORT Parent : public QObject,
public ParentInterface{
Q_OBJECT
Q_INTERFACES(ParentInterface)
...
signals:
void test();
}
b.dll:
class DLLB_SHAREDEXPORT Child : public Parent{
Q_OBJECT
...
}
class in project:
class SomeClass : public QObject{
Q_OBJECT
public:
...
void someFunction(){
Parent* c = getChildObject(); //some call to get the Object of class Child
QObject::connect(c, &Parent::test,
this, &SomeClass::handle_test);
//QObject::connect: signal not found in Child
}
}
public slots:
void handle_test(){
//do something
}
}
So the problem seems to be somewhere is the proper export of everything in the DLL B but I can't figure out how to do it. Any help would be appreciated.
I'm sorry, but I can't reproduce this issue on either Windows 10.0 with MSVC2015 and Qt 5.7 or OS X 10.10. Please try this project verbatim and see if it works for you. If it does, you're doing something else wrong and will need to produce a test case and edit it into the question.
The project is available from: https://github.com/KubaO/stackoverflown/tree/master/questions/sigslot-dll-39149263
sigslot-dll-39149263.pro
TEMPLATE = subdirs
SUBDIRS += lib1 lib2 main
main.depends += lib1 lib2
lib2.depends += lib1
lib1/lib1.pro
QT = core
CONFIG += c++11
TEMPLATE = lib
HEADERS += lib1.h
win32:DEFINES += LIB1_EXPORT=__declspec(dllexport)
lib1/lib1.h
#ifndef LIB1_H
#define LIB1_H
#include <QObject>
#ifdef WIN32
#ifndef LIB1_EXPORT
#define LIB1_EXPORT __declspec(dllimport)
#endif
#else
#define LIB1_EXPORT
#endif
class LIB1_EXPORT Parent : public QObject {
Q_OBJECT
public:
Q_SIGNAL void test();
};
#endif
lib2/lib2.pro
QT = core
CONFIG += c++11
TEMPLATE = lib
HEADERS += lib2.h
win32:DEFINES += LIB2_EXPORT=__declspec(dllexport)
win32:CONFIG(debug,release|debug) LIBSUBPATH=/debug
win32:CONFIG(release,release|debug) LIBSUBPATH=/release
LIBS += -L../lib1$$LIBSUBPATH -llib1
INCLUDEPATH += ..
DEPENDPATH += ..
lib2/lib2.h
#ifndef LIB2_H
#define LIB2_H
#include "lib1/lib1.h"
#ifdef WIN32
#ifndef LIB2_EXPORT
#define LIB2_EXPORT __declspec(dllimport)
#endif
#else
#define LIB2_EXPORT
#endif
class LIB2_EXPORT Child : public Parent {
Q_OBJECT
};
#endif
main/main.pro
QT = core
CONFIG += c++11
TEMPLATE = app
SOURCES += main.cpp
win32:CONFIG(debug,release|debug) LIBSUBPATH=/debug
win32:CONFIG(release,release|debug) LIBSUBPATH=/release
LIBS += -L../lib1$$LIBSUBPATH -llib1 -L../lib2$$LIBSUBPATH -llib2
INCLUDEPATH += ..
DEPENDPATH += ..
main/main.cpp
#include "lib1/lib1.h"
#include "lib2/lib2.h"
int main() {
int counter = 0;
Child child;
Parent * parent = &child;
QObject::connect(parent, &Parent::test, [&]{ counter++; });
emit parent->test();
emit parent->test();
Q_ASSERT(counter == 2);
}
So the issue seems to be somewhere with the usage of the Interface. We solved the issue for now by moving everything from the ParentInterface into the Parent class and removing the ParentInterface. This feels more like a workaround than a proper solution though.

"plugin verification data mismatch" while loading plugin for qt5 project

I have raw (no QtDesigner) Qt5 project with two simple plugins, which one don't load with laconic error: "plugin verification data mismatch".
Header of first plugin (which loads and run well):
#ifndef __PIROGRONIAN__P2P2__GUI_PLUGIN__H__
#define __PIROGRONIAN__P2P2__GUI_PLUGIN__H__
#include "QtCore/QtCore"
#include "PluginInterface.h"
namespace P2P2 {
class GuiPlugin : public QObject, public PluginInterface {
Q_OBJECT
Q_PLUGIN_METADATA(IID "Pirogronian.P2P2.GuiPlugin")
Q_INTERFACES(P2P2::PluginInterface)
public:
bool init(CoreServer *);
bool receiveObject(Object*);
int channelType();
};
};
#endif
The second one, which don't load:
#ifndef __PIROGRONIAN__P2P2__CHAT_PLUGIN__H__
#define __PIROGRONIAN__P2P2__CHAT_PLUGIN__H__
#include <QtNetwork/QtNetwork>
#include "Chat.h"
#include "PluginInterface.h"
namespace P2P2 {
class ChatPlugin : public QObject, public PluginInterface {
Q_OBJECT
Q_PLUGIN_METADATA(IID "Pirogronian.P2P2.ChatPlugin")
Q_INTERFACES(P2P2::PluginInterface)
CoreServer *_server;
QHash<Channel *, Chat *> _chats;
public:
virtual bool init(CoreServer *);
virtual bool receiveObject(Object *);
virtual int channelType();
};
};
//Q_DECLARE_METATYPE(QPointer<P2P2::ChatPlugin>)
#endif
Here is PluginInterface header:
#ifndef __PIROGRONIAN__P2P2__PLUGIN_INTERFACE__H__
#define __PIROGRONIAN__P2P2__PLUGIN_INTERFACE__H__
#include "CoreServer.h"
namespace P2P2 {
class PluginInterface {
public:
virtual bool init(CoreServer *) = 0;
virtual bool receiveObject(Object *) = 0;
virtual int channelType() = 0;
};
};
Q_DECLARE_INTERFACE(P2P2::PluginInterface, "Pirogronian/P2P2/PluginInterface/1.0")
#endif
I'm not expert and writing plugins for qt5 is described very cursorily. But since I can't find any major difference between those plugins, problem becomes rather mysterious to me. Mayby a bug in Qt? I've rebuilt both several times to bye sure that both are up to date.
I'm trying put the whole code somewhere into net, but it'll take a while... Edit: done - packed as zip here: http://uploaduj.net/D74c2f/v0-1-pure-zip/
Not sure if it helps but it seems that your plugin has invalid metadata. Here's code that sets error message http://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/plugin/qlibrary.cpp#n303
You could use debug version of Qt and set breakpoint in that function. This would give you exact line that fails while loading your plugin.
Maybe you have an error in your metadata?

how to create plugins (Qt 5.2.0)

i ‘m trying to create a plugin(not for Qt creator),
i created an empty project and added the following files :
but i’m getting the following errors:
1.
C:\Qt\Qt5.2.0\5.2.0\mingw48_32\include\QtCore\qglobal.h:666: erreur : invalid application of ‘sizeof’ to incomplete type ‘QStaticAssertFailure’ enum {Q_STATIC_ASSERT_PRIVATE_JOIN(q_static_assert_result, COUNTER) = sizeof(QStaticAssertFailure)} ^
2.
D:\MyFiles\Projects\QtProjects\pluginTest2\plugintest.cpp:9: erreur : expected initializer before ‘PluginTest’ QString PluginTest::name() const ^
PluginTest2.pro (project name)
CONFIG += plugin
TARGET = PluginTest
CONFIG += plugin release
VERSION =1.0.0
TEMPLATE = lib
SOURCES += \
plugintest.cpp
HEADERS += \
Interface.h \
plugintest.h
interface.h
#ifndef INTERFACE_H
#define INTERFACE_H
#include <QString>
class Interface
{
public:
virtual QString name() const =0;
};
Q_DECLARE_INTERFACE(Interface,"interface /1.0.0")
#endif // INTERFACE_H
plugintest.h
#ifndef PLUGINTEST_H
#define PLUGINTEST_H
#include <QObject>
#include <QString>
#include<QtPlugin>
#include "Interface.h"
class PluginTest:public QObject,public Interface
{
Q_OBJECT
Q_INTERFACES(Interface)
public:
PluginTest();
QString name() const;
};
#endif // PLUGINTEST_H
plugintest.cpp
#include "plugintest.h"
PluginTest::PluginTest()
{
}
Q_EXPORT_PLUGIN2(PluginTest,PluginTest)
QString PluginTest::name() const
{
return "pluginTest";
}
The problem is this line:
Q_EXPORT_PLUGIN2(PluginTest,PluginTest)
This is a Qt 4 feature, so you would need to either remove it, or put behind a version checking macro as follows:
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
Q_EXPORT_PLUGIN2(PluginTest,PluginTest)
#endif
For completeness, it seems you do not specify the meta data for the plugin. You would need to add that either unconditionally or conditionally with a version checking macro if you wish to support both Qt 4 and Qt 5 as follows:
class PluginTest:public QObject,public Interface
{
Q_OBJECT
Q_INTERFACES(Interface)
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
Q_PLUGIN_METADATA(IID "your-string-here" FILE "file-here-but-can-be-empty")
#endif
...
};
Also, note that you append the "plugin" CONFIG item twice in your project file.