[Rcpp-commits] r3858 - in pkg/Rcpp: . R inst inst/include/Rcpp man src

noreply at r-forge.r-project.org noreply at r-forge.r-project.org
Tue Oct 30 15:57:05 CET 2012


Author: jjallaire
Date: 2012-10-30 15:57:05 +0100 (Tue, 30 Oct 2012)
New Revision: 3858

Added:
   pkg/Rcpp/R/Attributes.R
   pkg/Rcpp/man/compileAttributes.Rd
   pkg/Rcpp/man/dependsAttribute.Rd
   pkg/Rcpp/man/exportAttribute.Rd
   pkg/Rcpp/man/sourceCpp.Rd
   pkg/Rcpp/src/Attributes.cpp
Modified:
   pkg/Rcpp/ChangeLog
   pkg/Rcpp/NAMESPACE
   pkg/Rcpp/inst/NEWS.Rd
   pkg/Rcpp/inst/include/Rcpp/exceptions.h
Log:
new attributes feature (sourceCpp and compileAttributes functions)

Modified: pkg/Rcpp/ChangeLog
===================================================================
--- pkg/Rcpp/ChangeLog	2012-10-30 10:11:11 UTC (rev 3857)
+++ pkg/Rcpp/ChangeLog	2012-10-30 14:57:05 UTC (rev 3858)
@@ -1,3 +1,16 @@
+2012-10-30  JJ Allaire <jj at rstudio.org>
+
+    * R/Attributes.R: new functions sourceCpp and compileAttributes
+    that use C++11 style attributes (embedded in comments) to 
+    perform automatic generation of boilerplate marshaling code
+    * src/Attributes.cpp: support functions for attribute handling
+    * include/Rcpp/exceptions.h: file io exception classes
+    * man/sourceCpp.Rd: documentation for sourceCpp
+    * man/compileAttributes.Rd: documentation for compileAttributes
+    * man/exportAttriute.Rd: documentation for export attribute
+    * man/dependsAttribute.Rd: documentation for depends attribute
+    * NAMESPACE: exports for sourceCpp and compileAttributes
+
 2012-10-30  Romain Francois <romain at r-enthusiasts.com>
 
         * include/Rcpp/as.h: new bare_as to simplify uses of as

Modified: pkg/Rcpp/NAMESPACE
===================================================================
--- pkg/Rcpp/NAMESPACE	2012-10-30 10:11:11 UTC (rev 3857)
+++ pkg/Rcpp/NAMESPACE	2012-10-30 14:57:05 UTC (rev 3858)
@@ -16,7 +16,7 @@
 
 export( 
     Module, Rcpp.package.skeleton, populate, loadRcppModules, setRcppClass,
-       loadModule
+       loadModule, sourceCpp, compileAttributes
 )
 
 exportClass(RcppClass)

