[Rcpp-commits] r3900 - in pkg/Rcpp: . src

noreply at r-forge.r-project.org noreply at r-forge.r-project.org
Tue Nov 6 20:13:27 CET 2012


Author: jjallaire
Date: 2012-11-06 20:13:27 +0100 (Tue, 06 Nov 2012)
New Revision: 3900

Modified:
   pkg/Rcpp/ChangeLog
   pkg/Rcpp/src/Attributes.cpp
   pkg/Rcpp/src/AttributesParser.cpp
   pkg/Rcpp/src/AttributesParser.h
Log:
support for interfaces attribute

Modified: pkg/Rcpp/ChangeLog
===================================================================
--- pkg/Rcpp/ChangeLog	2012-11-06 15:24:58 UTC (rev 3899)
+++ pkg/Rcpp/ChangeLog	2012-11-06 19:13:27 UTC (rev 3900)
@@ -1,8 +1,10 @@
 2012-11-06  JJ Allaire <jj at rstudio.org>
 
         * R/Attributes.R: tweak whitespace in verbose mode
-        * src/Attributes.cpp: refactor code generators; use single module
-        for exports
+        * src/AttributesParser.h: support for interfaces attribute
+        * src/AttributesParser.cpp: support for interfaces attribute
+        * src/Attributes.cpp: support for interfaces attribute; refactor 
+        code generators; use single module for exports
 
 2012-11-05  Romain Francois <romain at r-enthusiasts.com>
 

Modified: pkg/Rcpp/src/Attributes.cpp
===================================================================
--- pkg/Rcpp/src/Attributes.cpp	2012-11-06 15:24:58 UTC (rev 3899)
+++ pkg/Rcpp/src/Attributes.cpp	2012-11-06 19:13:27 UTC (rev 3900)
@@ -72,7 +72,24 @@
         time_t lastModified_;
     };
     
-     // Check if the passed attribute represents an exported function
+    // Remove a file (call back into R for this)
+    void removeFile(const std::string& path) {
+        if (FileInfo(path).exists()) {
+            Rcpp::Function rm = Rcpp::Environment::base_env()["file.remove"];
+            rm(path);
+        }
+    }
+    
+    // Recursively create a directory (call back into R for this)
+    void createDirectory(const std::string& path) {
+        if (!FileInfo(path).exists()) {
+            Rcpp::Function mkdir = Rcpp::Environment::base_env()["dir.create"];
+            mkdir(path, Rcpp::Named("recursive") = true);
+        }
+    }
+    
+    
+    // Check if the passed attribute represents an exported function
     bool isExportedFunction(const Attribute& attribute) {
         return (attribute.name() == kExportAttribute) &&
                !attribute.function().empty();
@@ -158,6 +175,7 @@
         
         SourceCppDynlib(const std::string& cppSourcePath, Rcpp::List platform) 
             :  cppSourcePath_(cppSourcePath)
+               
         {
             // get cpp source file info 
             FileInfo cppSourceFilenameInfo(cppSourcePath_);
@@ -232,8 +250,8 @@
             cppOfs << std::endl;
             cppOfs << generatedCpp_;
             cppOfs.close();
-            
-            // enumerate exported functions and dependencies
+               
+            // discover exported functions, and dependencies
             exportedFunctions_.clear();
             depends_.clear();
             for (SourceFileAttributes::const_iterator 
@@ -275,7 +293,7 @@
         std::string dynlibPath() const {
             return buildDirectory_ + fileSep_ + dynlibFilename();
         }
-        
+       
         const std::vector<std::string>& exportedFunctions() const {
             return exportedFunctions_;
         }
@@ -389,16 +407,13 @@
         virtual ~ExportsGenerator() {}
         
         // Abstract interface for code generation
-        virtual void writeBegin() {}
+        virtual void writeBegin() = 0;
         virtual void writeFunctions(const SourceFileAttributes &attributes,
-                                    bool verbose) {}
-        virtual void writeEnd() {}
+                                    bool verbose) = 0;
+        virtual void writeEnd() = 0;
         
         virtual bool commit(const std::vector<std::string>& includes,
-                            const std::vector<std::string>& namespaces,
-                            const std::vector<std::string>& prototypes) {
-            return commit();                        
-        }
+                            const std::vector<std::string>& prototypes) = 0;
         
         // Allow generator to appear as a std::ostream&
         operator std::ostream&() {
@@ -453,6 +468,11 @@
             }
         }
         
+        // Remove the generated file entirely
+        void remove() {
+            removeFile(targetFile_);
+        }
+        
     private:
     
         // Check whether it's safe to overwrite this file (i.e. whether we 
