[Rcpp-commits] r3921 - in pkg/Rcpp: . R man src

noreply at r-forge.r-project.org noreply at r-forge.r-project.org
Thu Nov 8 17:20:46 CET 2012


Author: jjallaire
Date: 2012-11-08 17:20:45 +0100 (Thu, 08 Nov 2012)
New Revision: 3921

Modified:
   pkg/Rcpp/ChangeLog
   pkg/Rcpp/R/Attributes.R
   pkg/Rcpp/man/cppFunction.Rd
   pkg/Rcpp/man/evalCpp.Rd
   pkg/Rcpp/man/exportAttribute.Rd
   pkg/Rcpp/man/interfacesAttribute.Rd
   pkg/Rcpp/man/sourceCpp.Rd
   pkg/Rcpp/src/Attributes.cpp
Log:
use .h suffix; handle package/src files specially; update documentation

Modified: pkg/Rcpp/ChangeLog
===================================================================
--- pkg/Rcpp/ChangeLog	2012-11-08 12:38:47 UTC (rev 3920)
+++ pkg/Rcpp/ChangeLog	2012-11-08 16:20:45 UTC (rev 3921)
@@ -1,9 +1,18 @@
 
 2012-11-08  JJ Allaire <jj at rstudio.org>
 
+        * R/Attributes.R: special sourceCpp handling for file within the src
+        directory of a package; use .h as suffix for generated headers
         * src/Attributes.cpp: add information on arguments to Rcpp module;
-        use inline rather than static linkage for generated C++ shims
+        use inline rather than static linkage for generated C++ shims;
+        generated headers: add header guard and use .h suffix;
+        * man/cppFunction.Rd: update documentation
+        * man/sourceCpp.Rd: update documentation
+        * man/evalCpp.Rd: update documentation
+        * man/exportAttribute.Rd: update documentation
+        * man/interfacesAttribute.Rd: update documentation
 
+
 2012-11-08  Romain Francois <romain at r-enthusiasts.com>
 
         * R/Module.R: Module functions taking no arguments don't get the ellipsis

Modified: pkg/Rcpp/R/Attributes.R
===================================================================
--- pkg/Rcpp/R/Attributes.R	2012-11-08 12:38:47 UTC (rev 3920)
+++ pkg/Rcpp/R/Attributes.R	2012-11-08 16:20:45 UTC (rev 3921)
@@ -56,7 +56,7 @@
         .validatePackages(depends, context$cppSourceFilename)
         
         # temporarily modify environment for the build
-        envRestore <- .setupBuildEnvironment(depends)
+        envRestore <- .setupBuildEnvironment(depends, file)
         
         # temporarily setwd to build directory
         cwd <- getwd()
@@ -128,9 +128,12 @@
                 "force a rebuild)\n\n", sep="")
     }
     
-    # load the module
-    dll <- dyn.load(context$dynlibPath)
-    populate(Module(context$moduleName, PACKAGE = dll, mustStart = TRUE), env)
+    # load the module if we have exported functions (else there is no module)
+    if (length(context$exportedFunctions) > 0) {
+        dll <- dyn.load(context$dynlibPath)
+        populate(Module(context$moduleName, PACKAGE = dll, mustStart = TRUE), 
+                 env)
+    }
     
     # return (invisibly) a list of exported functions
     invisible(context$exportedFunctions)
@@ -197,16 +200,18 @@
 }
 
 # Evaluate a simple c++ expression
-evalCpp <- function( code, 
-                     depends = character(), 
-                     includes = character(), 
-                     showOutput = verbose, 
-                     verbose = getOption( "verbose" ) ){
+evalCpp <- function(code, 
+                    depends = character(), 
+                    includes = character(), 
+                    rebuild = FALSE,
+                    showOutput = verbose, 
+                    verbose = getOption( "verbose" ) ){
  
                          
     code <- sprintf( "SEXP get_value(){ return wrap( %s ) ; }", code )
     env <- new.env()
-    cppFunction( code, depends = depends, includes = includes, env = env, showOutput = showOutput )
+    cppFunction(code, depends = depends, includes = includes, env = env, 
+                rebuild = rebuild, showOutput = showOutput)
     fun <- env[["get_value"]]
     fun()
 }
@@ -267,11 +272,14 @@
 }
 
 
