Native Plugins
Native plugins are shared libraries that register C++ functions and modules directly into the scripting namespace. They are the fastest way to extend Scrii with host functionality.
Scaffolding a plugin
The module/ folder ships a generator:
./module/create_module.sh mymod
make -C mymod # -> mymod.so / .dylib / .dll
../build/interpreter/scrii_repl mymod/example.scr
This creates:
mymod/
├── mymod.cpp plugin source — implements scrii_plugin_init()
├── include/ copies of the scrii headers it compiles against
│ ├── plugin.hpp Registry interface + ABI version
│ ├── parser/result.hpp status enum, result type
│ ├── parser/error.hpp ScriptError for argument validation
│ └── var/var.hpp var::Var value type
├── Makefile -> .so / .dylib / .dll
└── example.scr load_plugin(...) demo
The generated mymod.cpp registers a restricted module with one sample
function. Edit it, add your own functions to the module, and rebuild.
The plugin entry point
#include "plugin.hpp"
extern "C" bool scrii_plugin_init(scrii::plugin::Registry ®) {
if (reg.abi_version() != scrii::plugin::kAbiVersion)
return false;
reg.add_function("shout",
[](std::vector<scrii::var::Var> a) -> scrii::var::Var {
return scrii::var::Var(a[0].to_string() + "!!!",
scrii::var::var_type::STRING);
},
"shout(s) - Uppercase s with emphasis");
reg.add_module("mymod", "my module description", { ... }); // optional restricted module object
return true;
}
Build with the same compiler and headers as the host interpreter. The plugin
does not link against scrii — the host exports the symbols that the plugin
resolves against, which is why the generated Makefile has no link step.
The Registry API
The scrii::plugin::Registry passed to scrii_plugin_init is the plugin's
whole interface to the interpreter:
| Member | Description |
|---|---|
int abi_version() | Returns kAbiVersion. Refuse to load if it doesn't match what the plugin was compiled against. |
const std::string &engine_version() | The host engine's version string. |
void add(name, var::Var value) | Registers value as a restricted top-level global. Re-registering replaces the previous value. |
void add_function(name, fn, description) | Convenience wrapper for add registering a function global. |
void add_module(name, description, members) | Registers a restricted module object holding the given member table. |
std::vector<std::string> registered_names | Names registered through this Registry (used for unload bookkeeping). |
module in the sample above is built with add_module:
extern "C" bool scrii_plugin_init(scrii::plugin::Registry ®) {
if (reg.abi_version() != scrii::plugin::kAbiVersion)
return false;
reg.add_module("mymod", "My demo module.", {
{"greet", scrii::var::Var(
[](std::vector<scrii::var::Var> a) -> scrii::var::Var {
return scrii::var::Var("hi " + a[0].to_string(),
scrii::var::var_type::STRING);
},
scrii::var::var_type::FUNCTION)},
});
return true;
}
load_plugin("./mymod.so")
print(mymod.greet("world")) // hi world
The ABI version is the one stable contract between host and plugin. Bump your plugin's compiled headers in lockstep with the host; mismatches are caught by the version check rather than by undefined behavior.
Loading from a script
load_plugin("./mymod.so") // true on success
print(shout("hi")) // HI!
- Registered names become restricted globals — scripts can call them but never overwrite them.
- Duplicate loads of the same file are no-ops.
unload_plugin(path)removes the module's globals immediately; guarded calls then throw"plugin module is unloaded". The library itself stays mapped until engine teardown (deferred close), and re-loading works normally afterwards.
Platforms
| Platform | Generator | Build command | Output |
|---|---|---|---|
| Linux | bash | make | .so |
| macOS | bash | make | .dylib |
| Windows | Git Bash / WSL | mingw32-make (MinGW) | .dll |
MSVC one-liner equivalent:
cl /std:c++23 /LD /EHsc /Iinclude mymod.cpp /Fe:mymod.dll
Gotchas
- Keep the plugin's compiler/headers matching the host binary's toolchain — C++ ABIs are not stable across compilers or versions.
- Throw
scrii::ScriptError{status::..., msg}for clean script-visible errors; other exceptions are caught and surfaced too. - The embeddable core provides the same
plugin.hppinterface — see Embedding.