Added: pkg/Rcpp/R/Attributes.R
===================================================================
--- pkg/Rcpp/R/Attributes.R	                        (rev 0)
+++ pkg/Rcpp/R/Attributes.R	2012-10-30 14:57:05 UTC (rev 3858)
@@ -0,0 +1,432 @@
+# Copyright (C) 2010 - 2012 JJ Allaire, Dirk Eddelbuettel and Romain Francois
+#
+# This file is part of Rcpp.
+#
+# Rcpp is free software: you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# Rcpp is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Rcpp.  If not, see <http://www.gnu.org/licenses/>.
+
+
+# Source C++ code from a file
+sourceCpp <- function(file, 
+                      local = FALSE, 
+                      rebuild = FALSE,
+                      show.output = verbose,
+                      verbose = getOption("verbose")) {
+    
+    # get the context (does code generation as necessary)
+    file <- normalizePath(file, winslash = "/")
+    context <- .Call("sourceCppContext", PACKAGE="Rcpp", file, .Platform)
+     
+    # perform a build if necessary
+    if (context$buildRequired || rebuild) {
+    
+        # print output for verbose mode 
+        if (verbose) 
+            .printVerboseOutput(context)   
+        
+        # variables used to hold completed state (passed to completed hook)
+        succeeded <- FALSE
+        output <- NULL
+        
+        # validate packages (error if package not found)
+        .validatePackages(context$depends, context$cppSourceFilename)
+        
+        # temporarily modify environment for the build
+        envRestore <- .setupBuildEnvironment(context$depends)
+        
+        # temporarily setwd to build directory
+        cwd <- getwd()
+        setwd(context$buildDirectory)
+          
+        # call the onBuild hook. note that this hook should always be called
+        # after .setupBuildEnvironment so subscribers are able to examine
+        # the build environment
+        if (!.callBuildHook(context$cppSourcePath, show.output)) {
+            .restoreEnvironment(envRestore)
+            setwd(cwd)
+            return (invisible(NULL))
+        }
+        
+        # on.exit handler calls hook and restores environment and working dir
+        on.exit({
+            .callBuildCompleteHook(succeeded, output)
+            setwd(cwd)
+            .restoreEnvironment(envRestore)
+        })
+        
+        # unload and delete existing dylib if necessary
+        if (file.exists(context$dynlibPath)) {
+            try(silent=TRUE, {
+                dyn.unload(context$dynlibPath)
+                file.remove(context$dylibPath)
+            })
+        }
+           
+        # prepare the command (output if we are in show.output mode)
+        cmd <- paste(R.home(component="bin"), .Platform$file.sep, "R ",
+                     "CMD SHLIB ",  
+                     "-o ", shQuote(context$dynlibFilename), " ",
+                     ifelse(rebuild, "--preclean ", ""),
+                     shQuote(context$cppSourceFilename), sep="")
+        if (show.output)
+            cat(cmd, "\n")
+        
+        # execute the build -- suppressWarnings b/c when show.output = FALSE
+        # we are going to explicitly check for an error and print the output
+        result <- suppressWarnings(system(cmd, intern = !show.output))
+        
+        # check build results
+        if(!show.output) {
+            # capture output
+            output <- result
+            attributes(output) <- NULL
+            # examine status
+            status <- attr(result, "status")
+            if (!is.null(status)) {
+                cat(result, "\n")
+                succeeded <- FALSE
+                stop("Error ", status, " occurred building shared library.")
+            } else {
+                succeeded <- TRUE
+            }
+        } 
+        else if (!identical(as.character(result), "0")) {
+            succeeded <- FALSE
+            stop("Error ", result, " occurred building shared library.")
+        } else {
+            succeeded <- TRUE
+        }
+    } 
+    else {
+        if (verbose)
+            cat("\nNo rebuild required (use rebuild = TRUE to ",
+                "force a rebuild)\n\n", sep="")
+    }
+    
+    # source the R script
+    scriptPath <- file.path(context$buildDirectory, context$rSourceFilename)    
+    source(scriptPath, local)
+}
+
+# Scan the source files within a package for attributes and generate code
+# based on the attributes. 
+compileAttributes <- function(pkgdir = ".", verbose = getOption("verbose")) {
+    
+    # verify this is a package and read the DESCRIPTION to get it's name
+    pkgdir <- normalizePath(pkgdir, winslash = "/")
+    descFile <- file.path(pkgdir,"DESCRIPTION")
+    if (!file.exists(descFile))
+        stop("pkgdir must refer to the directory containing an R package")
+    DESCRIPTION <- read.dcf(descFile, all = TRUE)
+    pkgname <- DESCRIPTION$Package
+    
+    # determine source directory
+    srcDir <- file.path(pkgdir, "src")
+    if (!file.exists(srcDir))
+        return (FALSE)
+    
+    # create R directory if it doesn't already exist
+    rDir <- file.path(pkgdir, "R")
+    if (!file.exists(rDir))
+        dir.create(rDir)
+    
+    # get a list of all source files
+    cppFiles <- list.files(srcDir, pattern=glob2rx("*.c*"))
+    cppFiles <- file.path(srcDir, cppFiles)
+    cppFiles <- normalizePath(cppFiles, winslash = "/")
+    
+    # generate the includes list based on LinkingTo
+    includes <- .rcppExportsIncludes(DESCRIPTION$LinkingTo)
+    
+    # generate exports
+    invisible(.Call("compileAttributes", PACKAGE="Rcpp", 
+                    pkgdir, pkgname, cppFiles, includes, verbose, .Platform))
+}
+
+
+# Print verbose output
+.printVerboseOutput <- function(context) {
+    
+    cat("\nGenerated extern \"C\" functions",
+        "\n--------------------------------------------------------\n")
+    cat(context$generatedCpp, sep="")
+    
+    cat("\nGenerated R .Call bindings",
+        "\n-------------------------------------------------------\n\n")
+    cat(readLines(file.path(context$buildDirectory, 
+        context$rSourceFilename)), 
+        sep="\n")
+    
+    cat("Building shared library", 
+        "\n--------------------------------------------------------\n",
+        "\nDIR: ", context$buildDirectory, "\n\n", sep="")
+}
+
+
+# Error if a package is not currently available
+.validatePackages <- function(depends, sourceFilename) {
+    unavailable <- depends[!depends %in% .packages(all.available=TRUE)]
+    if (length(unavailable) > 0) {
+        stop(paste("Package '", unavailable[[1]], "' referenced from ",
+                    "Rcpp::depends in source file ",
+                      sourceFilename, " is not available.", 
+                      sep=""),
+                call. = FALSE)
+    }
+}
+
+
+# Get the inline plugin for the specified package (return NULL if none found)
+.getInlinePlugin <- function(package) {
+    tryCatch(get("inlineCxxPlugin", asNamespace(package)),
+             error = function(e) NULL) 
+}
+
+# Setup the build environment based on the specified dependencies. Returns an
+# opaque object that can be passed to .restoreEnvironment to reverse whatever
+# changes that were made
+.setupBuildEnvironment <- function(depends) {
+    
+    # discover dependencies
+    buildEnv <- list()
+    linkingToPackages <- c("Rcpp")
+    for (package in depends) {
+        
+        # add a LinkingTo for this package
+        linkingToPackages <- unique(c(linkingToPackages, package))
+        
+        # see if the package exports a plugin
+        plugin <- .getInlinePlugin(package)
+        if (!is.null(plugin)) {
+            
+            # get the plugin settings 
+            settings <- plugin()
+            
+            # merge environment variables
+            pluginEnv <- settings$env
+            for (name in names(pluginEnv)) {
+                # if it doesn't exist already just set it
+                if (is.null(buildEnv[[name]])) {
+                    buildEnv[[name]] <- pluginEnv[[name]]
+                }
+                # if it's not identical then append
+                else if (!identical(buildEnv[[name]],
+                                    pluginEnv[[name]])) {
+                    buildEnv[[name]] <- paste(buildEnv[[name]], 
+                                              pluginEnv[[name]]);
+                }
+                else {
+                    # it already exists and it's the same value, this 
+                    # likely means it's a flag-type variable so we 
+                    # do nothing rather than appending it
+                }   
+            }
+            
+            # capture any LinkingTo elements defined by the plugin
+            linkingToPackages <- unique(c(linkingToPackages, 
+                                          settings$LinkingTo))
+        }
+    }
+    
+    # if there is no buildEnv from a plugin then use the Rcpp plugin
+    if (length(buildEnv) == 0) {
+        buildEnv <- Rcpp:::inlineCxxPlugin()$env
+    } else {
+        # we are using a plugin -- confirm that the plugin includes the Rcpp
+        # PKG_LIBS and if it doesn't then add them
+        pkgLibs <- buildEnv$PKG_LIBS
+        rcppLibs <- Rcpp:::RcppLdFlags()
+        if (is.null(pkgLibs) || !grepl(rcppLibs, pkgLibs, fixed = TRUE))
+            buildEnv$PKG_LIBS <- paste(pkgLibs, rcppLibs)
+    }
+    
+    # set cxxFlags based on the LinkingTo dependencies (and also respect
+    # any PKG_CXXFLAGS set by the plugin)
+    pkgCxxFlags <- .buildPkgCxxFlags(linkingToPackages)
+    buildEnv$PKG_CXXFLAGS <- paste(buildEnv$PKG_CXXFLAGS, pkgCxxFlags)        
+
+    # add cygwin message muffler
+    buildEnv$CYGWIN = "nodosfilewarning"
+    
+    # create restore list
+    restore <- list()
+    for (name in names(buildEnv))
+        restore[[name]] <- Sys.getenv(name, unset = NA)
+        
+    # set environment variables
+    do.call(Sys.setenv, buildEnv)
+     
+    # return restore list
+    return (restore)
+}
+
+# Build PKG_CXXFLAGS by from include directories of LinkingTo packages
+.buildPkgCxxFlags <- function(linkingToPackages) {
+    pkgCxxFlags <- NULL
+    for (package in linkingToPackages) {
+        packagePath <- find.package(package, NULL, quiet=TRUE)
+        pkgCxxFlags <- paste(pkgCxxFlags, 
+            paste0('-I"', packagePath, '/include"'), 
+            collapse=" ")
+    }
+    return (pkgCxxFlags)
+}
+
+.restoreEnvironment <- function(restore) {
+    # variables to reset
+    setVars <- restore[!is.na(restore)]
+    if (length(setVars))
+        do.call(Sys.setenv, setVars)
+    
+    # variables to remove
+    removeVars <- names(restore[is.na(restore)])
+    if (length(removeVars))
+        Sys.unsetenv(removeVars)
+}
+
+
+# Call the onBuild hook. This hook is provided so that external tools
+# can perform processing (e.g. lint checking or other diagnostics) prior
+# to the execution of a build). The show.output flag is there to inform the
+# subscriber whether they'll be getting output in the onBuildComplete hook
+# or whether it will need to be scraped from the console (for verbose=TRUE)
+# The onBuild hook is always called from within the temporary build directory
+.callBuildHook <- function(file, show.output) {
+        
+    for (fun in .getHooksList("sourceCpp.onBuild")) {
+        
+        if (is.character(fun)) 
+            fun <- get(fun)
+        
+        # allow the hook to cancel the build (errors in the hook explicitly
+        # do not cancel the build since they are unexpected bugs)
+        continue <- tryCatch(fun(file, show.output),
+                             error = function(e) TRUE)
+        
+        if (!continue)
+            return (FALSE)
+    }    
+    
+    return (TRUE)
+}
+
+# Call the onBuildComplete hook. This hook is provided so that external tools
+# can do analysis of build errors and (for example) present them in a 
+# navigable list. Note that the output parameter will be NULL when show.output
+# is TRUE. Tools can try to scrape the output from the console (in an 
+# implemenentation-dependent fashion) or can simply not rely on output 
+# processing in that case (since the user explicitly asked for output to be
+# printed to the console). The onBuildCompleted hook is always called within
+# the temporary build directory.
+.callBuildCompleteHook <- function(succeeded, output) {
+    
+    # Call the hooks in reverse order to align sequencing with onBuild
+    for (fun in .getHooksList("sourceCpp.onBuildComplete")) {
+        
+        if (is.character(fun)) 
+            fun <- get(fun)
+        
+        try(fun(succeeded, output))
+    }
+}
+
+# The value for getHooks can be a single function or a list of functions,
+# This function ensures that the result can always be processed as a list
+.getHooksList <- function(name) {
+    hooks <- getHook(name)
+    if (!is.list(hooks))
+        hooks <- list(hooks)
+    hooks
+}
+
+
+# Generate list of includes for RcppExports.cpp based on LinkingTo.
+# Since the RcppExports.cpp file contains only shims, we typically don't
+# need to capture headers from linked to packages. However, if a linked
+# to package includes definitions of Rcpp type converters (as/wrap) then
+# we do need those headers. To distinguish this case and to capture 
+# headers in the correct order we analyze the contents of the plugin's
+# includes field and extact the includes.before and includes.after
+.rcppExportsIncludes <- function(linkingTo) {
+    
+    # This field can be NULL or empty -- in that case just return Rcpp.h
+    if (is.null(linkingTo) || !nzchar(linkingTo))
+        return (c("#include <Rcpp.h>"))
+    
+    # Look for Rcpp inline plugins within the list or LinkedTo packages
+    include.before <- character()
+    include.after <- character()
+    linkingToPackages <- strsplit(linkingTo, "\\s*\\,")[[1]]
+    linkingToPackages <- gsub("\\s", "", linkingToPackages)
+    for (package in linkingToPackages) {
+        
+        # We already handle Rcpp internally
+        if (identical(package, "Rcpp"))
+            next
+        
+        # see if there is a plugin that we can extract includes from
+        plugin <- .getInlinePlugin(package)
+        if (!is.null(plugin)) {
+            includes <- .pluginIncludes(plugin)
+            if (!is.null(includes)) {
+                include.before <- c(include.before, includes$before)
+                include.after <- c(include.after, includes$after)
+            }
+        }
+    }
+    
+    # return the includes
+    c(include.before, "#include <Rcpp.h>", include.after)
+}
+
+# Analyze the plugin's includes field to determine include.before and
+# include.after. We are ONLY interested in plugins that work with Rcpp since 
+# the only types we need from includes are as/wrap marshallers. Therefore, 
+# we verify that the plugin was created using Rcpp.plugin.maker and then
+# use that assumption to correctly extract include.before and include.after
+.pluginIncludes <- function(plugin) {
+      
+    # First determine the standard suffix of an Rcpp plugin by calling
+    # Rcpp.plugin.maker. If the plugin$includes has this suffix we know
+    # it's an Rcpp plugin
+    token <- "include_after_token"
+    stockRcppPlugin <- Rcpp:::Rcpp.plugin.maker(include.after=token)
+    includes <- stockRcppPlugin()$includes
+    suffix <- strsplit(includes, token)[[1]][[2]]
+    
+    # now ask the plugin for it's includes, ensure that the plugin includes
+    # are not null, and verify they have the Rcpp suffix before proceeding
+    pluginIncludes <- plugin()$includes
+    if (is.null(pluginIncludes)) 
+        return (NULL)
+    if (!grepl(suffix, pluginIncludes))
+        return (NULL)
+    
+    # strip the suffix then split on stock Rcpp include to get before and after
+    pluginIncludes <- strsplit(pluginIncludes, suffix)[[1]][[1]]
+    pluginIncludes <- strsplit(pluginIncludes, c("#include <Rcpp.h>"))[[1]]
+
+    # extract before and after and nix empty lines
+    before <- pluginIncludes[[1]]
+    before <- strsplit(before, "\n")[[1]]
+    before <- before[nzchar(before)]
+    after <- pluginIncludes[[2]]
+    after <- strsplit(after, "\n")[[1]]
+    after <- after[nzchar(after)]
+    
+    # return before and after
+    list(before = before, after = after)
+}
+
+
+