+# Add LinkingTo dependencies if the sourceFile is in a package
 .getSourceCppDependencies <- function(depends, sourceFile) {
     
-    # add package LinkingTo dependencies if the source file is in a package
-    descFile <- file.path(dirname(sourceFile), "..", "DESCRIPTION")
-    if (file.exists(descFile)) {
+    # If the source file is in a package then simulate it being built 
+    # within the package by including it's LinkingTo dependencies,
+    # the src directory (.), and the inst/include directory
+    if (.isPackageSourceFile(sourceFile)) {
+        descFile <- file.path(dirname(sourceFile), "..", "DESCRIPTION")
         DESCRIPTION <- read.dcf(descFile, all = TRUE)
         linkingTo <- .parseLinkingTo(DESCRIPTION$LinkingTo)
         unique(c(depends, linkingTo))
@@ -280,6 +288,12 @@
     }
 }
 
+
+# Check whether a source file is in a package
+.isPackageSourceFile <- function(sourceFile) {
+    file.exists(file.path(dirname(sourceFile), "..", "DESCRIPTION"))
+}
+
 # Error if a package is not currently available
 .validatePackages <- function(depends, sourceFilename) {
     unavailable <- depends[!depends %in% .packages(all.available=TRUE)]
@@ -302,7 +316,7 @@
 # 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) {
+.setupBuildEnvironment <- function(depends, sourceFile) {
     
     # discover dependencies
     buildEnv <- list()
@@ -359,6 +373,16 @@
     
     # set CLINK_CPPFLAGS based on the LinkingTo dependencies
     buildEnv$CLINK_CPPFLAGS <- .buildClinkCppFlags(linkingToPackages)
+    
+    # if the source file is in a package then add standard package
+    # include directories
+    if (.isPackageSourceFile(sourceFile)) {
+        srcDir <- dirname(sourceFile)
+        incDir <- file.path(dirname(sourceFile), "..", "inst", "include")
+        buildEnv$CLINK_CPPFLAGS <- paste(buildEnv$CLINK_CPPFLAGS, 
+                                         paste0('-I"', c(srcDir, incDir), '"'), 
+                                         collapse=" ")
+    }
 
     # add cygwin message muffler
     buildEnv$CYGWIN = "nodosfilewarning"
@@ -488,7 +512,7 @@
         # otherwise check for standard Rcpp::interfaces generated include
         else if (!pluginsOnly) {
             pkgPath <- find.package(package, NULL, quiet=TRUE)
-            pkgHeader <- paste(package, ".hpp", sep="")
+            pkgHeader <- paste(package, ".h", sep="")
             pkgHeaderPath <- file.path(pkgPath, "include",  pkgHeader)
             if (file.exists(pkgHeaderPath)) {
                 pkgInclude <- paste("#include <", pkgHeader, ">", sep="")

Modified: pkg/Rcpp/man/cppFunction.Rd
===================================================================
--- pkg/Rcpp/man/cppFunction.Rd	2012-11-08 12:38:47 UTC (rev 3920)
+++ pkg/Rcpp/man/cppFunction.Rd	2012-11-08 16:20:45 UTC (rev 3921)
@@ -50,7 +50,7 @@
 
 
 \seealso{
-\code{\link{sourceCpp}}
+\code{\link{sourceCpp}},  \code{\link{evalCpp}}
 }
 \examples{
 \dontrun{

Modified: pkg/Rcpp/man/evalCpp.Rd
===================================================================
--- pkg/Rcpp/man/evalCpp.Rd	2012-11-08 12:38:47 UTC (rev 3920)
+++ pkg/Rcpp/man/evalCpp.Rd	2012-11-08 16:20:45 UTC (rev 3921)
@@ -1,15 +1,16 @@
 \name{evalCpp}
 \alias{evalCpp}
 \title{
-Evaluate a C++ expression
+Evaluate a C++ Expression
 }
 \description{
 Evaluates a C++ expression. This creates a C++ function using 
-\code{\link{cppFunction}} and call it to get the result. 
+\code{\link{cppFunction}} and calls it to get the result. 
 }
 \usage{
 evalCpp(code, depends = character(), includes = character(), 
-    showOutput = verbose, verbose = getOption("verbose") )
+        rebuild = FALSE, showOutput = verbose, 
+        verbose = getOption("verbose"))
 }
 \arguments{
   \item{code}{
@@ -21,6 +22,9 @@
   \item{includes}{
 see \code{\link{cppFunction}}
 }
+  \item{rebuild}{
+see \code{\link{cppFunction}}
+}
   \item{showOutput}{
 see \code{\link{cppFunction}}
 }
@@ -29,13 +33,13 @@
 }
 }
 \note{
-    The result type of the C++ expression must be compatible with wrap.     
+    The result type of the C++ expression must be compatible with \code{Rcpp::wrap}.     
 }
 \value{
     The result of the evaluated C++ expression. 
 }
 \seealso{
-\code{\link{sourceCpp}} and \code{\link{cppFunction}}
+\code{\link{sourceCpp}}, \code{\link{cppFunction}}
 }
 \examples{
 \dontrun{

Modified: pkg/Rcpp/man/exportAttribute.Rd
===================================================================
--- pkg/Rcpp/man/exportAttribute.Rd	2012-11-08 12:38:47 UTC (rev 3920)
+++ pkg/Rcpp/man/exportAttribute.Rd	2012-11-08 16:20:45 UTC (rev 3921)
@@ -29,7 +29,7 @@
 }
 
 \seealso{
-\code{\link{sourceCpp}}, \code{\link{compileAttributes}}
+\code{\link{sourceCpp}} and \code{\link{compileAttributes}}
 }
 
 \examples{

Modified: pkg/Rcpp/man/interfacesAttribute.Rd
===================================================================
--- pkg/Rcpp/man/interfacesAttribute.Rd	2012-11-08 12:38:47 UTC (rev 3920)
+++ pkg/Rcpp/man/interfacesAttribute.Rd	2012-11-08 16:20:45 UTC (rev 3921)
@@ -22,7 +22,7 @@
     When \code{cpp} bindings are requested code is generated as follows:
 
 \enumerate{
-    \item Bindings are generated into a header file located in the \code{inst/include} directory of the package using the naming convention \emph{PackageName.hpp}
+    \item Bindings are generated into a header file located in the \code{inst/include} directory of the package using the naming convention \emph{PackageName.h}
     \item The generated header file allows calling the exported C++ functions without any linking dependency on the package (this is based on using the \code{R_RegisterCCallable} and \code{R_GetCCallable} functions).
     \item The exported functions are defined within a C++ namespace that matches the name of the package.
 }
@@ -31,7 +31,7 @@
 \preformatted{
    // [[Rcpp::depends(MyPackage)]]
 
-   #include <MyPackage.hpp>
+   #include <MyPackage.h>
 
    void foo() {
       MyPackage::bar();
@@ -44,7 +44,7 @@
 
 \note{
 
-    If a file by the name of \emph{PackageName.hpp} that wasn't generated by \code{compileAttributes} already exists in in the \code{inst/include} directory then it will not be overwritten (rather, an error will occur).
+    If a file by the name of \emph{PackageName.h} that wasn't generated by \code{compileAttributes} already exists in in the \code{inst/include} directory then it will not be overwritten (rather, an error will occur).
     
     A static naming scheme for generated header files is used to ensure consistent usage semantics for clients of exported \code{cpp} interfaces. Packages that wish to export more complex interfaces or additional C++ types are therefore typically better off not using this mechanism.
 

Modified: pkg/Rcpp/man/sourceCpp.Rd
===================================================================
--- pkg/Rcpp/man/sourceCpp.Rd	2012-11-08 12:38:47 UTC (rev 3920)
+++ pkg/Rcpp/man/sourceCpp.Rd	2012-11-08 16:20:45 UTC (rev 3921)
@@ -7,8 +7,9 @@
 \code{sourceCpp} parses the specified C++ file or source code and looks for functions marked with the \code{\link[=exportAttribute]{Rcpp::export}} attribute. A shared library is then built and its exported functions are made available as R functions in the specified environment.
 }
 \usage{
-sourceCpp(file = "", code = NULL, env = globalenv(), rebuild = FALSE, 
-          showOutput = verbose, verbose = getOption("verbose"))
+sourceCpp(file = "", code = NULL, env = globalenv(), 
+          rebuild = FALSE, showOutput = verbose, 
+          verbose = getOption("verbose"))
 }
 %- maybe also 'usage' for other objects documented here.
 \arguments{
@@ -36,17 +37,23 @@
 
     Functions exported using \code{sourceCpp} must meet several conditions, including being defined in the global namespace and having return types that are compatible with \code{Rcpp::wrap} and parameter types that are compatible with \code{Rcpp::as}. See the \code{\link[=exportAttribute]{Rcpp::export}} documentation for more details.
     
-    If the code has compilation dependencies on other packages (e.g. \pkg{Matrix}, \pkg{RcppArmadillo}) then an \code{\link[=dependsAttribute]{Rcpp::depends}} attribute should be added naming these dependencies. Note that if you are sourcing a C++ file from within the \code{src} directory of a package then the package's \code{LinkingTo} dependencies are automatically included in the compilation.
+    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 provided naming these dependencies. 
     
-    The \code{sourceCpp} function will not rebuild the shared library if the underlying code has not changed since the last compilation.
+    The \code{sourceCpp} function will not rebuild the shared library if the source file has not changed since the last compilation.
 }
 
 \value{
     Returns (invisibly) a character vector with the names of the R functions that were sourced into the specified environment.
 }
 
+\note{
+    The \code{sourceCpp} function is designed for compiling a standalone source file whose only dependencies are R packages. If you are compiling more than one source file or have external dependencies then you should create an R package rather than using \code{sourceCpp}. Note that the \code{\link[=exportAttribute]{Rcpp::export}} attribute can also be used within packages via the \code{\link{compileAttributes}} function.
+    
+    If you are sourcing a C++ file from within the \code{src} directory of a package then the package's \code{LinkingTo} dependencies, \code{inst/include}, and \code{src} directories are automatically included in the compilation.
+}
+
 \seealso{
-\code{\link[=exportAttribute]{Rcpp::export}}, \code{\link[=dependsAttribute]{Rcpp::depends}}
+\code{\link[=exportAttribute]{Rcpp::export}}, \code{\link[=dependsAttribute]{Rcpp::depends}}, \code{\link{cppFunction}}, \code{\link{evalCpp}}
 }
 
 \examples{

Modified: pkg/Rcpp/src/Attributes.cpp
===================================================================
--- pkg/Rcpp/src/Attributes.cpp	2012-11-08 12:38:47 UTC (rev 3920)
+++ pkg/Rcpp/src/Attributes.cpp	2012-11-08 16:20:45 UTC (rev 3921)
@@ -384,7 +384,7 @@
         }
         
         virtual bool commit(const std::vector<std::string>& includes) {
-            
+                       
             // includes 
             std::ostringstream ostr;
             if (!includes.empty()) {
@@ -440,7 +440,7 @@
                                      const std::string& fileSep)
             : ExportsGenerator( 
                 packageDir +  fileSep + "inst" +  fileSep + "include" +
-                fileSep + package + ".hpp", 
+                fileSep + package + ".h", 
                 package,
                 "//")
         {
@@ -534,7 +534,9 @@
         
         virtual void writeEnd() {
             ostr() << "}" << std::endl;
-        }
+            ostr() << std::endl;
+            ostr() << "#endif // " << getHeaderGuard() << std::endl;
+         }
         
         virtual bool commit(const std::vector<std::string>& includes) {
             
@@ -545,6 +547,13 @@
                 
                 // generate preamble 
                 std::ostringstream ostr;
+                
+                // header guard
+                std::string guard = getHeaderGuard();
+                ostr << "#ifndef " << guard << std::endl;
+                ostr << "#define " << guard << std::endl << std::endl; 
+                
+                // includes
                 if (!includes.empty()) {
                     for (std::size_t i=0;i<includes.size(); i++)
                         ostr << includes[i] << std::endl;
@@ -570,6 +579,10 @@
             return ostr.str();
         }
         
+        std::string getHeaderGuard() const {
+            return package() + "_RcppExports_h";
+        }
+        
     private:
         std::string includeDir_;
         bool hasCppInterface_;
@@ -867,11 +880,14 @@
             SourceFileAttributes sourceAttributes(cppSourcePath_);
         
             // generate RCPP module
-            std::ostringstream ostr;
-            ostr << "RCPP_MODULE(" << moduleName()  << ") {" << std::endl;
-            generateCppModuleFunctions(ostr, sourceAttributes, false);
-            ostr << "}" << std::endl;
-            generatedCpp_ = ostr.str();
+            generatedCpp_.clear();
+            if (!sourceAttributes.empty()) {
+                std::ostringstream ostr;
+                ostr << "RCPP_MODULE(" << moduleName()  << ") {" << std::endl;
+                generateCppModuleFunctions(ostr, sourceAttributes, false);
+                ostr << "}" << std::endl;
+                generatedCpp_ = ostr.str();
+            }
             
             // open source file and append module
             std::ofstream cppOfs(generatedCppSourcePath().c_str(), 
@@ -1052,6 +1068,7 @@
     
     // return context as a list
     Rcpp::List context;
+    
     context["moduleName"] = dynlib.moduleName();
     context["cppSourcePath"] = dynlib.cppSourcePath();
     context["buildRequired"] = buildRequired;



More information about the Rcpp-commits mailing list