Export Macros
Every plugin DLL/SO needs one entry point so the host can ABI-check, create,
render, and destroy its module. You never write it by hand — place
FRAMELIFT_MODULE_ENTRY from <framelift/ModuleEntry.h> (included via
<framelift/core.h>) at file scope.
Plugin identity (id, name, version, publisher, ABI, dependencies) comes from
the .Plugin.json metadata that add_framelift_plugin(... PLUGIN_JSON ...)
compiles in. The macro below only carries the runtime traits that depend on the
concrete module type — the factory and, for a UI module, its render order.
FRAMELIFT_MODULE_ENTRY
A plugin has exactly one module. FRAMELIFT_MODULE_ENTRY(Type, { ... }) takes your
module type and a braced FrameLiftModuleEntryDesc initializer:
// A module that draws UI (a QObject view-model with an embedded QML root):
FRAMELIFT_MODULE_ENTRY(MyPanel, {
.renderOrder = 50,
})
// A module that draws nothing opts out explicitly:
FRAMELIFT_MODULE_ENTRY(MyService, {
.qml = false,
})
Descriptor fields
| Field | Default | Meaning |
|---|---|---|
.qml | true | Set false for modules with no UI. A .qml = true module is used as its own QML view-model. |
.renderOrder | 0 | Draw order (mapped to Qt z) — lower draws first (further back). Ignored when .qml = false. |
Because .qml defaults to true, a type that is not a QObject fails to
compile until it either inherits QObject (to serve as the QML view-model — see
Rendering UI) or states .qml = false:
FRAMELIFT_MODULE_ENTRY: MyService is not a QObject; add .qml = false or inherit QObject first
The QML root component itself is supplied to the build, not the macro:
add_framelift_plugin(... QML_URI <uri> QML_ENTRY <file.qml> ...).
What it generates
The macro emits a QObject that implements IPlugin,
declares Q_INTERFACES(IPlugin), and carries Q_PLUGIN_METADATA whose FILE is
the generated plugin metadata JSON:
class IPlugin
{
void SetLogSink(Log::SinkFn); // host installs its log forwarder
IModule* CreateModule(); // new Type()
void DestroyModule(IModule*); // delete
QObject* GetViewModel(IModule*); // the module as a QML view-model, or nullptr
const char* QmlEntryUrl(); // qrc URL of the root QML, or nullptr
int RenderOrder(); // .renderOrder, or 0
};
Load sequence on the host side:
- The host reads the embedded
Q_PLUGIN_METADATAJSON first viaQPluginLoader::metaData(). It applies the ABI rule — an exactplugin.abiVersion == host.abiVersionmatch — and resolves dependencies, before any vtable is touched. - For an accepted plugin it calls
instance()andqobject_cast<IPlugin*>, thenSetLogSink()soLog::*from your binary routes into the host logger. CreateModule()constructs the module; the host then callsInstall().- For a UI module,
GetViewModel()/QmlEntryUrl()give the host the QML root and its view-model, layered atRenderOrder(). - On unload,
DestroyModule()deletes it.
See also
- Your First Plugin — the macro in context.
- ABI Compatibility — what the version gate protects.