Claude
Skills
Sign in
Back

greycat-c

Included with Lifetime
$97 forever

GreyCat C API and GCL Standard Library reference. Use for: (1) Native C development with gc_machine_t context, tensors, objects, memory management, crypto, I/O; (2) GCL Standard Library modules - std::core (Date/Time/Tuple/geospatial types), std::runtime (Scheduler/Task/Logger/User/Security/System/OpenAPI/MCP), std::io (CSV/JSON/XML/HTTP/Email/FileWalker), std::util (Queue/Stack/SlidingWindow/Gaussian/Histogram/Quantizers/Random/Plot); (3) Plugin development patterns - lifecycle hooks, type configuration, nativegen, module-level and type-level function linking, global state, thread safety, conditional logging. Keywords: GreyCat, GCL, native functions, tensors, task automation, scheduler, plugin development.

Backend & APIs

What this skill does


# GreyCat SDK - C API, Standard Library & Plugin Development

Comprehensive reference for GreyCat native development (C API), the GCL Standard Library, and plugin development patterns. Tracks SDK **2.5.6**.

## Key Considerations

- **Allocator API is mandatory.** Every non-trivial allocation routes through an explicit `gc_allocator_t *`. Per-call scratch comes from `gc_machine__allocator(ctx)` (or `((gc_ctx_t *)ctx)->allocator`); plugin-global state comes from `gc_host__global_allocator()` (a convenience wrapper around `gc_host__allocator(gc_host__get_global())`). The thread-bound helpers `gc_malloc` / `gc_free` / `gc_realloc` are public API too — they target whichever allocator is currently bound to the calling thread via `gc_alloc__bind`.
- **`gc_alloc__create(bool shared)`** — creating your own allocator now takes a `shared` flag. Pass `true` for arenas touched by multiple threads, `false` for a thread-private arena. `gc_alloc__allocated` and `gc_alloc__stats` are exposed for live-bytes accounting and debug dumps.
- **New `gc/log.h` module.** Structured logging is now first-class: `gc_log_level_t` plus `gc_log__machine` / `gc_log__machinef` (VM context) and `gc_log__host` / `gc_log__hostf` (host context). Use `gc_log__enabled(host, level)` to skip work in cold paths.
- **`gc/str.h` (inline short strings) is gone.** `gc_str_t`, `gc_core_str__encode/_add_to_buffer`, and the `gc_core_str` / `gc_core_t2…t4f` extern globals are no longer part of the public SDK. Use `gc_string_t` for all string handling.
- **`gc_machine__call_function` takes a `const gc_program_function_t *fn`** (not a raw function-body pointer). On `false` the result is a synthesized `Error` object (type `gc_core_Error`); the caller owns one mark on the result. `gc_machine__impersonate(ctx, user_id)` is new for permission-aware sub-calls. `gc_machine__allocator(ctx)` is the documented sugar for the per-call allocator.
- **Scheduler API** in `gc/host.h`: `gc_scheduler_t`, `gc_periodic_task_t`, `gc_periodicity_t` (fixed / daily / weekly / monthly / yearly configs), `gc_scheduler__add/activate/deactivate/create_object`.
- **ABI**: `gc_abi_header_check_error_truncated = 4` is a new variant of `gc_abi_header_check_error_t`. `gc_abi_t` carries its own allocator.
- **Iterator params**: `gc_program_iterator_param_t` lost the old `limit` variant. Values are now `from=0`, `to=1`, `nullable=2`, `from_excl=3`, `to_excl=4`.
- **Geo constant rename**: `GC_CORE_GEO_LAT_EPS` is now `GC_CORE_GEO_EPS`.

## Contents

1. **C API** - Native function implementation, tensor operations, object manipulation, maps, arrays, tables, geospatial, time/date, crypto, buffers, I/O
2. **Standard Library (std)** - GCL runtime features, I/O, collections, and utilities
3. **Plugin Development** - Complete guide to building native plugins with lifecycle hooks, type configuration, and real-world patterns

---

# GreyCat C API

## Core Concepts

**gc_machine_t** - Execution context passed to all native functions. Use to get parameters, set results, report errors, create objects, and access scratch buffers.

**gc_slot_t** - Universal value container (tagged union) holding any GreyCat value: integers, floats, bools, objects, enums, tuples, etc.

**gc_type_t** - Type system enum (8-bit, 24 values) defining all GreyCat types: null, bool, char, int, float, node variants, geo, time, duration, cubic, static_field, object, block_ref, block_inline, function, undefined, type, field, stringlit, error.

**gc_object_t** - Generic handle for heap-allocated objects. Packed to 128 bits. Every collection type (Array, Map, Table, Tensor, String, Buffer) starts with this as its first member.

## Common Operations

**Parameter handling:**
```c
gc_slot_t param = gc_machine__get_param(ctx, 0);        // 0-indexed
gc_type_t type = gc_machine__get_param_type(ctx, 0);
u32_t count = gc_machine__get_param_nb(ctx);
gc_slot_t self = gc_machine__this(ctx);                  // 'this' for instance methods
```

