Skip to main content

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

FieldDefaultMeaning
.nameRequired. 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.
.rendertrueSet false for plugins that draw nothing.
.renderOrder0Draw order when rendering — lower draws first (further back). Ignored when .render = false.
.publishernullptrOptional author/vendor name.
.descriptionnullptrOptional 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:

  1. 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.
  2. framelift_set_log_sink() installs the host's log sink into the plugin, so Log::* from your DLL routes into the host logger.
  3. framelift_create() constructs your plugin; the host then calls Install().
  4. If framelift_get_renderable() returns non-null, the object joins the render list at framelift_render_order().
  5. 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