Skip to content

Embedding in C++

ScriptManager is the single entry point for hosting CxxScript in your application.

1. Create a manager

#include "ScriptManager.h"
using namespace Script;

ScriptManager manager;

2. (Optional) Register external functions and variables

manager.registerExternalFunction("myFunction", [](const std::vector<Value> &args) -> Value {
    return static_cast<int32_t>(42 + std::get<int32_t>(args[0]));
});

int32_t hostValue = 10;
manager.registerExternalVariable(
    "sharedValue",
    [&]() -> Value { return static_cast<int32_t>(hostValue); },
    [&](const Value &v) { hostValue = std::get<int32_t>(v); }
);

See External Functions and External Variables for more detail.

3. Load scripts

std::vector<CompilationError> errors;
if (!manager.loadScriptFile("example.script", errors)) {
    for (const auto &error : errors) {
        std::cout << error.toString() << std::endl;
    }
    return;
}

loadScriptSource(source, filename, errors) loads from an in-memory string instead of a file — useful for scripts embedded as resources or fetched at runtime.

4. Inspect loaded procedures (optional)

for (const auto &name : manager.getProcedureNames()) {
    ScriptManager::ProcedureInfo info;
    manager.getProcedureInfo(name, info);
    std::cout << ValueHelper::typeToString(info.returnType) << " " << info.name << std::endl;
}

5. Execute a procedure

std::vector<Value> arguments = {static_cast<int32_t>(10), static_cast<int32_t>(20)};
Value returnValue;
std::string errorMessage;

if (manager.executeProcedure("calculate", arguments, returnValue, errorMessage)) {
    std::cout << "Result: " << std::get<bool>(returnValue) << std::endl;
} else {
    std::cout << "Error: " << errorMessage << std::endl;
}

API summary

Method Purpose
loadScriptFile(filename, errors) Load and compile a script file
loadScriptSource(source, filename, errors) Load and compile script from a string
checkScript(filename, errors) / checkScriptSource(...) Validate without loading
executeProcedure(name, args, result, error) Execute a loaded procedure
hasProcedure(name) Check if a procedure is loaded
getProcedureNames() / getProcedureInfo(name, info) Introspect loaded procedures
registerExternalFunction(s) / unregisterExternalFunction / hasExternalFunction Manage host callbacks
registerExternalFunctionUnary<Ret, Arg> / registerExternalFunctionBinary<Ret, A1, A2> Typed helpers for common signatures
registerExternalVariable / registerExternalVariableReadOnly / unregisterExternalVariable / hasExternalVariable Expose host state
setExecutionLimits(maxCallDepth, maxSteps) / clearExecutionLimits() Optional runtime guardrails (0 = unlimited)
clear() Reset interpreter state (removes loaded procedures and external bindings)

See the full API Reference for type signatures.

Hardening: bound untrusted scripts

setExecutionLimits is disabled by default (0 = unlimited). If your host executes scripts from a semi-trusted source (config files, business rules authored by non-developers, etc.), set explicit bounds so a malformed or malicious script fails with a runtime error instead of overflowing the native call stack or hanging the process:

ScriptManager manager;
manager.setExecutionLimits(/*maxCallDepth=*/200, /*maxSteps=*/2'000'000);

Both limits apply per top-level executeProcedure call and are shared across the whole call tree, so recursion combined with looping still counts against the same budget. Exceeding either limit surfaces as an ordinary failed executeProcedure call — check errorMessage as usual. Call clearExecutionLimits() to remove the caps again.