Modified: pkg/Rcpp/inst/NEWS.Rd
===================================================================
--- pkg/Rcpp/inst/NEWS.Rd	2012-10-30 10:11:11 UTC (rev 3857)
+++ pkg/Rcpp/inst/NEWS.Rd	2012-10-30 14:57:05 UTC (rev 3858)
@@ -8,6 +8,9 @@
     [ TODO -- Lots more Rcpp modules work to be described ? ]
     \item Provide a namespace 'R' for the standalone Rmath library so
     that Rcpp users can access those functions too 
+    \item Added new functions sourceCpp() and compileAttributes()
+    that use C++11 style attributes (embedded in comments) to 
+    perform automatic generation of boilerplate marshaling code
   }
 }
 

Modified: pkg/Rcpp/inst/include/Rcpp/exceptions.h
===================================================================
--- pkg/Rcpp/inst/include/Rcpp/exceptions.h	2012-10-30 10:11:11 UTC (rev 3857)
+++ pkg/Rcpp/inst/include/Rcpp/exceptions.h	2012-10-30 14:57:05 UTC (rev 3858)
@@ -50,7 +50,27 @@
     std::string message ;                                                      
 } ;
 
+class file_io_error : public std::exception {                                      
+public:                                                                        
+    file_io_error(const std::string& file) throw() : message( std::string("file io error: '") + file + "'" ){} ;
+    file_io_error(int code, const std::string& file) throw() : message( "file io error " + toString(code) + ": '" + file + "'") {} ;
+    file_io_error(const std::string& msg, const std::string& file) throw() : message( msg + ": '" + file + "'") {} ;
+    virtual ~file_io_error() throw(){} ;                                         
+    virtual const char* what() const throw(){ return message.c_str() ; } ;     
+private:                                                                       
+    std::string message ;                                                      
+} ;
 
