Skip to contents

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.)

Value

ccallback returns an external pointer to a synthetically generated C function.

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
# }