Skip to main content

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

FieldDefaultMeaning
.qmltrueSet false for modules with no UI. A .qml = true module is used as its own QML view-model.
.renderOrder0Draw 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:

  1. The host reads the embedded Q_PLUGIN_METADATA JSON first via QPluginLoader::metaData(). It applies the ABI rule — an exact plugin.abiVersion == host.abiVersion match — and resolves dependencies, before any vtable is touched.
  2. For an accepted plugin it calls instance() and qobject_cast<IPlugin*>, then SetLogSink() so Log::* from your binary routes into the host logger.
  3. CreateModule() constructs the module; the host then calls Install().
  4. For a UI module, GetViewModel() / QmlEntryUrl() give the host the QML root and its view-model, layered at RenderOrder().
  5. On unload, DestroyModule() deletes it.

See also