1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
|
plugins <- new.env()
plugins[["default"]] <- function( ){
includes = '#include <R.h>
#include <Rdefines.h>
#include <R_ext/Error.h>
'
list(
includes = includes,
body = function( x ) paste( x, '\nRf_warning("your C++ program does not return anything"); \n return R_NilValue ; ' )
)
}
registerPlugin <- function( name, plugin ){
plugins[[ name ]] <- plugin
}
getPlugin <- function( name, ... ){
if( name %in% ls( plugins ) ){
plugins[[ name ]]( ... )
} else if( sprintf("package-%s", name) %in% search() || require( name, character.only = TRUE, quietly = TRUE) ){
plugin <- get( "inlineCxxPlugin" , asNamespace(name) )
if( is.null(plugin) ){
stop( sprintf( "package '%s' does not define an inline plugin", name ) )
}
registerPlugin( name, plugin )
plugin( ... )
} else {
stop( sprintf( "could not find plugin '%s'", name ) )
}
}
paste0 <- function(...) paste(..., sep="")
addLineNumbers <- function( code ){
code <- strsplit( paste( code, collapse = "\n" ), "\n" )[[1]]
sprintf( "%4d : %s", 1:length(code), code)
}
cxxfunction <- function (
sig = character(), body = character(),
plugin = "default",
includes = "",
settings = getPlugin(plugin),
...,
verbose = FALSE
){
f <- basename( tempfile( ) )
if( ! is.list( sig ) ){
sig <- list( sig )
names( sig ) <- f
if( ! length( body ) ) body <- ""
names( body ) <- f
}
if( length(sig) != length(body) )
stop("mismatch between the number of functions declared in 'sig' and the number of function bodies provided in 'body'")
signature <- lapply( sig, function(x) {
if( ! length(x) ){
""
} else {
paste( sprintf( "SEXP %s", names(x) ), collapse = ", " )
}
} )
decl <- lapply( 1:length(sig) , function(index) {
sprintf( 'SEXP %s( %s) ;', names(signature)[index] , signature[[index]] )
} )
def <- lapply( 1:length(sig), function(index){
sprintf( '
SEXP %s( %s ){
%s
}
', names(signature)[index],
signature[[index]],
if(is.null(settings$body)) body[[index]] else settings$body(body[[index]]) )
} )
settings_includes <- if( is.null( settings$includes ) ) "" else paste( settings$includes, collapse = "\n" )
code <- sprintf( '// includes from the plugin
%s
// user includes
%s
// declaration
extern "C" {
%s
}
// definition
%s
', settings_includes , paste( includes, collapse = "\n" ),
paste( decl, collapse = "\n" ),
paste( def, collapse = "\n")
)
if( !is.null( env <- settings$env ) ){
do.call( Sys.setenv, env )
if( isTRUE(verbose) ){
cat( " >> setting environment variables: \n" )
writeLines( sprintf( "%s = %s", names(env), env ) )
}
}
LinkingTo <- settings$LinkingTo
if( !is.null( LinkingTo ) ){
paths <- .find.package(LinkingTo, quiet=TRUE)
if( length( paths ) ){
flag <- paste(
paste0( '-I"', paths, '/include"' ),
collapse = " " )
Sys.setenv( CLINK_CPPFLAGS = flag )
if( isTRUE( verbose ) ){
cat( sprintf( "\n >> LinkingTo : %s\n", paste( LinkingTo, collapse = ", " ) ) )
cat( "CLINK_CPPFLAGS = ", flag, "\n\n" )
}
}
}
if( isTRUE( verbose ) ){
writeLines( " >> Program source :\n" )
writeLines( addLineNumbers( code ) )
}
language <- "C++"
## WRITE AND COMPILE THE CODE
libLFile <- compileCode( f, code, language = language, verbose = verbose )
## SET A FINALIZER TO PERFORM CLEANUP
cleanup <- function(env) {
if ( f %in% names(getLoadedDLLs()) ) dyn.unload(libLFile)
unlink(libLFile)
}
reg.finalizer(environment(), cleanup, onexit=TRUE)
## Create new objects of class CFunc, each containing the code of ALL inline
## functions. This will be used to recompile the whole shared lib when needed
res <- vector("list", length(sig))
names(res) <- names(sig)
res <- new( "CFuncList", res )
for( i in seq_along(sig) ){
res[[i]] <- new( "CFunc", code = code )
## this is the skeleton of the function, the external call is added below using 'body'
## important here: all variables are kept in the local environment
fn <- function(arg) {
if ( !file.exists(libLFile) )
libLFile <<- compileCode(f, code, "C++", verbose)
if ( !( f %in% names(getLoadedDLLs()) ) ) dyn.load(libLFile)
}
## Modify the function formals to give the right argument list
args <- formals(fn)[ rep(1, length(sig[[i]])) ]
names(args) <- names(sig[[i]])
formals(fn) <- args
## create .C/.Call function call that will be added to 'fn'
body <- quote( .Call( "EXTERNALNAME", PACKAGE=f, ARG) )[ c(1:3, rep(4, length(sig[[i]]))) ]
for ( j in seq(along = sig[[i]]) ) body[[j+3]] <- as.name(names(sig[[i]])[j])
body[[2]] <- names(sig)[[i]]
## update the body of 'fn'
body(fn)[[4]] <- body
## set fn as THE function in CFunc of res[[i]]
res[[i]]@.Data <- fn
}
## clear the environment
rm( j )
if( identical( length(sig), 1L ) ) res[[1L]] else res
}
|