[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