@@ -512,7 +532,6 @@
         }
         
         virtual bool commit(const std::vector<std::string>& includes,
-                            const std::vector<std::string>& namespaces,
                             const std::vector<std::string>& prototypes) {
             
             // generate preamble 
@@ -523,12 +542,6 @@
                 ostr << std::endl;
             }
             
-            if (!namespaces.empty()) {
-                for (std::size_t i=0;i<namespaces.size(); i++)
-                    ostr << namespaces[i] << std::endl;
-                ostr << std::endl;
-            }
-            
             if (!prototypes.empty()) {
                 for (std::size_t i=0;i<prototypes.size(); i++)
                     ostr << prototypes[i] << ";" << std::endl;
@@ -542,6 +555,120 @@
     
     };
     
+    // Class which manages generating RcppExports.cpp
+    class CppIncludeGenerator : public ExportsGenerator {
+    public:
+        explicit CppIncludeGenerator(const std::string& packageDir, 
+                                     const std::string& fileSep,
+                                     const std::string& scope)
+            : ExportsGenerator( 
+                packageDir +  fileSep + "inst" +  fileSep + "include" +
+                fileSep + scope + ".hpp", 
+                "//")
+        {
+            scope_ = scope;
+            includeDir_ = packageDir +  fileSep + "inst" +  fileSep + "include";
+            hasCppInterface_ = false; 
+        }
+        
+        virtual void writeBegin() {
+            ostr() << "namespace " << scope_ << " {" << std::endl;
+        }
+    
+        virtual void writeFunctions(const SourceFileAttributes &attributes,
+                                    bool verbose) {
+                                        
+            // don't write anything if there is no C++ interface
+            if (!attributes.hasInterface(kInterfaceCpp)) 
+                return;
+            
+            // there is a C++ interface so set flag indicating so
+            hasCppInterface_ = true;
+                                        
+            for(std::vector<Attribute>::const_iterator 
+                it = attributes.begin(); it != attributes.end(); ++it) {
+         
+                if (isExportedFunction(*it)) {
+                    
+                    Function function = 
+                        it->function().renamedTo(exportedName(*it));
+                        
+                    // if the function starts with "." then it's a 
+                    // a hidden R-only function
+                    if (function.name().find_first_of('.') == 0)
+                        continue;  
+                    
+                    ostr() << "    inline " << function << " {" 
+                            << std::endl;
+                    
+                    ostr() << "        static " << function.type() 
+                           << "(*p_" << function.name() << ")(";
+                    
+                    const std::vector<Argument>& args = 
+                                                function.arguments();
+                    
+                    for (std::size_t i = 0; i<args.size(); i++) {
+                        ostr() << args[i].type();
+                        if (i != (args.size()-1))
+                            ostr() << ",";
+                    }
+                    
+                    ostr() << ") = Rcpp::GetCppCallable(\"RcppExports\", "
+                           << "\"" << function.name() << "\");" 
+                           << std::endl;
+                    
+                    ostr() << "        return p_" << function.name()
+                           << "(";
+                           
+                    for (std::size_t i = 0; i<args.size(); i++) {
+                        ostr() << args[i].name();
+                        if (i != (args.size()-1))
+                            ostr() << ",";
+                    }
+                           
+                    ostr() << ");" << std::endl;
+                    ostr() << "    }" << std::endl;
+                    
+                    
+                } 
+            }                           
+        }
+        
+        virtual void writeEnd() {
+            ostr() << "}" << std::endl;
+        }
+        
+        virtual bool commit(const std::vector<std::string>& includes,
+                            const std::vector<std::string>& prototypes) {
+            
+            if (hasCppInterface_) {
+                
+                // create the include dir if necessary
+                createDirectory(includeDir_);
+                
+                // generate preamble 
+                std::ostringstream ostr;
+                if (!includes.empty()) {
+                    for (std::size_t i=0;i<includes.size(); i++)
+                        ostr << includes[i] << std::endl;
+                    ostr << std::endl;
+                }
+                
+                // commit with preamble
+                return ExportsGenerator::commit(ostr.str());
+            }
+            else {
+                ExportsGenerator::remove();
+                return false;
+            }
+        }
+        
+    private:
+        std::string scope_;
+        std::string includeDir_;
+        bool hasCppInterface_;
+    };
+    
     // Class which manages generator RcppExports.R
     class RExportsGenerator : public ExportsGenerator {
     public:
@@ -553,16 +680,55 @@
         {
         }
         
+        virtual void writeBegin() {
+        }
+        
         virtual void writeFunctions(const SourceFileAttributes &attributes,
                                     bool verbose) {
-            // generate roxygen placeholders
-            generateRoxygen(ostr(), attributes);                            
+                                        
+            // add to exported functions if we have an R interface
+            if (attributes.hasInterface(kInterfaceR)) {
+                
+                // track exported functions
+                for (SourceFileAttributes::const_iterator 
+                     it = attributes.begin(); it != attributes.end(); ++it) {
+                    if (isExportedFunction(*it)) {
+                        rExports_.push_back(exportedName(*it));
+                    }
+                }
+                
+                // generate roxygen 
+                generateRoxygen(ostr(), attributes);      
+            }                      
         }
         
-        virtual void writeEnd() {   
-            ostr() << "Rcpp::loadModule(\"RcppExports\", what = TRUE)" 
-                   << std::endl;
+        virtual void writeEnd() { 
+            
+            ostr() << "Rcpp::loadModule(\"RcppExports\", ";
+            
+            if (rExports_.size() > 0) {
+                ostr() << "what = c(";
+                for (size_t i=0; i<rExports_.size(); i++) {
+                    if (i != 0)
+                        ostr() << "                                         ";
+                    ostr() << "\"" << rExports_[i] << "\"";
+                    if (i != (rExports_.size()-1))
+                        ostr() << "," << std::endl;
+                }
+                ostr() << "))" << std::endl;
+            }
+            else {
+                ostr() << "what = character())";
+            }
         }
+        
+        virtual bool commit(const std::vector<std::string>& includes,
+                            const std::vector<std::string>& prototypes) {
+            return ExportsGenerator::commit();                    
+        }
+    
+    private:
+        std::vector<std::string> rExports_;
     };
     
     // Class to manage and dispatch to a list of generators
@@ -602,13 +768,12 @@
         }
         
         bool commit(const std::vector<std::string>& includes,
-                    const std::vector<std::string>& namespaces,
                     const std::vector<std::string>& prototypes) {
             
             bool wrote = false;
             
             for(Itr it = generators_.begin(); it != generators_.end(); ++it) {
-               if ((*it)->commit(includes, namespaces, prototypes))
+               if ((*it)->commit(includes, prototypes))
                 wrote = true;
             }
                
@@ -711,7 +876,7 @@
     ExportsGenerators generators;
     generators.add(new CppExportsGenerator(packageDir, fileSep));
     generators.add(new RExportsGenerator(packageDir, fileSep));
-    std::vector<std::string> namespaces;
+    generators.add(new CppIncludeGenerator(packageDir, fileSep, packageName));
     std::vector<std::string> prototypes;
     
     // write begin
@@ -726,10 +891,7 @@
         if (attributes.empty())
             continue;
             
-        // copy namespaces and prototypes
-        std::copy(attributes.namespaces().begin(),
-                  attributes.namespaces().end(),
-                  std::back_inserter(namespaces));
+        // copy prototypes
         std::copy(attributes.prototypes().begin(),
                   attributes.prototypes().end(),
                   std::back_inserter(prototypes));
@@ -740,15 +902,9 @@
     
     // write end
     generators.writeEnd();
-            
-    // eliminate namespace duplicates
-    std::sort(namespaces.begin(), namespaces.end());
-    std::vector<std::string>::const_iterator it = 
-                        std::unique(namespaces.begin(), namespaces.end());
-    namespaces.resize( it - namespaces.begin());
-          
+
     // commit 
-    bool wrote = generators.commit(includes, namespaces, prototypes);  
+    bool wrote = generators.commit(includes, prototypes);  
                                                                                                                    
     // verbose output
     if (verbose) {

Modified: pkg/Rcpp/src/AttributesParser.cpp
===================================================================
--- pkg/Rcpp/src/AttributesParser.cpp	2012-11-06 15:24:58 UTC (rev 3899)
+++ pkg/Rcpp/src/AttributesParser.cpp	2012-11-06 19:13:27 UTC (rev 3900)
@@ -175,6 +175,9 @@
     // Known attribute names
     const char * const kExportAttribute = "export";
     const char * const kDependsAttribute = "depends";
+    const char * const kInterfacesAttribute = "interfaces";
+    const char * const kInterfaceR = "r";
+    const char * const kInterfaceCpp = "cpp";
       
     // Parse the attributes from a source file
     SourceFileAttributes::SourceFileAttributes(const std::string& sourceFile)
@@ -239,20 +242,11 @@
                 } 
                 
                 // if it's not an attribute line then it could still be a 
-                // line of interest (e.g. using namespace)
+                // line of interest (e.g. roxygen comment)
                 else {
                     
-                    // save global namespace imports (need to bring these over
-                    // to RcppExports.cpp to ensure type names all work)
-                    if (line.find("using namespace") == 0) {
-                        std::string namespaceLine = line;
-                        trimWhitespace(&namespaceLine);
-                        if (*(namespaceLine.rbegin()) == ';')
-                            namespaces_.push_back(namespaceLine);
-                    }
-                    
-                    // look for roxygen comments
-                    else if (line.find("//'") == 0) {
+                    // save roxygen comments
+                    if (line.find("//'") == 0) {
                         std::string roxLine = "#" + line.substr(2);
                         roxygenBuffer_.push_back(roxLine);
                     }
@@ -312,6 +306,24 @@
                 rcppExportWarning("No function found", lineNumber);    
         }  
         
+        // validate interfaces parameter
+        else if (name == kInterfacesAttribute) {
+            if (params.empty()) {
+                rcppInterfacesWarning("No interfaces specified", lineNumber);
+            }
+            else {
+                for (std::size_t i=0; i<params.size(); i++) {
+                    std::string param = params[i].name();
+                    if (param != kInterfaceR && param != kInterfaceCpp) {
+                        rcppInterfacesWarning(
+                            "Unknown interface '" + param + "'", lineNumber);
+                    }
+                }
+            }
+                
+           
+        }
+        
         // Return attribute
         Attribute attribute = Attribute(name, params, function, roxygenBuffer_);
         roxygenBuffer_.clear();
@@ -547,7 +559,8 @@
     
     bool SourceFileAttributes::isKnownAttribute(const std::string& name) const {
         return name == kExportAttribute || 
-               name == kDependsAttribute;
+               name == kDependsAttribute ||
+               name == kInterfacesAttribute;
     }
 
     // Print an attribute parsing related warning
@@ -586,11 +599,17 @@
     void SourceFileAttributes::rcppExportInvalidParameterWarning(
                                                     const std::string& param, 
                                                     size_t lineNumber) {
-        rcppExportWarning("Invalid parameter declaration: "
+        rcppExportWarning("Invalid parameter: "
                           "'" + param + "'", lineNumber);
     }
     
-   
+    void SourceFileAttributes::rcppInterfacesWarning(const std::string& message,
+                                                     size_t lineNumber) {
+        attributeWarning(message + " (valid interfaces are 'r' and 'cpp')", 
+                        "Rcpp::interfaces", lineNumber);                                                     
+    }
+    
+
     // Track /* */ comment state     
     void SourceFileAttributes::CommentState::submitLine(const std::string& line) {
         std::size_t pos = 0;

Modified: pkg/Rcpp/src/AttributesParser.h
===================================================================
--- pkg/Rcpp/src/AttributesParser.h	2012-11-06 15:24:58 UTC (rev 3899)
+++ pkg/Rcpp/src/AttributesParser.h	2012-11-06 19:13:27 UTC (rev 3900)
@@ -81,6 +81,10 @@
         {
         }
         
+        Function renamedTo(const std::string& name) const {
+            return Function(type(), name, arguments(), source());
+        }
+        
         bool empty() const { return name().empty(); }
         
         const Type& type() const { return type_; }
@@ -152,9 +156,12 @@
     std::ostream& operator<<(std::ostream& os, const Param& param); 
     std::ostream& operator<<(std::ostream& os, const Attribute& attribute); 
 
-    // Known attribute names
+    // Known attribute names & parameters
     extern const char * const kExportAttribute;
     extern const char * const kDependsAttribute;
+    extern const char * const kInterfacesAttribute;
+    extern const char * const kInterfaceR;
+    extern const char * const kInterfaceCpp;
    
     // Class used to parse and return attribute information from a source file
     class SourceFileAttributes {
@@ -177,9 +184,20 @@
         const_iterator end() const { return attributes_.end(); }
         bool empty() const { return attributes_.empty(); }
         
-        // namespace imports
-        const std::vector<std::string>& namespaces() const {
-            return namespaces_;
+        // Higher level queries
+        bool hasInterface(const std::string& name) const {
+            
+            for (const_iterator it=begin(); it != end(); ++it) {
+                if (it->name() == kInterfacesAttribute) {
+                    return it->hasParameter(name);
+                }
+            }
+            
+            // if there's no interfaces attrbute we default to R
+            if (name == kInterfaceR)
+                return true;
+            else
+                return false;            
         }
         
         // function prototypes
@@ -208,6 +226,8 @@
         void rcppExportNoFunctionFoundWarning(size_t lineNumber); 
         void rcppExportInvalidParameterWarning(const std::string& param, 
                                                size_t lineNumber); 
+        void rcppInterfacesWarning(const std::string& message,
+                                   size_t lineNumber);
         
         // Helper class for determining whether we are in a comment
         class CommentState {
@@ -228,7 +248,6 @@
         std::string sourceFile_;
         CharacterVector lines_;
         std::vector<Attribute> attributes_;
-        std::vector<std::string> namespaces_;
         std::vector<std::string> prototypes_;
         std::vector<std::string> roxygenBuffer_;
     };



More information about the Rcpp-commits mailing list