Function to wrap R functions as C function pointers.
Arguments
- signature
character string specifying the call signature of the C function callback type.
- fun
R function to be wrapped as a C function pointer.
- envir
the environment in which to evaluate the call to
fun.- callback
external pointer returned by
ccallback().- x
object returned by
callback_status().- ...
unused.
Value
An external pointer to a synthetically generated C function.
callback_status() returns a list with class callback_status.
callback_is_active() returns a single logical value.
callback_last_error() returns NULL or a list describing the last error.
Details
Callbacks are user-defined functions that are registered in a foreign library and that are executed at a later time from within that library. Examples include user-interface event handlers that are registered in GUI toolkits, and, comparison functions for custom data types to be passed to generic sort algorithm.
The function ccallback() wraps an R function fun as a C function pointer
and returns an external pointer.
The foreign C function type of the wrapped R function is specified by a call signature given by signature.
When the C function pointer is called, a global callback handler (implemented
in C) is executed first, that dynamically creates an R call expression to
fun using the arguments, passed from C and converted to R, according to the
argument types signature within the call signature
specified. See dyncall() for details on the format.
Finally, the handler evaluates the R call expression within the environment
given by envir. On return, the R return value of fun is coerced to the C
value, according to the return type signature specified in signature .
Aggregate by-value callback arguments and returns use the same <Type>
signature syntax as dyncall(). An aggregate argument is passed to the R
callback as a raw-backed cdata object with struct and typeinfo
attributes. An aggregate return value must be a raw-backed object for the
same aggregate type and size. Type or storage mismatches disable the callback
and record diagnostic state; no warning is emitted from the foreign frame.
Aggregate callbacks are supported on the implemented 64-bit x86 and ARM64
dyncallback backends. On unsupported backends, creating a callback whose
signature contains <Type> fails early.
If an error occurs during the evaluation, the callback will be disabled for
further invocations. Use callback_status() or callback_last_error() to
inspect a callback after foreign code has invoked it. The entire conversion
and evaluation boundary catches non-local exits, including interrupts and
warnings promoted to errors. A failed callback returns zero (or an empty
string/aggregate) to the foreign caller; the foreign operation may continue.
Failure notifications are recorded in callback_status() instead of signalling
warnings from foreign frames. Applications that must stop it should catch
conditions inside fun, request
an orderly stop from their foreign library, and rethrow after it returns.
callback_status() reports whether a callback is still active, how many
times it has been invoked, and why it was disabled. A disabled callback is
not re-enabled by these helpers; create a new callback if foreign code needs
to call into R again.
callback_is_active() returns only the active flag. callback_last_error()
returns NULL until an error disables the callback. Once an error has been
recorded, it returns a list with message, class, and reason.
Note
The call signature MUST match the foreign C callback function type, otherwise an activated callback call from C can lead to a fatal R, process crash.
A small amount of memory is allocated with each wrapper. A finalizer function that frees the allocated memory is registered at the external pointer. If the external callback function pointer is registered in a C library, a reference should also be held in R as long as the callback can be activated from a foreign C run-time context, otherwise the garbage collector might call the finalizer and the next invocation of the callback could lead to a fatal R process crash as well.
References
Adler, D. (2012) "Foreign Library Interface", The R Journal, 4(1) , 30–40, June 2012. https://journal.r-project.org/articles/RJ-2012-004/
Adler, D., Philipp, T. (2008) DynCall Project. https://dyncall.org
See also
See call signature for details on call signatures,
reg.finalizer() for details on finalizers.
Examples
# Create a function, wrap it to a callback and call it via dyncall:
f <- function(x, y) x + y
cb <- ccallback("ii)i", f)
r <- dyncall(cb, "ii)i", 20, 3)
# Sort vectors directly via 'qsort' C library function using an R callback:
dynbind(c("msvcrt","c","c.so.6"), "qsort(piip)v;")
#> dynbind report
#> library: libc.so.6
#> unresolved symbols: 0
cb <- ccallback("pp)i", function(px, py) {
x <- unpack(px, 0, "d")
y <- unpack(py, 0, "d")
if (x > y) return(1) else if (x == y) return(0) else return(-1)
})
x <- rnorm(100)
qsort(x, length(x), 8, cb)
#> NULL
x
#> [1] -2.612334333 -2.437263611 -2.274114857 -1.911720491 -1.910087468
#> [6] -1.863011492 -1.821817661 -1.699450568 -1.630989402 -1.512399651
#> [11] -1.470736306 -1.400043517 -1.304543545 -1.177563309 -0.975850616
#> [16] -0.935847354 -0.914074827 -0.826788954 -0.665088249 -0.639123324
#> [21] -0.553699384 -0.548257264 -0.522012515 -0.381951112 -0.361221255
#> [26] -0.354361164 -0.313445978 -0.296640025 -0.282705449 -0.279237242
#> [31] -0.251483443 -0.247325302 -0.245896412 -0.244199607 -0.243236740
#> [36] -0.206087195 -0.155693776 -0.133997013 -0.109935672 -0.097445104
#> [41] -0.090327287 -0.052601910 -0.049964899 -0.038102895 -0.015950311
#> [46] -0.005571287 0.019177592 0.029560754 0.046531380 0.070034850
#> [51] 0.112038083 0.118194874 0.131670635 0.172181715 0.176488611
#> [56] 0.213355750 0.236696283 0.243685465 0.255317055 0.284150344
#> [61] 0.362951256 0.424187575 0.433889790 0.444797116 0.468154420
#> [66] 0.486148920 0.488628809 0.512426950 0.523909788 0.542996343
#> [71] 0.549827542 0.556224329 0.577709069 0.606748047 0.621552721
#> [76] 0.628982042 0.737776321 0.748791268 0.862086482 0.935363190
#> [81] 0.946347886 1.048712620 1.063101996 1.065057320 1.067307879
#> [86] 1.074345882 1.110534893 1.113952419 1.148411606 1.298392759
#> [91] 1.316826356 1.318293384 1.337320413 1.623548883 1.672882611
#> [96] 1.888504929 1.924343341 2.065024895 2.682557184 2.755417575
cb <- ccallback("i)i", function(x) {
if (x < 0) stop("negative values are not supported")
x
})
callback_is_active(cb)
#> [1] TRUE
callback_status(cb)
#> <rdyncall callback: active>
#> signature: i)i
#> invocations: 0 (success: 0, errors: 0, disabled: 0)