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 emit a warning.

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.

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)