| Title: | Simple Foreign Function Interface using 'S7' and 'libffi' |
|---|---|
| Description: | Simple Foreign Function Interface for 'R' using 'libffi' and 'S7' classes. Supports calling 'C' functions with type conversion and struct handling. Includes standard 'C' types (int8, int16, int32, int64, uint variants), platform types (size_t, bool), floating point types, and complex struct types. Header parsing uses 'Rtinycc' for 'TinyCC'-backed preprocessing and enables automatic generation of 'R' bindings from 'C' header files, simplifying package development for 'C' libraries. |
| Authors: | Sounkou Mahamane Toure [aut, cre], Anthony Green, Red Hat, Inc and others [ctb] (libffi authors and COPYRIGHT holders) |
| Maintainer: | Sounkou Mahamane Toure <[email protected]> |
| License: | GPL-3 |
| Version: | 1.2.0.90000 |
| Built: | 2026-05-11 23:13:29 UTC |
| Source: | https://github.com/sounkou-bioinfo/RSimpleFFI |
FFI Array Type
ArrayType( name = character(0), size = integer(0), ref = NULL, element_type = FFIType(), length = integer(0) )ArrayType( name = character(0), size = integer(0), ref = NULL, element_type = FFIType(), length = integer(0) )
name |
Character name of the type |
size |
Integer size in bytes |
ref |
External pointer to ffi_type |
element_type |
FFIType of array elements |
length |
Integer length of array |
An ArrayType object
Parses R's header files (Rinternals.h, R.h, Rmath.h) and generates R bindings that allow calling R's internal C functions directly via FFI.
bindgen_r_api( output_file = NULL, headers = c("Rinternals.h", "R.h", "Rmath.h"), include_path = R.home("include"), load_r_lib = .is_windows, verbose = FALSE )bindgen_r_api( output_file = NULL, headers = c("Rinternals.h", "R.h", "Rmath.h"), include_path = R.home("include"), load_r_lib = .is_windows, verbose = FALSE )
output_file |
Path to write the generated R bindings. If NULL, returns the parsed results without writing. |
headers |
Character vector of header names to parse. Default includes "Rinternals.h", "R.h", and "Rmath.h". |
include_path |
Path to R's include directory. Defaults to |
load_r_lib |
Logical. If TRUE (default on Windows), attempt to load the R shared library to ensure symbols are available. |
verbose |
Logical. If TRUE, print progress messages. |
On Unix-like systems (Linux, macOS), R's symbols are typically available
in the running process. On Windows, the R.dll may need to be explicitly
loaded. Set load_r_lib = TRUE to handle this automatically.
The generated bindings create wrapper functions that use ffi_function()
to call the underlying C functions. Each wrapper includes roxygen2
documentation with parameter types and return values.
Invisibly returns a list with parsed results for each header:
structs |
Named list of struct definitions |
functions |
Named list of function signatures |
typedefs |
Named list of typedef mappings |
enums |
Named list of enum definitions |
Core R internals: SEXP manipulation, memory management, type checking functions (Rf_isInteger, Rf_length, etc.)
Main R header, includes standard utilities
Statistical distribution functions (dnorm, pnorm, qnorm, gamma, beta, etc.)
ffi_parse_header(), generate_r_bindings(), ffi_function()
## Not run: # Generate bindings to a file bindgen_r_api(output_file = "r_api_bindings.R") # Parse without writing (for inspection) result <- bindgen_r_api(verbose = TRUE) names(result$Rinternals$functions) # Generate only Rmath bindings bindgen_r_api( output_file = "rmath_bindings.R", headers = "Rmath.h" ) # After sourcing generated bindings: # source("r_api_bindings.R") # r_Rf_dnorm4(0, 0, 1, 0L) # same as dnorm(0, 0, 1) ## End(Not run)## Not run: # Generate bindings to a file bindgen_r_api(output_file = "r_api_bindings.R") # Parse without writing (for inspection) result <- bindgen_r_api(verbose = TRUE) names(result$Rinternals$functions) # Generate only Rmath bindings bindgen_r_api( output_file = "rmath_bindings.R", headers = "Rmath.h" ) # After sourcing generated bindings: # source("r_api_bindings.R") # r_Rf_dnorm4(0, 0, 1, 0L) # same as dnorm(0, 0, 1) ## End(Not run)
Quick summary of what's available in R's C API headers, including all headers in the main include directory and the R_ext subdirectory.
bindgen_r_api_summary(include_path = R.home("include"))bindgen_r_api_summary(include_path = R.home("include"))
include_path |
Path to R's include directory |
Data frame with header info including name, exists flag, size, and category
## Not run: bindgen_r_api_summary() ## End(Not run)## Not run: bindgen_r_api_summary() ## End(Not run)
FFI Call Interface (CIF)
CIF(return_type = FFIType(), arg_types = list(), ref = NULL)CIF(return_type = FFIType(), arg_types = list(), ref = NULL)
return_type |
FFIType for return value |
arg_types |
List of FFIType objects for arguments |
ref |
External pointer to ffi_cif |
An CIF object
Create built-in FFI type
create_builtin_type
create_builtin_type(name, ...)create_builtin_type(name, ...)
name |
Character name of built-in type |
... |
Additional arguments (not used) |
An FFIType object
FFIType object for bool
Returns an external pointer to the underlying data (INTEGER, REAL, etc.) while ensuring the object won't be garbage collected.
data_ptr(x)data_ptr(x)
x |
An R vector (integer, double, complex, character, raw, or list) |
Note: For ALTREP objects (like 1:10), use data_ptr_ro() instead,
which properly handles deferred materialization.
External pointer to the data, with finalizer to release protection
## Not run: x <- c(1.0, 2.0, 3.0) # Regular vector, not ALTREP ptr <- data_ptr(x) # ptr points to the double* array # Safe to pass to C functions expecting double* ## End(Not run)## Not run: x <- c(1.0, 2.0, 3.0) # Regular vector, not ALTREP ptr <- data_ptr(x) # ptr points to the double* array # Safe to pass to C functions expecting double* ## End(Not run)
Like data_ptr() but attempts read-only access first (for ALTREP support).
If the ALTREP implementation doesn't provide direct access, falls back to
materializing the data (which may allocate memory).
data_ptr_ro(x)data_ptr_ro(x)
x |
An R vector |
External pointer to the data
This creates an FFI function wrapper for dynamically loaded native C functions. Uses direct address access like Rffi for maximum compatibility.
dll_ffi_symbol(symbol_name, return_type, ..., package = NULL, na_check = TRUE)dll_ffi_symbol(symbol_name, return_type, ..., package = NULL, na_check = TRUE)
symbol_name |
Name of the symbol |
return_type |
Return type specifimessageion |
... |
Argument type specifimessageions |
package |
Package name (optional) |
na_check |
Logical; if TRUE (default), check for NA values and error if found. Set to FALSE to skip NA checking for better performance (at your own risk). |
FFI function object that can be called directly
Get information about a loaded library
dll_info(handle)dll_info(handle)
handle |
Library handle (path) |
List with library information
Check if a symbol is loaded
dll_is_loaded(symbol_name, package = NULL)dll_is_loaded(symbol_name, package = NULL)
symbol_name |
Name of the symbol to check |
package |
Package name (optional) |
TRUE if symbol is loaded, FALSE otherwise
List loaded libraries
dll_list_loaded()dll_list_loaded()
Character vector of loaded library paths
This function compiles C code using R's configured compiler and loads it. Uses the same compiler configuration that R was built with.
dll_load(filename, now = TRUE, local = TRUE, verbose = FALSE) dll_unload(handle, verbose = FALSE) dll_symbol(symbol_name, package = NULL) dll_compile_and_load( code, name = "temp_dll", includes = NULL, libs = NULL, verbose = FALSE, cflags = NULL, compilation_directory = tempfile("dll_compile_") ) dll_load_system(lib_name, verbose = FALSE) dll_load_r(verbose = FALSE)dll_load(filename, now = TRUE, local = TRUE, verbose = FALSE) dll_unload(handle, verbose = FALSE) dll_symbol(symbol_name, package = NULL) dll_compile_and_load( code, name = "temp_dll", includes = NULL, libs = NULL, verbose = FALSE, cflags = NULL, compilation_directory = tempfile("dll_compile_") ) dll_load_system(lib_name, verbose = FALSE) dll_load_r(verbose = FALSE)
filename |
Path to the shared library |
now |
Whether to resolve all symbols immediately (default TRUE) |
local |
Keep symbols local to avoid namespace pollution (default TRUE) |
verbose |
Print loading information (default FALSE) |
handle |
Library handle (path) returned by dll_load() |
symbol_name |
Name of the symbol to find |
package |
Package name where symbol is registered (optional) |
code |
Character vector of C code to compile |
name |
Base name for the compiled library (default "temp_dll") |
includes |
Additional include directories |
libs |
Additional libraries to link |
cflags |
Additional compiler flags (e.g., "-O2", "-O3") |
compilation_directory |
Directory to use for compilation (default temp dir) |
lib_name |
Name of system library (e.g., libc.so.6, libm.dylib, kernel32.dll) |
Library handle (character string of loaded library path)
dyn.unload result invisibly
Symbol information including address as external pointer
Library handle that can be used with dll_* functions
Library handle or NULL if not found
Library handle or NULL if not found
FFI Enumeration Type
EnumType( name = character(0), size = integer(0), ref = NULL, values = integer(0), underlying_type = FFIType() )EnumType( name = character(0), size = integer(0), ref = NULL, values = integer(0), underlying_type = FFIType() )
name |
Character name of the type |
size |
Integer size in bytes |
ref |
External pointer to ffi_type |
values |
Named integer vector of enum values |
underlying_type |
FFIType for the underlying integer type |
Escape R name with backticks if needed
escape_r_name(name)escape_r_name(name)
name |
Variable name to escape |
Escaped name if needed, original otherwise
Returns a named integer vector with byte offsets for all fields. For packed structs, uses the pack alignment setting.
ffi_all_offsets(struct_type, use_pack = TRUE)ffi_all_offsets(struct_type, use_pack = TRUE)
struct_type |
StructType object |
use_pack |
Logical, whether to use the struct's pack setting. Default TRUE. |
Named integer vector of offsets
## Not run: Point <- ffi_struct(x = ffi_int(), y = ffi_double()) ffi_all_offsets(Point) # x y # 0 8 # Packed struct Packed <- ffi_struct(a = ffi_uint8(), b = ffi_int32(), .pack = 1) ffi_all_offsets(Packed) # a b # 0 1 ## End(Not run)## Not run: Point <- ffi_struct(x = ffi_int(), y = ffi_double()) ffi_all_offsets(Point) # x y # 0 8 # Packed struct Packed <- ffi_struct(a = ffi_uint8(), b = ffi_int32(), .pack = 1) ffi_all_offsets(Packed) # a b # 0 1 ## End(Not run)
Allocates a buffer for n elements of the given FFIType (e.g., int, double, etc). Returns an external pointer tagged with the type.
ffi_alloc(type, ...)ffi_alloc(type, ...)
type |
FFIType object |
... |
Additional arguments |
External pointer to buffer
Allocates a buffer of the given size (in bytes) and returns an external pointer. The memory is automatically freed when the pointer is garbage collected.
ffi_alloc_buffer(size)ffi_alloc_buffer(size)
size |
Number of bytes to allocate |
External pointer to buffer
Create an FFI array type
ffi_array_type(element_type, length)ffi_array_type(element_type, length)
element_type |
FFIType of array elements |
length |
Integer length of array |
An ArrayType object
Call a C function through the FFI interface.
ffi_call(cif, symbol, ...)ffi_call(cif, symbol, ...)
cif |
CIF object defining the call interface |
symbol |
NativeSymbol or character name of function |
... |
Arguments to pass to the function (including |
The method implementations accept an additional na_check argument (logical,
default TRUE). When TRUE, the function checks for NA values in arguments and
errors if found. Set to FALSE to skip NA checking for better performance
(at your own risk).
Important: libffi provides no error handling for the actual C function call. If the called C function crashes (segmentation fault, illegal instruction, abort, etc.), R itself will crash. This is a fundamental limitation of FFI
there is no portable way to catch such errors in C code.
Before making FFI calls, ensure:
The function pointer is valid (not NULL, points to executable code)
All pointer arguments are valid (use ffi_is_null to check)
Array/buffer sizes are correct - buffer overruns cause undefined behavior
The CIF signature exactly matches the C function's signature
Struct layouts match between R types and C (check alignment/padding)
For debugging crashes:
Run R under a debugger: R -d gdb
Enable core dumps: ulimit -c unlimited
Use address sanitizers when building the library being called
The return value from the C function, converted to an R type
ffi_is_null for checking pointer validity
Prepare FFI call interface Prepare FFI call interface
ffi_cif(return_type, ...)ffi_cif(return_type, ...)
return_type |
FFIType for return value |
... |
FFIType objects for arguments |
Creates a CIF for calling C functions with variable arguments (varargs). Unlike regular CIFs, variadic CIFs must specify the types of ALL arguments for each specific call, including the variadic ones.
ffi_cif_var(return_type, nfixedargs, ...)ffi_cif_var(return_type, nfixedargs, ...)
return_type |
FFIType for return value |
nfixedargs |
Number of fixed arguments (before the ...) |
... |
FFIType objects for ALL arguments (fixed + variadic) |
Due to C calling conventions, variadic arguments undergo "default argument promotions": float becomes double, and small integers (char, short) become int. You must use ffi_int() or ffi_double() for variadic arguments, not smaller types.
CIF object
## Not run: # Call a varargs function: test_varargs_sum(int nargs, ...) # First argument (nargs) is fixed, rest are variadic integers sym <- ffi_symbol("test_varargs_sum") # Call with 3 variadic int arguments cif <- ffi_cif_var(ffi_double(), nfixedargs = 1L, ffi_int(), ffi_int(), ffi_int(), ffi_int() ) result <- ffi_call(cif, sym, 3L, 10L, 20L, 30L) # returns 60 ## End(Not run)## Not run: # Call a varargs function: test_varargs_sum(int nargs, ...) # First argument (nargs) is fixed, rest are variadic integers sym <- ffi_symbol("test_varargs_sum") # Call with 3 variadic int arguments cif <- ffi_cif_var(ffi_double(), nfixedargs = 1L, ffi_int(), ffi_int(), ffi_int(), ffi_int() ) result <- ffi_call(cif, sym, 3L, 10L, 20L, 30L) # returns 60 ## End(Not run)
Wraps an R function so it can be used as a callback from C code. The closure has a CIF that describes its signature (return type and argument types). When C code calls through the closure's function pointer, the R function is invoked with converted arguments.
ffi_closure(r_function, return_type, ...)ffi_closure(r_function, return_type, ...)
r_function |
An R function to wrap as a callback |
return_type |
FFIType for return value |
... |
FFIType objects for arguments |
The R function must accept the same number of arguments as specified in the type signature. Arguments are converted from C types to R types before calling, and the return value is converted back to C.
Important: You must keep a reference to the FFIClosure object for as long as C code might call through it. If the closure is garbage collected, calling through its function pointer will crash.
An FFIClosure object
ffi_closure_pointer() to get the callable function pointer
## Not run: # Create a comparison function for qsort cmp_fn <- function(a, b) { as.integer(a - b) } # Wrap it as a C callback: int (*)(int*, int*) cmp_closure <- ffi_closure( cmp_fn, ffi_int(), # return type ffi_pointer(), ffi_pointer() # argument types (pointers to int) ) # Get the function pointer to pass to C cmp_ptr <- ffi_closure_pointer(cmp_closure) ## End(Not run)## Not run: # Create a comparison function for qsort cmp_fn <- function(a, b) { as.integer(a - b) } # Wrap it as a C callback: int (*)(int*, int*) cmp_closure <- ffi_closure( cmp_fn, ffi_int(), # return type ffi_pointer(), ffi_pointer() # argument types (pointers to int) ) # Get the function pointer to pass to C cmp_ptr <- ffi_closure_pointer(cmp_closure) ## End(Not run)
Returns the executable function pointer that can be passed to C functions expecting a callback.
ffi_closure_pointer(closure)ffi_closure_pointer(closure)
closure |
An FFIClosure object |
The returned pointer can be passed to C functions via ffi_call().
It will invoke the R function when called.
External pointer to the callable function
ffi_closure() to create closures
Not all platforms support FFI closures. Use this function to check before attempting to create closures.
ffi_closures_supported()ffi_closures_supported()
Logical; TRUE if closures are supported
Copy array from native memory
ffi_copy_array(ptr, length, element_type)ffi_copy_array(ptr, length, element_type)
ptr |
External pointer to array |
length |
Integer length of array |
element_type |
FFIType of array elements |
Copy array from native memory (ArrayType version)
ffi_copy_array_type(ptr, array_type)ffi_copy_array_type(ptr, array_type)
ptr |
External pointer to array |
array_type |
ArrayType object |
Generates getter and setter functions for a C structure with bit-fields, allowing easy manipulation of packed bit-field values.
ffi_create_bitfield_accessors(field_specs, base_type = ffi_uint32())ffi_create_bitfield_accessors(field_specs, base_type = ffi_uint32())
field_specs |
Named list where names are field names and values are bit widths (integers) |
base_type |
FFI type for the packed representation (default: ffi_uint32()) |
List with pack, unpack, get, and set functions
# Define a bit-field structure # C equivalent: # struct Flags { # unsigned int enabled : 1; # unsigned int mode : 3; # unsigned int priority : 4; # }; flags_accessors <- ffi_create_bitfield_accessors( list(enabled = 1L, mode = 3L, priority = 4L) ) # Pack values packed <- flags_accessors$pack(list(enabled = 1L, mode = 5L, priority = 12L)) # Unpack to list flags_accessors$unpack(packed) # $enabled: 1 # $mode: 5 # $priority: 12 # Get a single field flags_accessors$get(packed, "mode") # 5 # Set a single field new_packed <- flags_accessors$set(packed, "mode", 7L) flags_accessors$get(new_packed, "mode") # 7# Define a bit-field structure # C equivalent: # struct Flags { # unsigned int enabled : 1; # unsigned int mode : 3; # unsigned int priority : 4; # }; flags_accessors <- ffi_create_bitfield_accessors( list(enabled = 1L, mode = 3L, priority = 4L) ) # Pack values packed <- flags_accessors$pack(list(enabled = 1L, mode = 5L, priority = 12L)) # Unpack to list flags_accessors$unpack(packed) # $enabled: 1 # $mode: 5 # $priority: 12 # Get a single field flags_accessors$get(packed, "mode") # 5 # Set a single field new_packed <- flags_accessors$set(packed, "mode", 7L) flags_accessors$get(new_packed, "mode") # 7
This is the main user-facing function for API mode. It generates C code to compute field offsets using the compiler, compiles it, and returns a helper object with constructor and field metadata.
ffi_create_helpers(struct_name, field_types, include_dirs = NULL)ffi_create_helpers(struct_name, field_types, include_dirs = NULL)
struct_name |
Character string naming the struct (e.g., "Point2D") |
field_types |
Named list of FFIType objects for each field (e.g., list(x = ffi_int(), y = ffi_int())) |
include_dirs |
Optional character vector of include directories for compilation |
An rffi_struct_helpers S3 object with:
Constructor function that allocates a new struct
Named list of field metadata (offset + FFIType)
Get field value from struct pointer
Set field value in struct pointer
The compiled library object (for cleanup)
## Not run: # Define struct with field types helpers <- ffi_create_helpers( "Point2D", list(x = ffi_int(), y = ffi_int()) ) # Create instance pt <- helpers$new() # Set/get fields helpers$set(pt, "x", 42L) helpers$set(pt, "y", 100L) helpers$get(pt, "x") # 42L helpers$get(pt, "y") # 100L # Access field metadata helpers$fields$x$offset # 0 helpers$fields$y$offset # 4 ## End(Not run)## Not run: # Define struct with field types helpers <- ffi_create_helpers( "Point2D", list(x = ffi_int(), y = ffi_int()) ) # Create instance pt <- helpers$new() # Set/get fields helpers$set(pt, "x", 42L) helpers$set(pt, "y", 100L) helpers$get(pt, "x") # 42L helpers$get(pt, "y") # 100L # Access field metadata helpers$fields$x$offset # 0 helpers$fields$y$offset # 4 ## End(Not run)
Reads the pointer value stored at an address. This is useful for accessing global variables in shared libraries that are pointers (like R_GlobalEnv, R_NilValue, etc.).
ffi_deref_pointer(ptr)ffi_deref_pointer(ptr)
ptr |
External pointer to the address to dereference |
External pointer containing the value at the address
## Not run: # Get R_GlobalEnv from libR.so addr <- getNativeSymbolInfo("R_GlobalEnv")$address globalenv_sexp <- ffi_deref_pointer(addr) ## End(Not run)## Not run: # Get R_GlobalEnv from libR.so addr <- getNativeSymbolInfo("R_GlobalEnv")$address globalenv_sexp <- ffi_deref_pointer(addr) ## End(Not run)
double FFI type
ffi_double()ffi_double()
FFIType object for double
Create FFI enumeration type
ffi_enum(..., underlying_type = ffi_int())ffi_enum(..., underlying_type = ffi_int())
... |
Named integer values representing enum constants |
underlying_type |
FFIType for underlying integer type (default: ffi_int()) |
EnumType object
Look up the integer value for a named enum constant.
ffi_enum_to_int(enum_type, name)ffi_enum_to_int(enum_type, name)
enum_type |
EnumType object |
name |
Character name of enum constant |
Integer value
## Not run: Color <- ffi_enum(RED = 0L, GREEN = 1L, BLUE = 2L) ffi_enum_to_int(Color, "GREEN") # 1L ## End(Not run)## Not run: Color <- ffi_enum(RED = 0L, GREEN = 1L, BLUE = 2L) ffi_enum_to_int(Color, "GREEN") # 1L ## End(Not run)
Extracts a single bit-field value from a packed integer at a specified bit offset and width.
ffi_extract_bit_field(packed_value, bit_offset, bit_width)ffi_extract_bit_field(packed_value, bit_offset, bit_width)
packed_value |
Integer value containing packed bit-fields |
bit_offset |
Bit offset from LSB (0-based) |
bit_width |
Number of bits in the field |
Extracted integer value
# Extract 3-bit mode field at bit offset 1 from value 0x65 ffi_extract_bit_field(0x65L, 1L, 3L) # 5 # Extract 4-bit priority field at bit offset 4 ffi_extract_bit_field(0x65L, 4L, 4L) # 6# Extract 3-bit mode field at bit offset 1 from value 0x65 ffi_extract_bit_field(0x65L, 1L, 3L) # 5 # Extract 4-bit priority field at bit offset 4 ffi_extract_bit_field(0x65L, 4L, 4L) # 6
Extract a single bit-field from a 64-bit packed value
ffi_extract_bits64(packed_value, bit_offset, bit_width)ffi_extract_bits64(packed_value, bit_offset, bit_width)
packed_value |
Packed value (as double for 64-bit range) |
bit_offset |
Bit offset from LSB (0-based) |
bit_width |
Number of bits in the field |
Extracted value as double (for 64-bit range)
packed <- ffi_pack_bits64(c(1L, 5L, 12L), c(1L, 3L, 4L)) ffi_extract_bits64(packed, 1L, 3L) # 5 (mode field)packed <- ffi_pack_bits64(c(1L, 5L, 12L), c(1L, 3L, 4L)) ffi_extract_bits64(packed, 1L, 3L) # 5 (mode field)
Extracts a single bit-field and sign-extends it based on the high bit. This is the 32-bit version using pure R code.
ffi_extract_signed_bit_field(packed_value, bit_offset, bit_width)ffi_extract_signed_bit_field(packed_value, bit_offset, bit_width)
packed_value |
Integer value containing packed bit-fields |
bit_offset |
Bit offset from LSB (0-based) |
bit_width |
Number of bits in the field |
Extracted signed integer value
# A 4-bit value of 13 (0xD) represents -3 in signed 4-bit packed <- ffi_pack_bits(c(13L), c(4L)) ffi_extract_signed_bit_field(packed, 0L, 4L) # -3 # 3-bit value of 7 represents -1 in signed 3-bit packed2 <- ffi_pack_bits(c(7L), c(3L)) ffi_extract_signed_bit_field(packed2, 0L, 3L) # -1# A 4-bit value of 13 (0xD) represents -3 in signed 4-bit packed <- ffi_pack_bits(c(13L), c(4L)) ffi_extract_signed_bit_field(packed, 0L, 4L) # -3 # 3-bit value of 7 represents -1 in signed 3-bit packed2 <- ffi_pack_bits(c(7L), c(3L)) ffi_extract_signed_bit_field(packed2, 0L, 3L) # -1
Extracts a bit-field and sign-extends it based on the high bit. Useful for signed integer fields in C structures.
ffi_extract_signed_bits64(packed_value, bit_offset, bit_width)ffi_extract_signed_bits64(packed_value, bit_offset, bit_width)
packed_value |
Packed value (as double for 64-bit range) |
bit_offset |
Bit offset from LSB (0-based) |
bit_width |
Number of bits in the field |
Extracted signed value as double
# Pack a negative value in 4-bit signed field (-3 = 0xD in 4 bits) packed <- ffi_pack_bits64(c(13L), c(4L)) # 0xD = -3 as signed 4-bit ffi_extract_signed_bits64(packed, 0L, 4L) # -3# Pack a negative value in 4-bit signed field (-3 = 0xD in 4 bits) packed <- ffi_pack_bits64(c(13L), c(4L)) # 0xD = -3 as signed 4-bit ffi_extract_signed_bits64(packed, 0L, 4L) # -3
Returns metadata about a specific field, including its byte offset, size, and type information.
ffi_field_info(struct_type, field)ffi_field_info(struct_type, field)
struct_type |
StructType object |
field |
Character field name or integer field index (1-based) |
FieldInfo object with offset, size, alignment info
## Not run: Point <- ffi_struct(x = ffi_int(), y = ffi_double()) ffi_field_info(Point, "x") # <FieldInfo 'x' type=int, offset=0, size=4> ffi_field_info(Point, "y") # <FieldInfo 'y' type=double, offset=8, size=8> (offset 8 due to alignment) ## End(Not run)## Not run: Point <- ffi_struct(x = ffi_int(), y = ffi_double()) ffi_field_info(Point, "x") # <FieldInfo 'x' type=int, offset=0, size=4> ffi_field_info(Point, "y") # <FieldInfo 'y' type=double, offset=8, size=8> (offset 8 due to alignment) ## End(Not run)
Fill a typed buffer from an R vector (int or double)
ffi_fill_typed_buffer(ptr, values, type)ffi_fill_typed_buffer(ptr, values, type)
ptr |
External pointer to buffer |
values |
Integer or double vector |
type |
FFIType object |
float FFI type
ffi_float()ffi_float()
FFIType object for float
Explicitly frees memory that was allocated by C code and returned as a pointer. Use this when you know the pointer was allocated with malloc/calloc and it's your responsibility to free it.
ffi_free(ptr)ffi_free(ptr)
ptr |
External pointer to free |
When to use this:
Pointers returned from C functions that allocate memory (e.g., strdup, malloc)
When C documentation says "caller must free"
When NOT to use this:
Pointers allocated via ffi_alloc() (auto-freed by R's GC)
Static or global pointers from C
Pointers into existing structures
Pointers that C will free itself
Calling ffi_free() on an already-freed pointer or invalid pointer
will cause undefined behavior (likely crash).
NULL invisibly
## Not run: # C function that allocates and returns a string strdup_fn <- ffi_function("strdup", ffi_pointer(), ffi_string()) ptr <- strdup_fn("hello") # ... use ptr ... ffi_free(ptr) # We must free because strdup allocates ## End(Not run)## Not run: # C function that allocates and returns a string strdup_fn <- ffi_function("strdup", ffi_pointer(), ffi_string()) ptr <- strdup_fn("hello") # ... use ptr ... ffi_free(ptr) # We must free because strdup allocates ## End(Not run)
Create a reusable FFI function wrapper
ffi_function(name, return_type, ..., library = NULL, na_check = TRUE)ffi_function(name, return_type, ..., library = NULL, na_check = TRUE)
name |
Character name of the function |
return_type |
FFIType for return value |
... |
FFIType objects for arguments |
library |
Character name of library (optional) |
na_check |
Logical; if TRUE (default), check for NA values and error if found. Set to FALSE to skip NA checking for better performance (at your own risk). |
Returns a pointer to the i-th struct in a contiguous array of structs. The returned pointer shares memory with the original array.
ffi_get_element(ptr, index, struct_type)ffi_get_element(ptr, index, struct_type)
ptr |
External pointer to struct array (from ffi_alloc with n > 1) |
index |
1-based index of element to get |
struct_type |
StructType describing the element type |
External pointer to the element (no finalizer - parent owns memory)
## Not run: Point <- ffi_struct(x = ffi_int(), y = ffi_int()) points <- ffi_alloc(Point, 10L) # array of 10 Points p3 <- ffi_get_element(points, 3L, Point) ffi_set_field(p3, "x", 100L, Point) ## End(Not run)## Not run: Point <- ffi_struct(x = ffi_int(), y = ffi_int()) points <- ffi_alloc(Point, 10L) # array of 10 Points p3 <- ffi_get_element(points, 3L, Point) ffi_set_field(p3, "x", 100L, Point) ## End(Not run)
Get field value from FFI structure
ffi_get_field(ptr, field, struct_type, ...)ffi_get_field(ptr, field, struct_type, ...)
ptr |
External pointer to structure |
field |
Character field name or integer field index |
struct_type |
StructType object |
... |
Not used; required for S7 generic dispatch. |
Field value
Look up the enum constant name for an integer value.
ffi_int_to_enum(enum_type, value)ffi_int_to_enum(enum_type, value)
enum_type |
EnumType object |
value |
Integer value |
Character name of enum constant (or NA if not found)
## Not run: Color <- ffi_enum(RED = 0L, GREEN = 1L, BLUE = 2L) ffi_int_to_enum(Color, 1L) # "GREEN" ## End(Not run)## Not run: Color <- ffi_enum(RED = 0L, GREEN = 1L, BLUE = 2L) ffi_int_to_enum(Color, 1L) # "GREEN" ## End(Not run)
Int16 FFI type
ffi_int16()ffi_int16()
FFIType object for int16
Int32 FFI type
ffi_int32()ffi_int32()
FFIType object for int32
Int64 FFI type
ffi_int64()ffi_int64()
FFIType object for int64
Check if external pointer is NULL
ffi_is_null(ptr)ffi_is_null(ptr)
ptr |
External pointer |
Logical
Get information about loaded native libraries
ffi_loaded_libs()ffi_loaded_libs()
longdouble FFI type
ffi_longdouble()ffi_longdouble()
FFIType object for longdouble
long long FFI type
ffi_longlong()ffi_longlong()
FFIType object for longlong
Create a NULL pointer
ffi_null_pointer()ffi_null_pointer()
External pointer to NULL
Returns the byte offset of a field within a structure, accounting for alignment requirements. Similar to C's offsetof() macro.
ffi_offsetof(struct_type, field, use_pack = TRUE)ffi_offsetof(struct_type, field, use_pack = TRUE)
struct_type |
StructType object |
field |
Character field name or integer field index (1-based) |
use_pack |
Logical, whether to use the struct's pack setting. Default TRUE. Set to FALSE to get libffi's natural alignment offset even for packed structs. |
For packed structures (created with pack parameter), this function computes
the offset using the specified packing alignment rather than natural alignment.
Integer byte offset
## Not run: Point <- ffi_struct(x = ffi_int(), y = ffi_double()) ffi_offsetof(Point, "x") # 0 ffi_offsetof(Point, "y") # 8 (aligned to 8-byte boundary) # Packed struct example Packed <- ffi_struct(a = ffi_uint8(), b = ffi_int32(), .pack = 1) ffi_offsetof(Packed, "b") # 1 (no padding with .pack=1) ## End(Not run)## Not run: Point <- ffi_struct(x = ffi_int(), y = ffi_double()) ffi_offsetof(Point, "x") # 0 ffi_offsetof(Point, "y") # 8 (aligned to 8-byte boundary) # Packed struct example Packed <- ffi_struct(a = ffi_uint8(), b = ffi_int32(), .pack = 1) ffi_offsetof(Packed, "b") # 1 (no padding with .pack=1) ## End(Not run)
Packs multiple values into a single integer according to specified bit widths. This is useful when working with C structures that use bit-fields, which are not directly supported by libffi.
ffi_pack_bits(values, widths, base_type = ffi_uint32())ffi_pack_bits(values, widths, base_type = ffi_uint32())
values |
Integer vector of values to pack |
widths |
Integer vector of bit widths for each value |
base_type |
FFI type for the result (default: ffi_uint32()) |
Values are packed from LSB to MSB (least significant bit to most significant bit). Each value is masked to its specified width and shifted into position.
Packed integer value
# Pack three bit-fields: enabled (1 bit), mode (3 bits), priority (4 bits) packed <- ffi_pack_bits(c(1L, 5L, 12L), c(1L, 3L, 4L)) # Result: 0b1100101 = 0x65 = 101 # Verify by unpacking ffi_unpack_bits(packed, c(1L, 3L, 4L)) # [1] 1 5 12# Pack three bit-fields: enabled (1 bit), mode (3 bits), priority (4 bits) packed <- ffi_pack_bits(c(1L, 5L, 12L), c(1L, 3L, 4L)) # Result: 0b1100101 = 0x65 = 101 # Verify by unpacking ffi_unpack_bits(packed, c(1L, 3L, 4L)) # [1] 1 5 12
Packs multiple values into a single 64-bit integer (returned as double) according to specified bit widths. This version uses C code for full 64-bit support.
ffi_pack_bits64(values, widths)ffi_pack_bits64(values, widths)
values |
Integer vector of values to pack |
widths |
Integer vector of bit widths for each value |
Values are packed from LSB to MSB (least significant bit to most significant bit). Each value is masked to its specified width and shifted into position. Uses C implementation for full 64-bit support (R integers are only 32-bit).
Packed value as double (for 64-bit range)
# Pack three bit-fields: enabled (1 bit), mode (3 bits), priority (4 bits) packed <- ffi_pack_bits64(c(1L, 5L, 12L), c(1L, 3L, 4L)) # Works with values > 32 bits total large_packed <- ffi_pack_bits64(c(1, 0x7FFFFFFF), c(1L, 31L))# Pack three bit-fields: enabled (1 bit), mode (3 bits), priority (4 bits) packed <- ffi_pack_bits64(c(1L, 5L, 12L), c(1L, 3L, 4L)) # Works with values > 32 bits total large_packed <- ffi_pack_bits64(c(1, 0x7FFFFFFF), c(1L, 31L))
Uses tree-sitter for robust AST-based parsing of C headers.
ffi_parse_header(header_file, includes = NULL)ffi_parse_header(header_file, includes = NULL)
header_file |
Path to C header file |
includes |
Additional include directories |
List with parsed components (file, defines, structs, unions, enums, functions, typedefs)
pointer FFI type
ffi_pointer()ffi_pointer()
FFIType object for pointer
Pretty print struct contents
ffi_print_struct(ptr, struct_type)ffi_print_struct(ptr, struct_type)
ptr |
External pointer to struct |
struct_type |
StructType object |
Reads a typed value from a global symbol address. This is a typed version of ffi_deref_pointer that handles type conversion.
ffi_read_global(ptr, type)ffi_read_global(ptr, type)
ptr |
External pointer to the global variable address |
type |
FFIType describing the type of the global variable |
The value at the address, converted to an appropriate R type
Updates a single bit-field in a packed integer at a specified bit offset and width, returning the modified packed value.
ffi_set_bit_field(packed_value, new_value, bit_offset, bit_width)ffi_set_bit_field(packed_value, new_value, bit_offset, bit_width)
packed_value |
Integer value containing packed bit-fields |
new_value |
New value for the bit-field |
bit_offset |
Bit offset from LSB (0-based) |
bit_width |
Number of bits in the field |
Modified packed integer value
# Set 3-bit mode field at bit offset 1 to value 7 ffi_set_bit_field(0x65L, 7L, 1L, 3L) # 0x6F # Set 1-bit enabled field at bit offset 0 to 0 ffi_set_bit_field(0x65L, 0L, 0L, 1L) # 0x64# Set 3-bit mode field at bit offset 1 to value 7 ffi_set_bit_field(0x65L, 7L, 1L, 3L) # 0x6F # Set 1-bit enabled field at bit offset 0 to 0 ffi_set_bit_field(0x65L, 0L, 0L, 1L) # 0x64
Set a single bit-field in a 64-bit packed value
ffi_set_bits64(packed_value, new_value, bit_offset, bit_width)ffi_set_bits64(packed_value, new_value, bit_offset, bit_width)
packed_value |
Packed value (as double for 64-bit range) |
new_value |
New value for the bit-field |
bit_offset |
Bit offset from LSB (0-based) |
bit_width |
Number of bits in the field |
Modified packed value as double
packed <- ffi_pack_bits64(c(1L, 5L, 12L), c(1L, 3L, 4L)) new_packed <- ffi_set_bits64(packed, 7L, 1L, 3L) # Set mode to 7 ffi_extract_bits64(new_packed, 1L, 3L) # 7packed <- ffi_pack_bits64(c(1L, 5L, 12L), c(1L, 3L, 4L)) new_packed <- ffi_set_bits64(packed, 7L, 1L, 3L) # Set mode to 7 ffi_extract_bits64(new_packed, 1L, 3L) # 7
Set field value in FFI structure
ffi_set_field(ptr, field, value, struct_type, ...)ffi_set_field(ptr, field, value, struct_type, ...)
ptr |
External pointer to structure |
field |
Character field name or integer field index |
value |
Value to set |
struct_type |
StructType object |
... |
Not used; required for S7 generic dispatch. |
Updated pointer
short FFI type
ffi_short()ffi_short()
FFIType object for short
Size_t FFI type
ffi_size_t()ffi_size_t()
FFIType object for size_t
Get size of FFI type in bytes
ffi_sizeof(type, ...)ffi_sizeof(type, ...)
type |
FFIType object |
... |
Additional arguments (not used) |
Size in bytes
ssize_t FFI type
ffi_ssize_t()ffi_ssize_t()
FFIType object for ssize_t
String FFI type
ffi_string()ffi_string()
FFIType object for string
Creates an FFI structure type from named field types. By default, libffi
uses natural alignment (each field aligned to its size). Use the .pack
parameter to specify tighter packing similar to #pragma pack(n) in C.
ffi_struct(..., .pack = NULL)ffi_struct(..., .pack = NULL)
... |
Named FFIType objects representing struct fields |
.pack |
Integer specifying packing alignment (1, 2, 4, 8, or 16), or NULL for default/natural alignment. When .pack=1, fields are byte-aligned (no padding). The dot prefix prevents collision with C struct field names. |
StructType object
The .pack parameter affects ffi_offsetof(), ffi_sizeof(),
ffi_get_field(), and ffi_set_field().
Packed structs cannot be passed by value - GCC compiles functions taking
__attribute__((packed)) structs to expect arguments on the stack, but
libffi passes via registers. Use pointers instead.
# Natural alignment (default) Point <- ffi_struct(x = ffi_int(), y = ffi_int()) # Packed struct (1-byte alignment) PackedData <- ffi_struct( flag = ffi_uint8(), value = ffi_int32(), .pack = 1 ) # Check sizes ffi_sizeof(Point) # Natural size ffi_sizeof(PackedData) # Packed size (smaller) # Note: Packed structs cannot be passed by value to C functions. # Use pointers instead: # ffi_function("some_func", ffi_void(), ffi_pointer())# Natural alignment (default) Point <- ffi_struct(x = ffi_int(), y = ffi_int()) # Packed struct (1-byte alignment) PackedData <- ffi_struct( flag = ffi_uint8(), value = ffi_int32(), .pack = 1 ) # Check sizes ffi_sizeof(Point) # Natural size ffi_sizeof(PackedData) # Packed size (smaller) # Note: Packed structs cannot be passed by value to C functions. # Use pointers instead: # ffi_function("some_func", ffi_void(), ffi_pointer())
Allocate array of structs from R list
ffi_struct_array_from_list(struct_type, values)ffi_struct_array_from_list(struct_type, values)
struct_type |
StructType object |
values |
List of named lists, one per struct |
External pointer to allocated struct array
## Not run: Point <- ffi_struct(x = ffi_int(), y = ffi_int()) points <- ffi_struct_array_from_list(Point, list( list(x = 0L, y = 0L), list(x = 10L, y = 20L), list(x = 30L, y = 40L) )) ## End(Not run)## Not run: Point <- ffi_struct(x = ffi_int(), y = ffi_int()) points <- ffi_struct_array_from_list(Point, list( list(x = 0L, y = 0L), list(x = 10L, y = 20L), list(x = 30L, y = 40L) )) ## End(Not run)
Create and initialize a struct from R list
ffi_struct_from_list(struct_type, values)ffi_struct_from_list(struct_type, values)
struct_type |
StructType object |
values |
Named list of field values |
External pointer to allocated and initialized struct
## Not run: Point <- ffi_struct(x = ffi_int(), y = ffi_int()) pt <- ffi_struct_from_list(Point, list(x = 10L, y = 20L)) ## End(Not run)## Not run: Point <- ffi_struct(x = ffi_int(), y = ffi_int()) pt <- ffi_struct_from_list(Point, list(x = 10L, y = 20L)) ## End(Not run)
Convert struct to R list
ffi_struct_to_list(ptr, struct_type)ffi_struct_to_list(ptr, struct_type)
ptr |
External pointer to struct |
struct_type |
StructType object |
Named list of field values
## Not run: Point <- ffi_struct(x = ffi_int(), y = ffi_int()) pt <- ffi_alloc(Point) ffi_set_field(pt, "x", 42L, Point) ffi_set_field(pt, "y", 100L, Point) as.list(pt, Point) # list(x = 42L, y = 100L) ## End(Not run)## Not run: Point <- ffi_struct(x = ffi_int(), y = ffi_int()) pt <- ffi_alloc(Point) ffi_set_field(pt, "x", 42L, Point) ffi_set_field(pt, "y", 100L, Point) as.list(pt, Point) # list(x = 42L, y = 100L) ## End(Not run)
Get native symbol reference
ffi_symbol(name, library = NULL)ffi_symbol(name, library = NULL)
name |
Character name of the symbol |
library |
Character name of library (optional) |
Create native symbol from direct address
ffi_symbol_from_address(address, name = "anonymous")ffi_symbol_from_address(address, name = "anonymous")
address |
External pointer to the symbol address |
name |
Character name of the symbol (for reference) |
Uchar FFI type
ffi_uchar()ffi_uchar()
FFIType object for uchar
Uint16 FFI type
ffi_uint16()ffi_uint16()
FFIType object for uint16
Uint32 FFI type
ffi_uint32()ffi_uint32()
FFIType object for uint32
Uint64 FFI type
ffi_uint64()ffi_uint64()
FFIType object for uint64
Uint8 FFI type
ffi_uint8()ffi_uint8()
FFIType object for uint8
ulong FFI type
ffi_ulong()ffi_ulong()
FFIType object for ulong
ulonglong FFI type
ffi_ulonglong()ffi_ulonglong()
FFIType object for ulonglong
Creates a union type where all fields share the same memory location. The union's size is the size of its largest member.
ffi_union(..., .pack = NULL)ffi_union(..., .pack = NULL)
... |
Named FFIType objects representing union fields |
.pack |
Integer packing alignment (1, 2, 4, 8, or 16). When specified, the union's alignment is reduced to min(natural_alignment, .pack). This affects placement when the union is used as a struct member. Default NULL uses natural alignment. |
UnionType object
# Normal union U <- ffi_union(c = ffi_char(), i = ffi_int()) # Packed union (alignment = 1) PackedU <- ffi_union(c = ffi_char(), i = ffi_int(), .pack = 1) # Packed union in a struct - offset of next field is affected S <- ffi_struct(u = PackedU, after = ffi_char()) # Note: Packed unions cannot be passed by value to C functions. # Use pointers instead.# Normal union U <- ffi_union(c = ffi_char(), i = ffi_int()) # Packed union (alignment = 1) PackedU <- ffi_union(c = ffi_char(), i = ffi_int(), .pack = 1) # Packed union in a struct - offset of next field is affected S <- ffi_struct(u = PackedU, after = ffi_char()) # Note: Packed unions cannot be passed by value to C functions. # Use pointers instead.
Extracts multiple values from a packed integer according to specified bit widths.
This is the inverse operation of ffi_pack_bits.
ffi_unpack_bits(packed_value, widths)ffi_unpack_bits(packed_value, widths)
packed_value |
Integer value containing packed bit-fields |
widths |
Integer vector of bit widths for each field |
Values are unpacked from LSB to MSB (least significant bit to most significant bit). Each value is extracted by shifting and masking according to its width.
Integer vector of unpacked values
# Unpack a value with three bit-fields values <- ffi_unpack_bits(0x65L, c(1L, 3L, 4L)) values # [1] 1 5 12 # Round-trip test packed <- ffi_pack_bits(c(1L, 5L, 12L), c(1L, 3L, 4L)) identical(ffi_unpack_bits(packed, c(1L, 3L, 4L)), c(1L, 5L, 12L))# Unpack a value with three bit-fields values <- ffi_unpack_bits(0x65L, c(1L, 3L, 4L)) values # [1] 1 5 12 # Round-trip test packed <- ffi_pack_bits(c(1L, 5L, 12L), c(1L, 3L, 4L)) identical(ffi_unpack_bits(packed, c(1L, 3L, 4L)), c(1L, 5L, 12L))
Extracts multiple values from a packed 64-bit value according to specified bit widths.
ffi_unpack_bits64(packed_value, widths)ffi_unpack_bits64(packed_value, widths)
packed_value |
Packed value (as double for 64-bit range) |
widths |
Integer vector of bit widths for each field |
Integer vector of unpacked values
packed <- ffi_pack_bits64(c(1L, 5L, 12L), c(1L, 3L, 4L)) ffi_unpack_bits64(packed, c(1L, 3L, 4L)) # c(1, 5, 12)packed <- ffi_pack_bits64(c(1L, 5L, 12L), c(1L, 3L, 4L)) ffi_unpack_bits64(packed, c(1L, 3L, 4L)) # c(1, 5, 12)
Ushort FFI type
ffi_ushort()ffi_ushort()
FFIType object for ushort
Performs comprehensive validation of FFI call inputs before making the call. This helps diagnose issues that would otherwise cause crashes.
ffi_validate_call(cif, symbol, args = list(), verbose = FALSE)ffi_validate_call(cif, symbol, args = list(), verbose = FALSE)
cif |
CIF object defining the call interface |
symbol |
NativeSymbol object for the function |
args |
List of arguments to pass |
verbose |
Logical; if TRUE, print diagnostic information |
This function checks:
CIF and symbol pointers are not NULL
Argument count matches CIF specification
No NA values in arguments (unless explicitly allowed)
Pointer arguments are not NULL (when applicable)
Note that even with all checks passing, crashes can still occur if:
The C function signature doesn't match the CIF
Pointer arguments point to invalid memory
Buffer sizes are incorrect
The C function itself has bugs
A list with validation results:
Logical; TRUE if all checks pass
Character vector of error messages (empty if valid)
Character vector of warning messages
## Not run: cif <- ffi_cif(ffi_int(), ffi_int(), ffi_int()) sym <- ffi_symbol("add_ints") result <- ffi_validate_call(cif, sym, list(1L, 2L)) if (result$valid) { ffi_call(cif, sym, 1L, 2L) } ## End(Not run)## Not run: cif <- ffi_cif(ffi_int(), ffi_int(), ffi_int()) sym <- ffi_symbol("add_ints") result <- ffi_validate_call(cif, sym, list(1L, 2L)) if (result$valid) { ffi_call(cif, sym, 1L, 2L) } ## End(Not run)
Wide char FFI type
ffi_wchar_t()ffi_wchar_t()
FFIType object for wchar_t
A closure wraps an R function so it can be used as a callback from C code. The closure has an associated CIF that describes the function signature.
FFIClosure( r_function = function() NULL, cif = CIF(), ref = NULL, func_ptr = NULL )FFIClosure( r_function = function() NULL, cif = CIF(), ref = NULL, func_ptr = NULL )
r_function |
The R function to wrap |
cif |
CIF object describing the callback signature |
ref |
External pointer to the closure |
func_ptr |
External pointer to the executable function |
An FFIClosure object
FFI Type representation
FFIType(name = character(0), size = integer(0), ref = NULL)FFIType(name = character(0), size = integer(0), ref = NULL)
name |
Character name of the type |
size |
Integer size in bytes |
ref |
External pointer to ffi_type |
An FFIType object
Represents metadata about a single field in a structure. Field Information Class
FieldInfo( name = character(0), type = FFIType(), offset = integer(0), size = integer(0), index = integer(0) )FieldInfo( name = character(0), type = FFIType(), offset = integer(0), size = integer(0), index = integer(0) )
name |
Character name of the field |
type |
FFIType of the field |
offset |
Integer byte offset within structure |
size |
Integer size of field in bytes |
index |
Integer 1-based field index |
Contains metadata about a struct field including its name, type, byte offset within the structure, size, and index.
A FieldInfo object
Generate R enum definition from parsed enum
generate_enum_definition(enum_name, enum_values)generate_enum_definition(enum_name, enum_values)
enum_name |
Name of the enum |
enum_values |
Named integer vector of enum values |
Character vector with R code
Generate R function wrapper from parsed function
generate_function_wrapper(func_def, typedefs = NULL)generate_function_wrapper(func_def, typedefs = NULL)
func_def |
Function definition (row from functions data.frame) |
typedefs |
Named character vector of typedefs (optional). Used to resolve typedef'd types like SEXPTYPE to their underlying FFI types. |
Character vector with R code
Creates all necessary R files for a package wrapping a C library. Generates a proper R package structure with DESCRIPTION, NAMESPACE, and R code in the R/ subfolder. Uses templates from inst/templates/.
generate_package_from_headers( header_files, package_name, library_name, output_dir = package_name, library_path = NULL, use_system_lib = TRUE, include_helpers = TRUE, use_api_mode = FALSE, authors_r = NULL, title = NULL, description = NULL )generate_package_from_headers( header_files, package_name, library_name, output_dir = package_name, library_path = NULL, use_system_lib = TRUE, include_helpers = TRUE, use_api_mode = FALSE, authors_r = NULL, title = NULL, description = NULL )
header_files |
Character vector of header file paths |
package_name |
Name of the R package |
library_name |
Name of the shared library |
output_dir |
Directory to create the package (package root) |
library_path |
Optional: full path to shared library (for custom installs) |
use_system_lib |
Logical: search system library paths |
include_helpers |
Logical: include allocation helper functions |
use_api_mode |
Logical: use API mode (compile struct helpers into package). When TRUE, generates src/ directory with init.c and struct_helpers.c for compiled struct accessors. When FALSE (default), uses ABI mode with runtime offset calculation. API mode is required for structs with bitfields. |
authors_r |
Authors@R field for DESCRIPTION (R code string). Default creates a placeholder person(). |
title |
Package title (default: auto-generated) |
description |
Package description (default: auto-generated) |
Invisibly returns list of generated files
## Not run: # ABI mode (default) - runtime offset calculation generate_package_from_headers( header_files = "mylib.h", package_name = "MyRPackage", library_name = "mylib", use_api_mode = FALSE ) # API mode - compiled struct helpers (better for bitfields) generate_package_from_headers( header_files = c("mylib.h", "mylib_utils.h"), package_name = "MyRPackage", library_name = "mylib", output_dir = "MyRPackage", use_api_mode = TRUE, use_system_lib = TRUE, include_helpers = TRUE, authors_r = 'person("John", "Doe", email = "[email protected]", role = c("aut", "cre"))', title = "FFI Bindings to mylib", description = "Auto-generated FFI bindings for mylib with compiled struct helpers." ) ## End(Not run)## Not run: # ABI mode (default) - runtime offset calculation generate_package_from_headers( header_files = "mylib.h", package_name = "MyRPackage", library_name = "mylib", use_api_mode = FALSE ) # API mode - compiled struct helpers (better for bitfields) generate_package_from_headers( header_files = c("mylib.h", "mylib_utils.h"), package_name = "MyRPackage", library_name = "mylib", output_dir = "MyRPackage", use_api_mode = TRUE, use_system_lib = TRUE, include_helpers = TRUE, authors_r = 'person("John", "Doe", email = "[email protected]", role = c("aut", "cre"))', title = "FFI Bindings to mylib", description = "Auto-generated FFI bindings for mylib with compiled struct helpers." ) ## End(Not run)
Creates the zzz.R file content for loading external libraries
generate_package_init( library_name, package_name, library_path = NULL, use_system_lib = TRUE )generate_package_init( library_name, package_name, library_path = NULL, use_system_lib = TRUE )
library_name |
Name of the shared library (e.g., "mylib") |
package_name |
Name of the R package |
library_path |
Optional: specific path to library, or NULL for system search |
use_system_lib |
Logical: search system library paths |
Character string with zzz.R content
## Not run: # Generate for system library code <- generate_package_init("mylib", "MyRPackage", use_system_lib = TRUE) writeLines(code, "R/zzz.R") # Generate for bundled library code <- generate_package_init("mylib", "MyRPackage", use_system_lib = FALSE) ## End(Not run)## Not run: # Generate for system library code <- generate_package_init("mylib", "MyRPackage", use_system_lib = TRUE) writeLines(code, "R/zzz.R") # Generate for bundled library code <- generate_package_init("mylib", "MyRPackage", use_system_lib = FALSE) ## End(Not run)
Generate R bindings from parsed header
generate_r_bindings(parsed_header, output_file = NULL, verbose = FALSE)generate_r_bindings(parsed_header, output_file = NULL, verbose = FALSE)
parsed_header |
Parsed header object from ffi_parse_header() |
output_file |
Optional file to write code to |
verbose |
If TRUE, print progress messages |
Character vector with all generated R code
Generate R struct definition from parsed struct
generate_struct_definition(struct_name, struct_def, typedefs = NULL)generate_struct_definition(struct_name, struct_def, typedefs = NULL)
struct_name |
Name of the struct |
struct_def |
Struct definition from parsed header |
typedefs |
Optional data frame of typedefs to resolve type aliases |
Character vector with R code
Creates convenience functions for working with a struct type:
new_<struct>(): Allocate a new struct, optionally initialize from values
<struct>_to_list(): Convert struct pointer to R list
generate_struct_helpers(struct_name, field_names)generate_struct_helpers(struct_name, field_names)
struct_name |
Name of the struct (should match the ffi_struct variable name) |
field_names |
Character vector of field names |
Character string with R code for helper functions
Creates R code that maps a typedef alias to its underlying FFI type. Handles simple type aliases (e.g., typedef int my_int) and struct typedefs.
generate_typedef_definition( alias_name, base_type, known_structs = character(), known_typedefs = character() )generate_typedef_definition( alias_name, base_type, known_structs = character(), known_typedefs = character() )
alias_name |
Name of the typedef alias |
base_type |
The underlying C type string |
known_structs |
Character vector of known struct names (for struct type resolution) |
known_typedefs |
Named character vector of already-processed typedefs (for chained resolution) |
Character string with R code, or NULL if type cannot be mapped
Generate R union definition from parsed union
generate_union_definition(union_name, union_def)generate_union_definition(union_name, union_def)
union_name |
Name of the union |
union_def |
Union definition (list of fields) |
Character vector with R code
Retrieves the type tag from an external pointer.
get_pointer_type(ptr)get_pointer_type(ptr)
ptr |
External pointer |
Character string with the type name
Check if pointer is NULL
is_null_pointer(ptr)is_null_pointer(ptr)
ptr |
External pointer to check |
Check if an object is a protected SEXP pointer
is_protected_ptr(ptr)is_protected_ptr(ptr)
ptr |
An external pointer |
TRUE if ptr was created by sexp_ptr() or data_ptr()
Get libffi version string
libffi_version()libffi_version()
Creates an external pointer with a specific type tag for better type safety, similar to Rffi's approach.
make_typed_pointer(ptr, type_name)make_typed_pointer(ptr, type_name)
ptr |
External pointer |
type_name |
Character string describing the pointer type |
External pointer with type tag
Native Symbol Reference
NativeSymbol(name = character(0), address = NULL, library = character(0))NativeSymbol(name = character(0), address = NULL, library = character(0))
name |
Character name of the symbol |
address |
External pointer to the symbol |
library |
Character name of library (optional) |
A NativeSymbol object
Explicitly converts an external pointer to a character string. Use this instead of relying on automatic conversion heuristics.
pointer_to_string(ptr)pointer_to_string(ptr)
ptr |
External pointer that points to a null-terminated string |
Character vector of length 1, or NULL if pointer is NULL
Convert pointer to string safely
pointer_to_string_safe(ptr)pointer_to_string_safe(ptr)
ptr |
External pointer to convert |
Character string or NULL if pointer is NULL
Retrieves the original R object from a pointer created by sexp_ptr().
ptr_to_sexp(ptr)ptr_to_sexp(ptr)
ptr |
A protected SEXP pointer |
The original R object
Normally not needed - the finalizer handles this automatically. Use only if you need to release protection early.
release_ptr(ptr)release_ptr(ptr)
ptr |
A protected pointer from sexp_ptr() or data_ptr() |
NULL invisibly
Functions for safely extracting pointers from R objects while ensuring the R object remains protected from garbage collection.
When passing R objects to C functions via FFI, we need to:
Extract the underlying pointer (SEXP or data pointer)
Prevent R from garbage collecting the object while we use the pointer
Release the protection when we're done
These helpers use R's R_PreserveObject/R_ReleaseObject mechanism
to prevent GC. The returned external pointer includes a finalizer that
automatically releases the protection when the pointer is no longer used.
Returns an external pointer to the SEXP (R object header) while ensuring the object won't be garbage collected as long as the pointer exists.
sexp_ptr(x)sexp_ptr(x)
x |
Any R object |
External pointer to the SEXP, with finalizer to release protection
## Not run: x <- c(1L, 2L, 3L) # Use c() not 1:3 to avoid ALTREP ptr <- sexp_ptr(x) # ptr is now safe to pass to C functions expecting SEXP # When ptr is garbage collected, the protection is released ## End(Not run)## Not run: x <- c(1L, 2L, 3L) # Use c() not 1:3 to avoid ALTREP ptr <- sexp_ptr(x) # ptr is now safe to pass to C functions expecting SEXP # When ptr is garbage collected, the protection is released ## End(Not run)
FFI Structure Type
StructType( name = character(0), size = integer(0), ref = NULL, fields = character(0), field_types = list(), pack = NULL, has_packed_change = logical(0) )StructType( name = character(0), size = integer(0), ref = NULL, fields = character(0), field_types = list(), pack = NULL, has_packed_change = logical(0) )
name |
Character name of the type |
size |
Integer size in bytes |
ref |
External pointer to ffi_type |
fields |
Character vector of field names |
field_types |
List of FFIType objects for each field |
pack |
Integer packing alignment (NULL for default/natural alignment) |
has_packed_change |
Logical indicating if packing changes field offsets from natural alignment. When TRUE, the struct cannot be passed by value to C functions (only pointers work) because libffi doesn't support packed structs. |
Check if TCC is available
tcc_available()tcc_available()
Logical indicating if TCC is available
Get path to embedded TCC binary
tcc_binary_path()tcc_binary_path()
Path to tcc executable in installed package
Extract #define macros from C header file or preprocessed lines
tcc_extract_defines(header_file = NULL, preprocessed_lines = NULL)tcc_extract_defines(header_file = NULL, preprocessed_lines = NULL)
header_file |
Path to C header file (optional if preprocessed_lines provided) |
preprocessed_lines |
Character vector from tcc_preprocess() (optional) |
Named list of macro definitions
Preprocess C header file using embedded TCC
tcc_preprocess(header_file, includes = NULL, keep_defines = FALSE)tcc_preprocess(header_file, includes = NULL, keep_defines = FALSE)
header_file |
Path to C header file |
includes |
Additional include directories |
keep_defines |
Keep #define directives (not supported by -E alone) |
Character vector of preprocessed lines
Compile and run C code using embedded TCC
tcc_run(code, args = character())tcc_run(code, args = character())
code |
C source code as string |
args |
Arguments to pass to compiled program |
Output from program
FFI Union Type
UnionType( name = character(0), size = integer(0), ref = NULL, fields = character(0), field_types = list(), pack = NULL, has_packed_change = logical(0) )UnionType( name = character(0), size = integer(0), ref = NULL, fields = character(0), field_types = list(), pack = NULL, has_packed_change = logical(0) )
name |
Character name of the type |
size |
Integer size in bytes |
ref |
External pointer to ffi_type |
fields |
Character vector of field names |
field_types |
List of FFIType objects for each field |
pack |
Integer packing alignment (NULL for default/natural alignment) |
has_packed_change |
Logical indicating if packing changes alignment from natural. When TRUE, the union cannot be passed by value to C functions (only pointers work) because libffi doesn't support packed unions. |