Single-header SDK for writing ARO plugins in C or C++. Implements the ARO-0073 native plugin ABI and ARO-0045 package manager conventions.
- C11 or later (for
_Static_assert,//comments) - C++17 or later (for the
aro_plugin_sdk.hppwrapper) - GCC, Clang, or MSVC
__attribute__((constructor))support (GCC/Clang; MSVC uses#pragma init_seg— see Portability)
| File | Purpose |
|---|---|
include/aro_plugin_sdk.h |
Main C SDK (stb-style, define ARO_PLUGIN_SDK_IMPLEMENTATION in one TU) |
include/aro_plugin_sdk.hpp |
C++ wrapper with type-safe templates and aro:: namespace |
test/test_sdk.c |
Self-contained test suite |
/* my_plugin.c */
#define ARO_PLUGIN_SDK_IMPLEMENTATION
#include "aro_plugin_sdk.h"
/* Declare plugin identity */
ARO_PLUGIN("my-plugin", "1.0.0")
ARO_HANDLE("MyPlugin") /* PascalCase handle used as action prefix */
/* Optional: one-time setup */
ARO_INIT() {
/* called once before the first action invocation */
}
/* Optional: cleanup on unload */
ARO_SHUTDOWN() {
/* called when the plugin is unloaded */
}
/* Declare an action */
ARO_ACTION("Greet", "own", "with") {
const char* name = aro_input_string(ctx, "name");
if (!name) name = "World";
char greeting[256];
snprintf(greeting, sizeof(greeting), "Hello, %s!", name);
aro_output_string(ctx, "greeting", greeting);
return aro_ok(ctx);
}
/* Declare a qualifier */
ARO_QUALIFIER("reverse", "List", "Reverse a list") {
aro_array* arr = aro_qualifier_array(ctx);
size_t n = aro_array_length(arr);
/* Build reversed array... */
/* ... */
return aro_qualifier_result_array(ctx, arr);
}Compile:
cc -std=c11 -shared -fPIC -I path/to/include \
-o libmy-plugin.dylib my_plugin.c # macOS
-o libmy-plugin.so my_plugin.c # LinuxCorresponding plugin.yaml:
name: my-plugin
version: 1.0.0
handle: MyPlugin
description: My first ARO plugin
provides:
- type: c-plugin
path: src/
build:
compiler: clang
flags: [-O2, -fPIC, -shared]
output: libmy-plugin.dylib/* my_plugin.cpp */
#define ARO_PLUGIN_SDK_IMPLEMENTATION
#include "aro_plugin_sdk.hpp" /* includes aro_plugin_sdk.h automatically */
#include <algorithm>
#include <cctype>
ARO_PLUGIN("my-plugin", "1.0.0")
ARO_HANDLE("MyPlugin")
ARO_ACTION("Sort", "own", "with") {
auto items = aro::input_array<std::string>(ctx, "items");
std::sort(items.begin(), items.end());
aro::output_array(ctx, "sorted", items);
return aro_ok(ctx);
}
ARO_QUALIFIER("uppercase", "String", "Convert to uppercase") {
auto s = aro::qualifier_string(ctx);
std::string up = s;
for (auto& c : up) c = (char)std::toupper((unsigned char)c);
return aro::qualifier_result(ctx, up);
}Compile:
c++ -std=c++17 -shared -fPIC -I path/to/include \
-o libmy-plugin.dylib my_plugin.cpp| Macro | Description |
|---|---|
ARO_PLUGIN(name, version) |
Set plugin name and version. Place once at file scope. |
ARO_HANDLE(handle) |
Set the PascalCase handle used to namespace actions and qualifiers. |
ARO_INIT() |
Define the optional initialisation body. |
ARO_SHUTDOWN() |
Define the optional cleanup body. |
ARO_ACTION(name, role, prep) |
Register and define an action handler. |
ARO_QUALIFIER(name, types, desc) |
Register and define a qualifier. |
ARO_QUALIFIER_WITH_PARAMS(name, types, desc) |
Same, but marks accepts_parameters = true. |
ARO_SYSTEM_OBJECT(name, caps) |
Declare a system object (e.g. "config-store", "read,write"). |
Action roles: "request" | "own" | "response" | "export"
Prepositions: comma-separated list, e.g. "from,with" or "to"
All input helpers look up the key first in the _with clause, then at the top level of the input JSON.
const char* aro_input_string(ctx, key) // string value or NULL
int64_t aro_input_int(ctx, key) // integer value or 0
double aro_input_double(ctx, key) // double value or 0.0
int aro_input_bool(ctx, key) // 1/0 or 0
aro_array* aro_input_array(ctx, key) // array or NULL
const char* aro_input_result_base(ctx) // result binding name
const char* aro_input_source_base(ctx) // source binding name
const char* aro_input_preposition(ctx) // "from", "with", etc.
const char* aro_with_string(ctx, key, default) // with-clause string
int64_t aro_with_int(ctx, key, default) // with-clause integer
const char* aro_context_string(ctx, key) // _context["featureSet"] etc.size_t aro_array_length(arr)
const char* aro_array_string(arr, idx)
int64_t aro_array_int(arr, idx)
double aro_array_double(arr, idx)void aro_output_string(ctx, key, value)
void aro_output_int(ctx, key, int64_t)
void aro_output_double(ctx, key, double)
void aro_output_bool(ctx, key, int)
void aro_output_string_array(ctx, key, const char** items, size_t count)const char* aro_ok(ctx) // serialise output and return
const char* aro_error(ctx, code, fmt, ...) // return error response| Constant | Value | Meaning |
|---|---|---|
ARO_ERR_SUCCESS |
0 | Success |
ARO_ERR_INVALID_INPUT |
1 | Bad or missing input |
ARO_ERR_NOT_FOUND |
2 | Resource not found |
ARO_ERR_PERMISSION_DENIED |
3 | Permission denied |
ARO_ERR_TIMEOUT |
4 | Operation timed out |
ARO_ERR_CONNECTION_FAILED |
5 | Connection failed |
ARO_ERR_EXECUTION_FAILED |
6 | Execution failed |
ARO_ERR_INVALID_STATE |
7 | Invalid state |
ARO_ERR_RESOURCE_EXHAUSTED |
8 | Resources exhausted |
ARO_ERR_UNSUPPORTED |
9 | Not supported |
ARO_ERR_RATE_LIMITED |
10 | Rate limited |
Qualifiers receive the value-to-transform and must return a result:
const char* aro_qualifier_string(ctx) // input as string
aro_array* aro_qualifier_array(ctx) // input as array
int64_t aro_qualifier_int(ctx) // input as integer
const char* aro_qualifier_result_string(ctx, val)
const char* aro_qualifier_result_int(ctx, val)
const char* aro_qualifier_result_array(ctx, arr)
int64_t aro_qualifier_param_int(ctx, key, default)
const char* aro_qualifier_param_string(ctx, key, default)// Input
std::string aro::input_string(ctx, key, default="")
int64_t aro::input_int(ctx, key, default=0)
double aro::input_double(ctx, key, default=0.0)
bool aro::input_bool(ctx, key)
std::vector<T> aro::input_array<T>(ctx, key) // T: string, int64_t, double
std::string aro::result_base(ctx)
std::string aro::source_base(ctx)
std::string aro::preposition(ctx)
std::string aro::with_string(ctx, key, default="")
int64_t aro::with_int(ctx, key, default=0)
std::string aro::context_string(ctx, key)
// Output
void aro::output_string(ctx, key, std::string)
void aro::output_int(ctx, key, int64_t)
void aro::output_double(ctx, key, double)
void aro::output_bool(ctx, key, bool)
void aro::output_array<T>(ctx, key, std::vector<T>)
// Qualifiers
std::string aro::qualifier_string(ctx)
std::vector<T> aro::qualifier_array<T>(ctx)
int64_t aro::qualifier_int(ctx)
const char* aro::qualifier_result(ctx, std::string)
const char* aro::qualifier_result_array(ctx, std::vector<std::string>)
std::string aro::qualifier_param_string(ctx, key, default="")
int64_t aro::qualifier_param_int(ctx, key, default=0)
// Exception boundary
char* aro::safe_call(action, json, fn) // wraps fn in try/catchWhen the plugin shared library is loaded, each ARO_ACTION / ARO_QUALIFIER macro emits a __attribute__((constructor)) function that registers the entry in a table. The SDK then generates:
aro_plugin_info()— walks the tables to build a JSON descriptoraro_plugin_execute(action, json)— parses the input JSON using the built-in arena allocator, dispatches to the matching action handler, and returns the output JSONaro_plugin_qualifier(name, json)— same for qualifier handlersaro_plugin_free(ptr)— callsfree()
The JSON parser is a minimal, zero-dependency, arena-backed implementation that handles the exact JSON shape produced by the ARO runtime (ARO-0073). No external dependencies required.
Define these before including the header to override defaults:
| Macro | Default | Description |
|---|---|---|
ARO_MAX_ACTIONS |
128 | Maximum number of registered actions |
ARO_MAX_QUALIFIERS |
128 | Maximum number of registered qualifiers |
ARO_MAX_SYSOBJS |
32 | Maximum number of system objects |
ARO_OUTPUT_BUF |
65536 (64 KB) | Per-invocation output buffer size |
cc -std=c11 -Wall -Wextra -I include -o test_sdk test/test_sdk.c && ./test_sdkWith AddressSanitizer:
cc -std=c11 -g -fsanitize=address,undefined -I include -o test_sdk test/test_sdk.c && ./test_sdk- macOS / Linux: full support via
__attribute__((constructor)) - Windows (MSVC):
__attribute__((constructor))is not available. Use/INCLUDElinker flags or callaro__register_action/aro__register_qualifiermanually from a DLL entry point (DllMain). GCC/MinGW on Windows supports__attribute__((constructor))and works out of the box.
MIT — see LICENSE for details.