[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