+class file_not_found : public file_io_error {
+public:
+    file_not_found(const std::string& file) throw() : file_io_error("file not found", file) {}
+};
+
+class file_exists : public file_io_error {
+public:
+    file_exists(const std::string& file) throw() : file_io_error("file already exists", file) {}
+};
+
 #define RCPP_EXCEPTION_CLASS(__CLASS__,__WHAT__)                               \
 class __CLASS__ : public std::exception{                                       \
 public:                                                                        \

Added: pkg/Rcpp/man/compileAttributes.Rd
===================================================================
--- pkg/Rcpp/man/compileAttributes.Rd	                        (rev 0)
+++ pkg/Rcpp/man/compileAttributes.Rd	2012-10-30 14:57:05 UTC (rev 3858)
@@ -0,0 +1,44 @@
+\name{compileAttributes}
+\alias{compileAttributes}
+\title{
+Compile Rcpp Attributes for a Package
+}
+\description{
+Scan the source files within a package for attributes and generate code as required. Generates the bindings required to call C++ functions from R for functions adorned with the \code{Rcpp::export} attribute.
+}
+\usage{
+compileAttributes(pkgdir = ".", verbose = getOption("verbose"))
+}
+%- maybe also 'usage' for other objects documented here.
+\arguments{
+  \item{pkgdir}{
+    Directory containing the package to compile attributes for (defaults to the current working directory)
+}
+  \item{verbose}{
+    \code{TRUE} to print detailed information about generated code to the console
+}
+}
+\details{
+    The source files in the package directory given by \code{pkgdir} are scanned for attributes and code is generated as required based on the attributes. 
+    
+    For C++ functions adorned with the \code{Rcpp::export} attribute, the C++ and R source code required to bind to the function from R is generated and added (respectively) to \code{src/RcppExports.cpp} or \code{R/RcppExports.R}.
+    
+    In order to access the declarations for custom \code{Rcpp::as} and \code{Rcpp::wrap} handlers the \code{compileAttributes} function will also call any \link[inline:plugins]{inline plugins} available for packages listed in the \code{LinkingTo} field of the \code{DESCRIPTION} file.
+}
+\value{
+    Returns \code{TRUE} if generated code was updated, \code{FALSE} if no updates were required.
+}
+
+\seealso{
+\code{\link[=exportAttribute]{Rcpp::export}}
+}
+
+
+\examples{
+\dontrun{
+
+# Compile attributes for package in the current working dir
+compileAttributes()
+}
+}
+

Added: pkg/Rcpp/man/dependsAttribute.Rd
===================================================================
--- pkg/Rcpp/man/dependsAttribute.Rd	                        (rev 0)
+++ pkg/Rcpp/man/dependsAttribute.Rd	2012-10-30 14:57:05 UTC (rev 3858)
@@ -0,0 +1,41 @@
+\name{dependsAttribute}
+\alias{dependsAttribute}
+
+\title{Rcpp::depends Attribute}
+
+\description{
+The \code{Rcpp::depends} attribute is added to a C++ source file to indicate that it has a compilation dependency on one or more other packages. For example:
+\preformatted{
+// [[Rcpp::depends(RcppArmadillo)]]
+}
+}
+
+\arguments{
+ \item{\dots}{
+    Packages which the source file depends on for compilation
+}
+}
+
+\details{
+    The \code{Rcpp::depends} attribute is used by the implementation of the \code{\link{sourceCpp}} function to correctly setup the build environment for \code{R CMD SHLIB}. 
+    
+    The include directories of the specified packages are added to the \code{PKG_CXXFLAGS} environment varible. In addition, if the referenced package provides an \link[inline:plugins]{inline plugin} it is called to determine additional environment variables required to sucessfully build.
+    
+    The \code{Rcpp::depends} attribute is specified using a syntax compatible with the new \href{http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2761.pdf}{generalized attributes} feature of the C++11 standard. Note however that since this feature is not yet broadly supported by compilers it needs to be specified within a comment (see examples below).
+
+}
+
+
+\seealso{
+\code{\link{sourceCpp}}
+}
+
+\examples{
+\dontrun{
+
+// [[Rcpp::depends(RcppArmadillo)]]
+
+// [[Rcpp::depends(Matrix, RcppGSL)]]
+}
+}
+

Added: pkg/Rcpp/man/exportAttribute.Rd
===================================================================
--- pkg/Rcpp/man/exportAttribute.Rd	                        (rev 0)
+++ pkg/Rcpp/man/exportAttribute.Rd	2012-10-30 14:57:05 UTC (rev 3858)
@@ -0,0 +1,53 @@
+\name{exportAttribute}
+\alias{exportAttribute}
+
+\title{Rcpp::export Attribute}
+
+\description{
+The \code{Rcpp::export} attribute is added to a C++ function definition to indicate that it should be made available as an R function. The \code{\link{sourceCpp}} and \code{\link{compileAttributes}} functions process the \code{Rcpp::export} attribute by generating the code required to call the C++ function from R.
+}
+
+\arguments{
+  \item{name}{
+    Specify an alternate name for the generated R function (optional, defaults to the name of the C++ function if not specified).
+}
+}
+
+\details{
+    Functions marked with the \code{Rcpp::export} attribute must have return types that are compatible with \code{Rcpp::wrap} and parameter types that are compatible with \code{Rcpp::as}.
+    
+    The \code{Rcpp::export} attribute is specified using a syntax compatible with the new \href{http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2761.pdf}{generalized attributes} feature of the C++11 standard. Note however that since this feature is not yet broadly supported by compilers it needs to be specified within a comment (see examples below).
+}
+
+
+\seealso{
+\code{\link{sourceCpp}}, \code{\link{compileAttributes}}
+}
+
+\examples{
+\dontrun{
+
+// [[Rcpp::export]]
+int fibonacci(const int x) {
+
+   if (x == 0) return(0);
+   if (x == 1) return(1);
+   
+   return (fibonacci(x - 1)) + fibonacci(x - 2);
+}
+
+// [[Rcpp::export("convolveCpp")]]
+NumericVector convolve(NumericVector a, NumericVector b) {
+
+   int na = a.size(), nb = b.size();
+   int nab = na + nb - 1;
+   NumericVector xab(nab);
+    
+   for (int i = 0; i < na; i++)
+      for (int j = 0; j < nb; j++)
+         xab[i + j] += a[i] * b[j];
+    
+   return xab;
+}
+}
+}

Added: pkg/Rcpp/man/sourceCpp.Rd
===================================================================
--- pkg/Rcpp/man/sourceCpp.Rd	                        (rev 0)
+++ pkg/Rcpp/man/sourceCpp.Rd	2012-10-30 14:57:05 UTC (rev 3858)
@@ -0,0 +1,46 @@
+\name{sourceCpp}
+\alias{sourceCpp}
+\title{
+Source C++ Code from a File
+}
+\description{
+\code{sourceCpp} parses the specified C++ file and looks for functions marked with the \code{\link[=exportAttribute]{Rcpp::export}} attribute. The C++ file is then built into a shared library and it's exported functions are made available as R functions in the specified environment.
+}
+\usage{
+sourceCpp(file, local = FALSE, rebuild = FALSE, 
+          show.output = verbose, verbose = getOption("verbose"))
+}
+%- maybe also 'usage' for other objects documented here.
+\arguments{
+  \item{file}{
+    A character string giving the pathname of a file.
+}
+  \item{local}{
+    \code{TRUE}, \code{FALSE} or an environment, determining where the R functions will be made available. \code{FALSE} (the default) corresponds to the user's workspace (the global environment) and \code{TRUE} to the environment from which source is called
+}
+  \item{rebuild}{
+    Force a rebuild of the shared library by passing \code{--preclean} to \code{R CMD SHLIB} 
+}
+  \item{show.output}{
+    \code{TRUE} to print \code{R CMD SHLIB} output to the console
+}
+  \item{verbose}{
+    \code{TRUE} to print detailed information about generated code to the console
+}
+}
+\details{
+    Functions marked with the \code{\link[=exportAttribute]{Rcpp::export}} attribute must have return types that are compatible with \code{Rcpp::wrap} and parameter types that are compatible with \code{Rcpp::as}.
+    
+    If the source file has compilation dependencies on other packages (e.g. \pkg{Matrix}, \pkg{RcppArmadillo}) then an \code{\link[=dependsAttribute]{Rcpp::depends}} attribute should be added to the file naming these dependencies.
+}
+
+\seealso{
+\code{\link[=exportAttribute]{Rcpp::export}}, \code{\link[=dependsAttribute]{Rcpp::depends}}
+}
+
+\examples{
+\dontrun{
+
+sourceCpp("transform.cpp")
+}
+}

Added: pkg/Rcpp/src/Attributes.cpp
===================================================================
--- pkg/Rcpp/src/Attributes.cpp	                        (rev 0)
+++ pkg/Rcpp/src/Attributes.cpp	2012-10-30 14:57:05 UTC (rev 3858)
@@ -0,0 +1,1379 @@
+// -*- mode: C++; c-indent-level: 4; c-basic-offset: 4; indent-tabs-mode: nil; -*-
+//
+// Attributes.cpp: Rcpp R/C++ interface class library -- Rcpp attributes
+//
+// Copyright (C) 2010 - 2012  JJ Allaire, Dirk Eddelbuettel and Romain Francois
+//
+// This file is part of Rcpp.
+//
+// Rcpp is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Rcpp is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Rcpp.  If not, see <http://www.gnu.org/licenses/>.
+
+
+#include <fstream>
+#include <cstring>
+#include <map>
+#include <algorithm>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#include <Rcpp.h>
+#include <Rcpp/exceptions.h>
+
+namespace {
+
+// Some parsing utilities
+
+#define kWhitespaceChars " \f\n\r\t\v"
+
+// Query whether a character is whitespace
+bool isWhitespace(char ch) {
+    return std::strchr(kWhitespaceChars, ch) != NULL;
+}
+
+// Trim a string
+void trimWhitespace(std::string* pStr) {   
+    
+    // skip empty case
+    if (pStr->empty())
+        return;
+    
+    // trim right
+    std::string::size_type pos = pStr->find_last_not_of(kWhitespaceChars);
+    if (pos != std::string::npos)
+        pStr->erase(pos + 1);    
+        
+    // trim left
+    pos = pStr->find_first_not_of(kWhitespaceChars);
+    pStr->erase(0, pos);
+}
+
+// Strip balanced quotes from around a string (assumes already trimmed)
+void stripQuotes(std::string* pStr) {
+    if (pStr->length() < 2)
+        return;       
+    char quote = *(pStr->begin());
+    if ( (quote == '\'' || quote == '\"') && (*(pStr->rbegin()) == quote) )
+        *pStr = pStr->substr(1, pStr->length()-2);
+}
+
+// Utility class for getting file existence and last modified time
+class FileInfo {
+public:    
+    explicit FileInfo(const std::string& path) 
+        : path_(path), exists_(false), lastModified_(0)
+    {
+#ifdef _WIN32
+        struct _stat buffer;
+        int result = _stat(path.c_str(), &buffer);
+#else
+        struct stat buffer;
+        int result = stat(path.c_str(), &buffer);
+#endif  
+        if (result != 0) {
+            if (errno == ENOENT)
+                exists_ = false;
+            else
+                throw Rcpp::file_io_error(errno, path);
+        } else {
+            exists_ = true;
+            lastModified_ = buffer.st_mtime;
+        }
+    }
+    
+    std::string path() const { return path_; }
+    bool exists() const { return exists_; }
+    time_t lastModified() const { return lastModified_; }
+    
+private:
+    std::string path_;
+    bool exists_;
+    time_t lastModified_;
+};
+
+
+// Type info
+class Type {
+public:
+    Type() {}
+    Type(const std::string& name, bool isConst, bool isReference)
+        : name_(name), isConst_(isConst), isReference_(isReference)
+    {
+    }
+    bool empty() const { return name().empty(); }
+    
+    const std::string& name() const { return name_; }
+    
+    bool isVoid() const { return name() == "void"; }
+    bool isConst() const { return isConst_; }
+    bool isReference() const { return isReference_; }
+    
+private:
+    std::string name_;
+    bool isConst_;
+    bool isReference_;
+};
+
+std::ostream& operator<<(std::ostream& os, const Type& type) {
+    if (!type.empty()) {
+        if (type.isConst()) 
+            os << "const ";
+        os << type.name();
+        if (type.isReference())
+            os << "&";
+    }
+    return os;
+}
+  
+// Argument info
+class Argument {
+public:
+    Argument() {}
+    Argument(const std::string& name, const Type& type) 
+        : name_(name), type_(type) 
+    {
+    }
+    
+    bool empty() const { return type().empty(); }
+    
+    const std::string& name() const { return name_; }
+    const Type& type() const { return type_; }
+    
+private:
+    std::string name_;
+    Type type_;
+};
+
+std::ostream& operator<<(std::ostream& os, const Argument& argument) {
+    if (!argument.empty()) {
+        os << argument.type();
+        if (!argument.name().empty()) {
+            os << " ";
+            os << argument.name();
+        }
+    }
+    return os;
+}
+
+// Function info  
+class Function {
+public:
+    Function() {}
+    Function(const Type& type,
+             const std::string& name, 
+             const std::vector<Argument>& arguments,
+             const std::string& source)
+        : type_(type), name_(name), arguments_(arguments), source_(source)
+    {
+    }
+    
[TRUNCATED]

To get the complete diff run:
    svnlook diff /svnroot/rcpp -r 3858


More information about the Rcpp-commits mailing list