**Enum parameter handling (CRITICAL — common source of bugs):**

GCL enum values are **NOT** `gc_type_int`. They are `gc_type_static_field` and the ordinal is in `.tu32.right`, not `.i64`.

```c
// WRONG — enum will always hit the default fallback:
i64_t variant = (gc_machine__get_param_type(ctx, 0) == gc_type_int) ? slot.i64 : 0;

// CORRECT — reads the enum ordinal properly:
i64_t variant = (gc_machine__get_param_type(ctx, 0) == gc_type_static_field) ? (i64_t)slot.tu32.right : 0;
```

The `.tu32` field is a struct with `.left` (type offset, identifies the enum type) and `.right` (value offset / ordinal within the enum). For dispatch purposes you almost always want `.tu32.right`.

**Setting results:**
```c
gc_machine__set_result(ctx, (gc_slot_t){.i64 = 42}, gc_type_int);
gc_machine__set_result(ctx, (gc_slot_t){.f64 = 3.14}, gc_type_float);
gc_machine__set_result(ctx, (gc_slot_t){.b = true}, gc_type_bool);
gc_machine__set_result(ctx, (gc_slot_t){.object = obj}, gc_type_object);
gc_object__un_mark(obj, ctx);  // CRITICAL for objects -- prevents premature GC
// Returning an enum value (e.g., MyEnum::variant2 where variant2 is ordinal 1):
gc_machine__set_result(ctx, (gc_slot_t){.tu32 = {.left = 0, .right = 1}}, gc_type_static_field);
```

**Error handling:**
```c
gc_machine__set_runtime_error(ctx, "Something failed");
gc_machine__set_runtime_error_syserr(ctx);  // Uses errno
if (gc_machine__error(ctx)) return;         // Check propagated errors
```

**Object field access:**
```c
gc_slot_t value = gc_object__get_at(obj, field_offset, &type, ctx);
gc_object__set_at(obj, field_offset, value, value_type, ctx);
gc_object__declare_dirty(obj);  // Mark modified for persistence write-back
```

**Object creation:**
```c
gc_object_t *obj = gc_machine__create_object(ctx, gc_core_Map);
gc_object_t *ret = gc_machine__create_return_type_object(ctx);
```

**Tensor operations:**
```c
gc_core_tensor_t *t = gc_core_tensor__create(ctx);
gc_core_tensor__init_2d(t, rows, cols, gc_core_TensorType_f32, ctx);
f32_t val = gc_core_tensor__get_2d_f32(t, row, col, ctx);
gc_core_tensor__set_2d_f32(t, row, col, 3.14f, ctx);
f64_t *raw = (f64_t *)gc_core_tensor__get_data(t);  // Direct memory access
```

**Array operations:**
```c
gc_array_t *arr = (gc_array_t *)gc_machine__create_object(ctx, gc_core_Array);
gc_array__add_slot(arr, (gc_slot_t){.i64 = 42}, gc_type_int, ctx);
gc_array__get_slot(arr, 0, &value, &type);
gc_array__set_slot(arr, 0, value, type, ctx);
```

**Map operations:**
```c
gc_map_t *map = (gc_map_t *)gc_machine__create_object(ctx, gc_core_Map);
gc_map__set(map, key, key_type, value, value_type, ctx);
gc_slot_t val = gc_map__get(map, key, key_type, &val_type, prog);
bool found = gc_map__contains(map, key, key_type, prog);
```

**String operations:**
```c
gc_string_t *s = gc_string__create_from(data, len, ctx);
gc_string_t *s2 = gc_string__create_concat(buf1, len1, buf2, len2, ctx);
// Note: gc_string_t.buffer is NOT null-terminated. Use .size for length.
```

**Logging (gc/log.h):**
```c
// VM context (decorates with module::Type::fn + user/task ids):
gc_log__machinef(ctx, gc_log_level_info, "warmed cache");

// Host context (no VM frame):
gc_host_t *host = gc_host__get_global();
if (gc_log__enabled(host, gc_log_level_perf)) {
    gc_log__hostf(host, gc_log_level_perf, "ingest loop done");
}
```

**Memory management — allocator-first API:**

| Allocator | Lifecycle | When to use |
|-----------|-----------|-------------|
| `gc_machine__allocator(ctx)` (= `((gc_ctx_t *)ctx)->allocator`) | Per-native-call; reclaimed when the call ends | Default for everything inside a native: scratch buffers, intermediate arrays, per-call result strings. |
| `gc_host__global_allocator()` (= `gc_host__allocator(gc_host__get_global())`) | Plugin-global, persists across threads and calls | `lib_start` / `lib_stop` state, global caches, precomputed lookup tables. Protect with your own mutex if shared across workers. |

```c
// Per-call scratch (default):
gc_allocator_t *a = gc_machine__

Related in Backend & APIs