Unsolved Are there any special rules with plugins?
-
I have created two different pro files for the application and its plugins.
One variant builds plugins and accordingly builds an application that loads plugins dynamically and the other variant builds everything into one big static application.
Both variants use the same code base (except for the metadata macros).So the plugins are recognized and can be loaded, but when the plugins then try to execute application code, there are crashes.
To narrow this down, I ran the statement that "appears" to cause the crash before loading the plugins in the main program, which succeeded without error.
When I use the debugger to examine the crash location, it looks like the plugin is running in a completely separate memory space. Worse than two different threads.
I have a singleton that encapsulates and provides access to most of the application logic. While in the main program I can use the singleton without issues, when called from the plugin it looks like there is another singleton instance which has not been initialized. That of course leads to a crash.
Therefore my question: are there things that one must not do in a plugin or should perhaps do differently?
I haven't encountered anything like that so far, so I would be very happy if someone could help me out who has already created some plugins.
-
Hi,
Any chances that your singleton is a QObject ?
Remember one thing when using static librairies: each and every plugin will embed a copy of whatever it used from your library. So for QObject based class you will have multiple static meta object definition which is not what you want.
That said, usually a singleton is not the best design and having all the plugins using a singleton to act on your application looks wrong. A plugin usually provides an extension but does require deeper knowledge of the internals of an application.
-
Hi,
Thank you very much for the confirmation. That is exactly what I found out via debugging.
Stupidly I did not know that.
Would you have some links where I could learn more about the background?
Guess the copies of the plugins can't be initialized intentionally. When the static objects are copied, one wonders involuntarily: where does the copying circus start and where does it end.What about plugins?
- If I make my central object a (static?) plugin is that also copied?
- Is there a function that could be used to initialize the plugins "globals"?
Or how to escape the dilemma even better?
If I know it right, a shared library may only contain plain C code. So it's not really what I need. -
I thought I had the solution, but unfortunately I was wrong.
I had assumed that the access class for the singleton would be the problem and therefore "retrofitted" copy constructor and assignment operator - unfortunately I found that I can't set the static pointer in the plugin's address space, which still causes crashes.
I would be very happy about some help.
-
Rather than using a singleton, why not have a controller that you pass to your plugin ?
That said, again, plugins using a singleton to modify the internal state of the main application looks a bit off.
-
@SGaist said in Are there any special rules with plugins?:
Any chances that your singleton is a QObject ?
I completely overlooked the question.
You are right, of course - my singleton is a QObject. This is because I use a variety of signals to communicate with another thread.
My application is a bit different from usual GUI applications, because the (foreign) backend controls many functions, including the state of many UI elements.
To minimize the internal dependencies I decided to use the "singleton".
In the context of the plugins, I started another test. I passed the singleton pointer as a parameter to the plugins and put it back in the right place there.
In addition, I output the contents of the pointer from the application area and from the plugin area. Both were identical.
The first time I called a function that was supposed to use the pointer, the pointer was then suddenly null again, which of course led to a crash.From there I would need still some further support. When is the copied address range used and when not?
Apparently there is a difference if the plugin executes functions while it is created by the factory or if the call is made within the QT event cycle.I had the (crazy?) idea to make every page of the application a plugin. The only thing is that each interactive UI element has to communicate with the backend (different thread), which works via the signals from the singleton.
That's why I'm a bit stuck. Is my idea even feasible, or am I completely barking up the wrong tree?
-
Ok, injecting the memory address did the trick.
But stil got lots of crashes.Don't really understand why, but obtaining the real class from plugin interface does not work with
qobject_cast
. Had to replace it byreinterpret_cast
and now it works.Well, 9 out of 13 plugins work. On two I get unresolved externals like this:
Cannot load library .../plugins/libMyPlugin.so: (undefined symbol: _ZN6Kernel16staticMetaObjectE)
What I don't understand: I use an abstract class between Interface and Pluginclass. That abstract class handles initialization and memory injecting/patching. So from this point of view, all plugins should be identical.
Kernel is the name of the patched singleton - as mentioned, a member of that abstract base class and initialized from abstract base class too.what makes the story even stranger is the fact that I have no problems with unresolved externals in the static version. So everything should be in place.
How can I find out the source of evil?
-
Are you building your common library as a static one ?
-
yes of cause. I have 2 static libraries.
My app is a C++ app and shared libs need a C-interface.
I have to confess, I don't know how to combine that. -
@django-Reinhard said in Are there any special rules with plugins?:
yes of cause. I have 2 static libraries.
Si that's one of your issues. Remember the static meta object copy issue I mentioned earlier ?
@django-Reinhard said in Are there any special rules with plugins?:
My app is a C++ app and shared libs need a C-interface
Why ?
-
Hi,
thank you for your attention!
@SGaist said in Are there any special rules with plugins?:
My app is a C++ app and shared libs need a C-interface
Why ?
Your talking shorthands is not always helpful.
Especially in this case.Wherever I read about shared libs, even in Qt documentation, it mentions, that shared libs need to use special c-functions for interaction.
I don't know it - it was my first time to deal with shared libs. When I read Qt docs about plugins, I thought: ok, that's feasible. Qt docs don't mention the problems, that might occure in context with plugins. In my point of view that memory duplication is so important - it should be documented.
The point is, you probably would not design a plugin as I did, as you know how to do things right. I just work based on trial and error.
So if you know some prosa about shared lib usage without c-interface function, I would be happy to learn about it.
-
@django-Reinhard Shared libs do not have to provide C interfaces, they can provide C++ interfaces including classes. You just need to export symbols (functions and classes) from your libs you want to access in your application. You can see here how to do this: https://doc.qt.io/qt-5/sharedlibrary.html
-
Thank you very much for the pointer!
I already read that page before, but I overlooked the object part obviously.
May be I'm too outdated for that - I used lots of libraries with that decl_import/decl_export thingi and most if not all had extern "C" included in that macro. So for me, that decl-stuff was synonym for C-usage.
Luckily that is wrong, so good times will arise ;)I'll gonna rethink my stuff for that new perspective.
Actually I got a bigger problem. But that's worth to start a new thread.