The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
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[@]dogado.de.
This article describes the FFI type names implemented in
Rtinycc. The mapping below is taken from the package type
table and wrapper-generation code, not from a separate design
document.
The core scalar types are:
i8, i16,
i32, i64u8, u16,
u32, u64f32, f64boolcstringptrsexpvoidRtinycc converts R values to these C types inside
generated wrappers, but the R-side scalar carriers depend on what R can
represent directly:
i8, i16, i32,
u8, and u16 are mediated through R integer
scalarsu32, i64, u64,
f32, and f64 are mediated through R numeric
(double) coercion and boxingbool uses R logicalcstring uses an R character scalarSo the FFI names are C-side type names, not promises that R has a
matching native scalar type for every width. Integer-like paths are
validated, not just cast silently, and i64 /
u64 are only exact up to 2^53 on the R
side.
ffi <- tcc_ffi() |>
tcc_source(
"
int add_i32(int a, int b) { return a + b; }
double mul_f64(double x, double y) { return x * y; }
int negate_bool(_Bool x) { return !x; }
"
) |>
tcc_bind(
add_i32 = list(args = list("i32", "i32"), returns = "i32"),
mul_f64 = list(args = list("f64", "f64"), returns = "f64"),
negate_bool = list(args = list("bool"), returns = "bool")
) |>
tcc_compile()
ffi$add_i32(2L, 3L)
#> [1] 5
ffi$mul_f64(2, 4)
#> [1] 8
ffi$negate_bool(TRUE)
#> [1] FALSEcstring and ptr are intentionally
different:
cstring converts an R character scalar into a C
char * view for the callptr passes an external pointer address through
unchangedffi_str <- tcc_ffi() |>
tcc_source(
"
const char* echo_cstring(const char* s) { return s; }
void* echo_ptr(void* p) { return p; }
"
) |>
tcc_bind(
echo_cstring = list(args = list("cstring"), returns = "cstring"),
echo_ptr = list(args = list("ptr"), returns = "ptr")
) |>
tcc_compile()
ptr <- tcc_malloc(8)
ffi_str$echo_cstring("hello")
#> [1] "hello"
inherits(ffi_str$echo_ptr(ptr), "externalptr")
#> [1] TRUE
tcc_free(ptr)
#> NULLThe important semantic difference is discussed in the
boundary-semantics article: returned cstring values are
copied into R strings, while returned ptr values stay as
raw addresses.
The implemented array input types are:
rawinteger_arraynumeric_arraylogical_arraycharacter_arraycstring_arrayThe first four map directly onto R vectors.
character_array passes the underlying CHARSXP
cells as a read-only SEXP *, not a char **.
Use cstring_array when the C side expects a temporary
const char **.
ffi_arr <- tcc_ffi() |>
tcc_source(
"
int first_int(int* x) { return x[0]; }
double second_num(double* x) { return x[1]; }
"
) |>
tcc_bind(
first_int = list(args = list("integer_array"), returns = "i32"),
second_num = list(args = list("numeric_array"), returns = "f64")
) |>
tcc_compile()
ffi_arr$first_int(as.integer(c(10, 20, 30)))
#> [1] 10
ffi_arr$second_num(c(1.5, 2.5))
#> [1] 2.5sexpsexp passes the R object through the wrapper without
conversion.
ffi_sexp <- tcc_ffi() |>
tcc_source(
"
#include <Rinternals.h>
SEXP id_sexp(SEXP x) { return x; }
"
) |>
tcc_bind(
id_sexp = list(args = list("sexp"), returns = "sexp")
) |>
tcc_compile()
ffi_sexp$id_sexp(list(a = 1, b = 2))
#> $a
#> [1] 1
#>
#> $b
#> [1] 2This is the lowest-friction way to cross the boundary when you want to work in terms of the R C API directly rather than the stricter scalar/vector FFI types.
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.
Health stats visible at Monitor.