The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.
This article covers the more structured parts of the FFI surface: array-return specifications, callbacks, globals, and generated struct helpers.
Array returns use a structured return specification rather than a bare type string. The implemented form is:
type: one of the supported array typeslength_arg: which argument provides the returned
lengthfree: whether the wrapper should free()
the returned C buffer after copyingffi <- tcc_ffi() |>
tcc_source(
"
#include <stdlib.h>
int* dup_array(int* x, int n) {
if (n <= 0) return NULL;
int* out = (int*)malloc(sizeof(int) * n);
for (int i = 0; i < n; ++i) out[i] = x[i] * 2;
return out;
}
"
) |>
tcc_bind(
dup_array = list(
args = list("integer_array", "i32"),
returns = list(type = "integer_array", length_arg = 2, free = TRUE)
)
) |>
tcc_compile()
ffi$dup_array(as.integer(c(1, 2, 3)), 3L)
#> [1] 2 4 6The wrapper copies the returned buffer into a fresh R vector. It does not hand that C buffer to R by reference.
Callback arguments use callback:<signature> or
callback_async:<signature>.
cb <- tcc_callback(function(x) x * 3, "double (*)(double)")
cb_ptr <- tcc_callback_ptr(cb)
ffi_cb <- tcc_ffi() |>
tcc_source(
"
double apply_cb(double (*cb)(void* ctx, double), void* ctx, double x) {
return cb(ctx, x);
}
"
) |>
tcc_bind(
apply_cb = list(
args = list("callback:double(double)", "ptr", "f64"),
returns = "f64"
)
) |>
tcc_compile()
ffi_cb$apply_cb(cb, cb_ptr, 5)
#> [1] 15
tcc_callback_close(cb)The callback object owns the registered R function. The callback pointer is the user-data token passed into the generated trampoline.
For callback_async:<signature> specifically:
i64, u32, and u64 async
arguments and returns travel through R numeric (double), so
only exact integer values up to 2^53 round-trip
exactlytcc_callback_async_drain() explicitly in
tight loops and teststcc_global() generates getter and setter wrappers for C
globals.
ffi_global <- tcc_ffi() |>
tcc_source(
"
int global_counter = 7;
"
) |>
tcc_global("global_counter", "i32") |>
tcc_compile()
ffi_global$global_global_counter_get()
#> [1] 7
ffi_global$global_global_counter_set(9L)
#> [1] 9
ffi_global$global_global_counter_get()
#> [1] 9Globals are limited to scalar FFI types. Array globals are rejected by the API.
For C structs, Rtinycc can generate allocation, getter,
setter, and free helpers.
ffi_struct <- tcc_ffi() |>
tcc_source(
"
struct point {
double x;
double y;
};
"
) |>
tcc_struct("point", accessors = c(x = "f64", y = "f64")) |>
tcc_compile()
pt <- ffi_struct$struct_point_new()
pt <- ffi_struct$struct_point_set_x(pt, 1.5)
pt <- ffi_struct$struct_point_set_y(pt, 2.5)
ffi_struct$struct_point_get_x(pt)
#> [1] 1.5
ffi_struct$struct_point_free(pt)
#> NULLThese helpers are separate from tcc_bind() because they
expose storage and layout rather than just call signatures.
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.