Skip to content

arolang/aro-plugin-sdk-c

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 

Repository files navigation

ARO C/C++ Plugin SDK

Single-header SDK for writing ARO plugins in C or C++. Implements the ARO-0073 native plugin ABI and ARO-0045 package manager conventions.

Requirements

  • C11 or later (for _Static_assert, // comments)
  • C++17 or later (for the aro_plugin_sdk.hpp wrapper)
  • GCC, Clang, or MSVC
  • __attribute__((constructor)) support (GCC/Clang; MSVC uses #pragma init_seg — see Portability)

Files

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

Quick Start (C)

/* 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      # Linux

Corresponding 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

Quick Start (C++)

/* 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

API Reference

Plugin Declaration

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"

Reading Input (C)

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.

Array Access (C)

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)

Writing Output (C)

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)

Returning Results (C)

const char* aro_ok(ctx)                          // serialise output and return
const char* aro_error(ctx, code, fmt, ...)       // return error response

Error Codes

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

Qualifier Helpers (C)

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)

C++ Wrappers (aro:: namespace)

// 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/catch

How It Works

When 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 descriptor
  • aro_plugin_execute(action, json) — parses the input JSON using the built-in arena allocator, dispatches to the matching action handler, and returns the output JSON
  • aro_plugin_qualifier(name, json) — same for qualifier handlers
  • aro_plugin_free(ptr) — calls free()

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.

Configuration

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

Running the Tests

cc -std=c11 -Wall -Wextra -I include -o test_sdk test/test_sdk.c && ./test_sdk

With AddressSanitizer:

cc -std=c11 -g -fsanitize=address,undefined -I include -o test_sdk test/test_sdk.c && ./test_sdk

Portability

  • macOS / Linux: full support via __attribute__((constructor))
  • Windows (MSVC): __attribute__((constructor)) is not available. Use /INCLUDE linker flags or call aro__register_action / aro__register_qualifier manually from a DLL entry point (DllMain). GCC/MinGW on Windows supports __attribute__((constructor)) and works out of the box.

License

MIT — see LICENSE for details.

About

ARO Plugin SDK for C/C++ — single-header SDK with JSON parser, macros, and arena allocator

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors