Skip to contents

Function to wrap R functions as C function pointers.

Usage

ccallback(signature, fun, envir = new.env())

callback_status(callback)

callback_is_active(callback)

callback_last_error(callback)

# S3 method for class 'callback_status'
print(x, ...)

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)