Dynamic wrapping of R functions as C callbacks
dyncallback.Rd
Function to wrap R functions as C function pointers.
Usage
ccallback(signature, fun, envir = new.env())
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
.
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
.
If an error occurs during the evaluation, the callback will be disabled for
further invocations. (This behaviour might change in the future.)
Portability
The implementation is based on the dyncallback library (part of the DynCall project).
The following processor architectures are supported: X86, X64, ARM (including Thumb) and partial stable support for PowerPC 32-bit; The library has been built and tested to work on various OSs: Linux, Mac OS X, Windows 32/64-bit, BSDs, Haiku, Nexenta/Open Solaris, Minix and Plan9, as well as embedded platforms such as Linux/ARM (OpenMoko, Beagleboard, Gumstix, Efika MX, Raspberry Pi), Nintendo DS (ARM), Sony Playstation Portable (MIPS 32-bit/eabi) and iOS (ARM - armv6 mode ok, armv7 unstable). Special notes for PowerPC 32-Bit: Callbacks for System V (Linux/BSD) are unstable in this release; MacOS X/Darwin works fine. In the context of R, dyncallback has currently no support for callbacks on MIPS, SPARC and PowerPC 64-Bit. Using dyncallback to implement non-default calling conventions is not supported yet. (e.g. Window Procedures on Win32/X86).
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 signature
for details on call signatures,
reg.finalizer
for details on finalizers.
Examples
# \donttest{
# 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;")
#> $libhandle
#> <pointer: 0x7fc851f49c30>
#> attr(,"path")
#> [1] "libc.so.6"
#> attr(,"auto.unload")
#> [1] TRUE
#>
#> $unresolved.symbols
#> character(0)
#>
#> attr(,"class")
#> [1] "dynbind.report"
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.51498224 -2.37701799 -2.07012070 -1.79026903 -1.76946654 -1.68702626
#> [7] -1.57653931 -1.47639384 -1.39810761 -1.39088577 -1.37326218 -1.14813541
#> [13] -1.04627491 -0.93645066 -0.88500061 -0.86082120 -0.85384714 -0.84633478
#> [19] -0.83857011 -0.80362628 -0.76851728 -0.75471887 -0.74489095 -0.70095913
#> [25] -0.68239883 -0.67169592 -0.59745438 -0.57238122 -0.57222245 -0.56749729
#> [31] -0.55612174 -0.55040754 -0.52713571 -0.45316727 -0.44552153 -0.44080254
#> [37] -0.37493484 -0.36861222 -0.35048638 -0.33423775 -0.28789487 -0.20654768
#> [43] -0.15518753 -0.05464603 -0.04494804 -0.01020229 0.05379768 0.06764315
#> [49] 0.09359007 0.09614483 0.13595083 0.13785350 0.17579595 0.19273734
#> [55] 0.26925586 0.27195869 0.36669333 0.39724949 0.40270607 0.43713346
#> [61] 0.46168670 0.47104623 0.51005708 0.51891454 0.55482566 0.56174459
#> [67] 0.58131392 0.59975488 0.63771279 0.70777987 0.72348474 0.74351713
#> [73] 0.81151878 0.81668518 0.85154361 0.85462698 0.99167979 1.00829285
#> [79] 1.03799017 1.07732472 1.11142901 1.13002176 1.13572653 1.15545315
#> [85] 1.16268399 1.22401495 1.24495818 1.27751250 1.34057326 1.44288042
#> [91] 1.48292940 1.51970836 1.58983885 1.74263195 1.84140045 1.88903641
#> [97] 1.89955269 1.91274885 1.99630548 2.14128757
# }