Export Macros
Every plugin DLL must export a fixed set of extern "C" entry points so the host
can version-check, create, render, and destroy it. You never write these by
hand — place FRAMELIFT_PLUGIN_EXPORT from <framelift/PluginExports.h> (included via
<framelift/core.h>) at file scope at the bottom of your plugin's .cpp.
The macro
FRAMELIFT_PLUGIN_EXPORT(Type, { ... }) takes your plugin type and a braced
FrameLiftPluginDesc initializer (designated initializers, in declaration order):
// A plugin that draws UI:
FRAMELIFT_PLUGIN_EXPORT(MyPanel, {
.name = "MyPanel",
.version = {1, 0, 0},
.renderOrder = 50,
.publisher = "Acme",
.description = "Does a thing",
})
// A plugin that draws nothing opts out explicitly:
FRAMELIFT_PLUGIN_EXPORT(MyService, {
.name = "MyService",
.version = {1, 0, 0},
.render = false,
})
Descriptor fields
| Field | Default | Meaning |
|---|---|---|
.name | — | Required. Human-readable plugin name (string literal); a missing name is a compile error. |
.version | {0, 0, 0} | Your plugin's own product semver {major, minor, patch}, independent of the ABI version. |
.render | true | Set false for plugins that draw nothing. |
.renderOrder | 0 | Draw order when rendering — lower draws first (further back). Ignored when .render = false. |
.publisher | nullptr | Optional author/vendor name. |
.description | nullptr | Optional one-line summary. |
.name and .version are your plugin's identity, reported to the host via
framelift_plugin_info() and shown in the host log on load.
Because .render defaults to true, a type that does not implement
IRenderable fails to compile until it either derives
IRenderable or states .render = false:
FRAMELIFT_PLUGIN_EXPORT: MyService does not implement IRenderable — add .render = false (or derive IRenderable)
Setting .render = false on a type that does implement IRenderable is
allowed: the plugin loads but never joins the render list.
Use the macro exactly once per plugin, and only at namespace/file scope. New descriptor fields are appended with defaults over time, so existing export blocks keep compiling unchanged.
What it generates
extern "C" {
const FrameLiftPluginInfo* framelift_plugin_info(); // ABI {major,minor,patch} + name + version + opt. publisher/description
void framelift_set_log_sink(Log::SinkFn); // host installs the log forwarder
IPlugin* framelift_create(); // new Type()
void framelift_destroy(IPlugin*); // delete
IRenderable* framelift_get_renderable(IPlugin*); // the object, or nullptr (.render = false)
int framelift_render_order(); // .renderOrder, or 0 (.render = false)
}
Load sequence on the host side:
framelift_plugin_info()is read first. The host applies the major/minor ABI rule (plugin.major == host.major && plugin.minor <= host.minor); an incompatible plugin is rejected, logged with its name and version, before any vtable is touched.framelift_set_log_sink()installs the host's log sink into the plugin, soLog::*from your DLL routes into the host logger.framelift_create()constructs your plugin; the host then callsInstall().- If
framelift_get_renderable()returns non-null, the object joins the render list atframelift_render_order(). - On unload,
framelift_destroy()deletes it.
Export visibility
The macros mark each entry point with the right export specifier per platform —
__declspec(dllexport) on Windows, default ELF visibility elsewhere (used by the
native-Linux unit-test build). You do not need any extra linker flags.
See also
- Your First Plugin — the macro in context.
- ABI Compatibility — what the version gate protects.