Mirror of the Rel4tion website/wiki source, view at <http://rel4tion.org>
Clone
HTTPS:
git clone https://vervis.peers.community/repos/yEzqv
SSH:
git clone USERNAME@vervis.peers.community:yEzqv
Branches
Tags
brainstorming.mdwn
Before I start reading other people’s material, I want to try some plans for myself. Just reading misses the fun of designing and making your own ideas, which could be original!
So, here’s the idea. I’m starting with OOP plugins, i.e. there’s a base class and plugins are derived classes. The general idea is that your program/library has several points on its execution, at which it allows dynamically detected modules modify or extend its behavior. It does so by finding plugin modules, loading them and executing functions they implement. The caller doesn’t know what the implementation does.
How and when the plugins are used by your code is specific to your code. The general purpose interface could take a plugin “type” from you, and give you a list of loaded plugins.
Hmmm… there’s a problem. Since I never wrote plugins, I don’t really know how they’re used. I think it’s time to read.
(reading a bit… looking at some code examples…)
Okay, it’s more or less the same idea. Let’s make more development. In my program, I want to pass two things in exchange of the plugin list: The name of the plugin, and the type. In fact, these names are very confusing. Let’s define them more carefully first.
A plugin is a module which can be discovered and loaded at runtime, dynamically. Some programs and libraries have several roles for plugins. For example, a GUI applciation may have a single interface for plugins and just give them access to the menu (to add buttons, items, etc.) and to the data (so that GUI can trigger changes). In a sense, all plugins have the same role from the application point of view, because they all get the same “tools” to use. But now assume we have an application which uses plugins for specific purposes:
- data storage backend
- desktop notification backend
- data encryption backend
- network access backend
Each plugin “type” has its own role, and probably also its own base class. Since “type” is overloaded in the context of source code, we’ll use “role” to refer to these different purposes of plugins.
Why does the base class not define the role? We could just say each base class is its own role. The reason is that the same base class can be used for different roles. For example, there could be a set of cryptographic plugins for hashing, and another set for private data transmission. But maybe the interface, i.e. base class, is the same.
The main process seems to be:
- Input: Plugin role
- Output: List of available plugins filling that role
Now, how is this thing implemented? Here’s an idea: Generate glue code on both sides, which uses C functions. Hmmm… even before that, we still need a factory.
The general idea of C plugins is maybe that there’s a plugin interface as a set of functions, and each plugin implements them. An OO C++ plugin has a more specific interface: It implements two functions. One creates a new object of a specific kind, and the other destroys it. These functions can too be virtual and implemented as a factory template. Or, actually, as a whole new class. The creator function serves as a constructor, which means each plugin class should have its own factory interface.
This interface doesn’t need to be OOP. Actually, it can benefit from the stable C ABI by being a pair of global C functions, “create” and “destroy”. The user doesn’t have to do anything related to C: These functions can be implemented in a header and wrap what the user actually writes.
Now let’s see how Qt plugins work…
Okay, here’s an update. Pluma is simple, but it takes a very specific OOP approach in which plugins are object factories. But it then misses the flexibility of another OOP abstraction: The factory class itself. It is possible to write any factory class, but Pluma isn’t aiming for that. Let’s see what can be more generic.
The basic idea is that each plugin class is an interface, and each plugin implements it. Plugins are like singletons: Each plugin is like an object of a specific subclass of the plugin class. That class can be a factory, but it also can be anything else.
Looking at Qt source code…
First, Qt does lots of useful checks. It even has an ELF parser. It could be perfect if the code was a standalone plugin system written in standard C++. But since it’s not the case, I’ll make my own small simple thing.
More Qt…
Hmmm I think I found something. The singleton object stored in the plugin shared library is provided by a single symbol “qt_plugin_instance” which is a simple function returning a pointer to QObject. I can do a similar thing. A plugin will just be a C function with a predefined internal name, taking no arguments and returning a void*. My code will convert the void* into the correct class type.
Question: When a plugin developer derives the plugin class, what causes the global instance and the global function to be created? Answer: Either by use of a header #include and possibly a macro, or by some smart use of templates and inheritance and so on. Maybe some CRTP base which keeps a static member of the derived class, and just “happens” to also define the C function. This allows passing the class as a template parameter instead of as a macro parameter.
I think I’ll try starting with the Dr Dobbs article now. If it feels too heavy, I’ll switch to one of the shorter tutorials I haven’t gone over yet.
I tried to find Qt’s way of generating the singleton, but couldn’t see it in the code. Maybe the build system qmake does it. When asked to build a plugin, maybe it automatically adds the singleton. I’ll check its source code later if I need to.
So before the Dr Dobbs new knowledge comes in, here’s a summary. Each plugin is a singleton, i.e. a single object of its own class. The shared library is required to have at least the single symbol linking to a function (can be a C function to avoid mangling problems) which takes no arguments and returns a pointer to the singleton object, which can be statically allocated in the shared library (also possible dynamically, but that’s the plugin writer’s decision). I haven’t decided yet whether the function returns void* or a pointer to the plugin base class - I understood that RTTI doesn’t work across shared libraries or something like that (need to read about it), which means there is no dynamic cast and the pointer type is meaningless. It’s probably easier behind the scenes to use use void*, maybe unless plugin classes will have some Plugin base class.
In order for the plugin to provide metadata like name, role, version, description and so on, a data file can be used (Qt uses JSON) or the plugin can provide functions which return the metadata. There are two kinds of metadata:
- Built-in - required by the framework and possibly used by it, e.g. to compare versions and ensure compatibility
- User - not required or recognized by the framework, and are just like any other user data or user functions in the plugin.
It may be useful to allow changing the metadata format or source without breaking the “API” between program and plugins. It can be done by requiring each plugin to inherit Plugin class, which has metadata functions but already implements them using e.g. a YAML file. Then a plugin can override these functions and provide the metadata in another way. Another option, instead of having a set of functions for the metadata, is to return a struct or a tuple or something like that, with the metadata. But which it means just one virtual metadata function instead of several, it makes the interface somewhat more ugly. A compromise can be to have a non-virtual PluginMetadata class with a getter for each metadata field, and the Plugin class can then have a single metadata function which returns a PluginMetadata object.
This is limited because you can’t selectively override just some metadata functions and not the others, but you can still call the base class’s function first and then modify the returned object and return it. Another option is to do both things, i.e. make separate functions and then wrap them with the PluginMetadata class, so people can use whatever suits them.
The program code casts the singleton pointer into the plugin base class and can then call any member function of it.
Now, Dr Dobbs…
Unbelievable. I started reading. I was much less impressive than I expected, but I kept reading. I knew there are things like version checks and C interoperability and static plugins, that I need to read about. Then, suddenly, I got a message:
“You’ve been busy! Looks like you’ve hit your 2 article limit.”
And it offered me to register with some service I never heard of. Whoever you are, Dr Dobb, forget it. I don’t want this ugly restriction system. I’ll use free open really shared resources instead.
Plugin locations:
- Claws:
/usr/lib/claws-mail/plugins/pgpcore.so
- XFCE4 panel:
/usr/lib/x86_64-linux-gnu/xfce4/panel/plugins/libcpugraph.so
- Thunar:
/usr/lib/x86_64-linux-gnu/thunarx-2/thunar-media-tags-plugin.so
- GStreamer:
/usr/lib/x86_64-linux-gnu/gstreamer-0.10/libgstgconfelements.so
- AbiWord:
/usr/lib/x86_64-linux-gnu/abiword-2.9/plugins/mathview.so
Let’s call “libdir” the directory in which a shared library would be installed. Then the default plugin directory for a project ‘proj’ and a given hook ‘hook’ is $(libdir)/proj/plugins/hook
. Inside that folder there can be many SO files.
Reference counting - do I need it? How is it supposed to work, and why? I have an idea. I’m adding it to features page.