Rtinycc bundles TinyCC and exposes two layers:
libtccThe package is designed around explicit data conversion. You declare
the C signature you want, Rtinycc generates the wrapper
code, and the compiled object exposes ordinary R-callable functions.
The bundled TinyCC installation is discoverable from R:
tcc_path()
#> [1] "/tmp/RtmpnGtaiV/Rinstb110b545d486a/Rtinycc/tinycc/bin/tcc"
tcc_include_paths()
#> [1] "/tmp/RtmpnGtaiV/Rinstb110b545d486a/Rtinycc/tinycc/include"
#> [2] "/tmp/RtmpnGtaiV/Rinstb110b545d486a/Rtinycc/tinycc/lib/tcc/include"
tcc_lib_paths()
#> [1] "/tmp/RtmpnGtaiV/Rinstb110b545d486a/Rtinycc/tinycc/lib/tcc"
#> [2] "/tmp/RtmpnGtaiV/Rinstb110b545d486a/Rtinycc/tinycc/lib"These helpers are useful when you need to inspect the bundled
toolchain or bridge Rtinycc with other build logic.
The usual entry point is tcc_ffi(). You add C source,
declare the function signatures you want to expose, and compile
once:
ffi <- tcc_ffi() |>
tcc_source(
"
static double affine(double x, double slope, double intercept) {
return slope * x + intercept;
}
"
) |>
tcc_bind(
affine = list(
args = list("f64", "f64", "f64"),
returns = "f64"
)
) |>
tcc_compile()
ffi$affine(2, 3, 4)
#> [1] 10The wrapper takes care of converting R numerics to
double and converting the C return value back to an R
scalar.
You can also expose C globals through generated getter and setter helpers:
ffi_globals <- tcc_ffi() |>
tcc_source(
"
int shared_counter = 1;
void bump_counter(void) {
shared_counter += 1;
}
"
) |>
tcc_bind(
bump_counter = list(args = list(), returns = "void")
) |>
tcc_global("shared_counter", "i32") |>
tcc_compile()
ffi_globals$global_shared_counter_get()
#> [1] 1
ffi_globals$bump_counter()
#> NULL
ffi_globals$global_shared_counter_get()
#> [1] 2This pattern is useful for wrapping small stateful C helpers without
dropping to the lower-level libtcc API.
tcc_link()tcc_compile() is for source you provide directly.
tcc_link() is for binding to functions that already exist
in a shared library. The high-level binding format is the same, so you
can move between generated code and linked code without changing the
R-side calling convention. See the
Linking External Libraries vignette for the
external-linking workflow and the platform-specific runtime notes.