knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) tcc_bind <- Rtinycc::tcc_bind tcc_compile <- Rtinycc::tcc_compile tcc_ffi <- Rtinycc::tcc_ffi tcc_generate_bindings <- Rtinycc::tcc_generate_bindings tcc_map_c_type_to_ffi <- Rtinycc::tcc_map_c_type_to_ffi tcc_source <- Rtinycc::tcc_source tcc_struct <- Rtinycc::tcc_struct tcc_treesitter_bindings <- Rtinycc::tcc_treesitter_bindings tcc_treesitter_functions <- Rtinycc::tcc_treesitter_functions tcc_treesitter_struct_accessors <- Rtinycc::tcc_treesitter_struct_accessors has_treesitter <- requireNamespace("treesitter.c", quietly = TRUE) && packageVersion("treesitter.c") >= "0.0.4"
Rtinycc can build FFI bindings from C declarations instead of requiring every
signature to be written manually. The parsing layer is provided by the optional
treesitter.c package, while Rtinycc is responsible for mapping parsed C
types into its own FFI type system.
This workflow is useful when:
These helpers require the optional treesitter.c package.
has_treesitter
If FALSE, the parsing functions are unavailable and the executable examples in
this vignette are skipped.
header <- paste( "double sqrt(double x);", "int add(int a, int b);", "struct point { double x; double y; };", "enum status { OK = 0, ERR = 1 };", sep = "\n" )
The lowest-level helpers return parsed declarations from the header:
tcc_treesitter_functions(header)
For quick inspection of functions only, tcc_treesitter_bindings() converts the
parsed signatures into tcc_bind()-ready specs:
tcc_treesitter_bindings(header)
For a fuller workflow, use tcc_generate_bindings() on a tcc_ffi object that
already has matching C source attached:
ffi <- tcc_ffi() |> tcc_source( " double sqrt(double x) { return x < 0 ? 0 : x; } int add(int a, int b) { return a + b; } struct point { double x; double y; }; enum status { OK = 0, ERR = 1 }; " ) ffi <- tcc_generate_bindings( ffi, header, functions = TRUE, structs = TRUE, enums = TRUE, unions = FALSE, globals = FALSE ) compiled <- tcc_compile(ffi) compiled$add(2L, 3L) compiled$enum_status_OK()
This keeps the parsing step separate from the actual compilation step, which is important when you want to inspect or edit the generated binding plan first.
You can also extract just the generated struct accessors:
tcc_treesitter_struct_accessors("struct point { double x; double y; };")
That output is what Rtinycc feeds into tcc_struct() when you ask it to
generate struct bindings from a header.
Bitfields now stay explicit in the accessor metadata rather than collapsing to a bare scalar type:
tcc_treesitter_struct_accessors( "struct flags { unsigned int flag : 1; unsigned int code : 6; };" )
Nested struct fields in structs currently still fall back to ptr-like accessors:
tcc_treesitter_struct_accessors( "struct child { int x; }; struct outer { struct child child; int y; };" )
For unions, nested struct members preserve list(type = "struct", ...) so the
generated helper remains a borrowed nested view rather than an opaque raw
pointer.
The default mapper is intentionally conservative. In particular, pointer types are not automatically treated as C strings unless that is semantically safe.
tcc_map_c_type_to_ffi("int") tcc_map_c_type_to_ffi("double") tcc_map_c_type_to_ffi("const char *")
If you know a specific API uses const char * as a real NUL-terminated string,
you can override the mapping:
string_mapper <- function(type) { if (trimws(type) == "const char *") { return("cstring") } tcc_map_c_type_to_ffi(type) } tcc_treesitter_bindings( "int puts(const char *s);", mapper = string_mapper )
This is the intended extension point: keep the default mapper strict, then relax specific cases where you know the source API contract.
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.