Skip to content

Commit 30efb68

Browse files
committed
Merge pull request #265 from RcppCore/feature/sourcecpp-include-local-implementation
sourceCpp: build c++ files corresponding to local shared header files
2 parents 2e17e39 + 3322aca commit 30efb68

File tree

5 files changed

+200
-33
lines changed

5 files changed

+200
-33
lines changed

ChangeLog

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
* src/attributes.cpp: Guard against includes referencing themselves
44
(and thus creating an endless loop of include processing); Process
5-
attributes in included files.
5+
attributes in included files; Automatically build implementation files
6+
(*.cc; *.cpp) corresponding to local header files if they exist.
67

78
2015-02-20 Lionel Henry <[email protected]>
89

R/Attributes.R

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,10 @@ sourceCpp <- function(file = "",
114114
"-o ", shQuote(context$dynlibFilename), " ",
115115
ifelse(rebuild, "--preclean ", ""),
116116
ifelse(dryRun, "--dry-run ", ""),
117-
shQuote(context$cppSourceFilename), sep="")
117+
paste(shQuote(context$cppDependencySourcePaths),
118+
collapse = " "), " ",
119+
shQuote(context$cppSourceFilename), " ",
120+
sep="")
118121
if (showOutput)
119122
cat(cmd, "\n")
120123

inst/NEWS.Rd

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
\item The \code{pkg_types.h} file is now included in \code{RcppExports.cpp}
2121
if it is present in either the \code{inst/include} or \code{src}.
2222
\item \code{sourceCpp} was modified to allow includes of local files
23-
(e.g. #include "foo.hpp").
23+
(e.g. #include "foo.hpp"). Implementation files (*.cc; *.cpp) corresponding
24+
to local includes are also automatically built if they exist.
2425
\item The generated attributes code was simplified with respect to
2526
\code{RNGScope} and now uses \code{RObject} and its destructor rather than \code{SEXP}
2627
protect/unprotect.

src/attributes.cpp

Lines changed: 146 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ namespace attributes {
5454
bool exists() const { return exists_; }
5555
time_t lastModified() const { return lastModified_; }
5656

57+
std::string extension() const {
58+
std::string::size_type pos = path_.find_last_of('.');
59+
if (pos != std::string::npos)
60+
return path_.substr(pos);
61+
else
62+
return "";
63+
}
64+
5765
bool operator<(const FileInfo& other) const {
5866
return path_ < other.path_;
5967
};
@@ -143,7 +151,17 @@ namespace attributes {
143151
{
144152
}
145153
bool empty() const { return name().empty(); }
146-
154+
155+
bool operator==(const Type& other) const {
156+
return name_ == other.name_ &&
157+
isConst_ == other.isConst_ &&
158+
isReference_ == other.isReference_;
159+
};
160+
161+
bool operator!=(const Type& other) const {
162+
return !(*this == other);
163+
};
164+
147165
const std::string& name() const { return name_; }
148166
std::string full_name() const {
149167
std::string res ;
@@ -175,6 +193,17 @@ namespace attributes {
175193
}
176194

177195
bool empty() const { return type().empty(); }
196+
197+
bool operator==(const Argument& other) const {
198+
return name_ == other.name_ &&
199+
type_ == other.type_ &&
200+
defaultValue_ == other.defaultValue_;
201+
};
202+
203+
bool operator!=(const Argument& other) const {
204+
return !(*this == other);
205+
};
206+
178207

179208
const std::string& name() const { return name_; }
180209
const Type& type() const { return type_; }
@@ -192,14 +221,13 @@ namespace attributes {
192221
Function() {}
193222
Function(const Type& type,
194223
const std::string& name,
195-
const std::vector<Argument>& arguments,
196-
const std::string& source)
197-
: type_(type), name_(name), arguments_(arguments), source_(source)
224+
const std::vector<Argument>& arguments)
225+
: type_(type), name_(name), arguments_(arguments)
198226
{
199227
}
200228

201229
Function renamedTo(const std::string& name) const {
202-
return Function(type(), name, arguments(), source());
230+
return Function(type(), name, arguments());
203231
}
204232

205233
std::string signature() const { return signature(name()); }
@@ -210,17 +238,25 @@ namespace attributes {
210238
}
211239

212240
bool empty() const { return name().empty(); }
241+
242+
bool operator==(const Function& other) const {
243+
return type_ == other.type_ &&
244+
name_ == other.name_ &&
245+
arguments_ == other.arguments_;
246+
};
247+
248+
bool operator!=(const Function& other) const {
249+
return !(*this == other);
250+
};
213251

214252
const Type& type() const { return type_; }
215253
const std::string& name() const { return name_; }
216254
const std::vector<Argument>& arguments() const { return arguments_; }
217-
const std::string& source() const { return source_; }
218-
255+
219256
private:
220257
Type type_;
221258
std::string name_;
222259
std::vector<Argument> arguments_;
223-
std::string source_;
224260
};
225261

226262
// Attribute parameter (with optional value)
@@ -229,6 +265,16 @@ namespace attributes {
229265
Param() {}
230266
explicit Param(const std::string& paramText);
231267
bool empty() const { return name().empty(); }
268+
269+
bool operator==(const Param& other) const {
270+
return name_ == other.name_ &&
271+
value_ == other.value_;
272+
};
273+
274+
bool operator!=(const Param& other) const {
275+
return !(*this == other);
276+
};
277+
232278

233279
const std::string& name() const { return name_; }
234280
const std::string& value() const { return value_; }
@@ -251,6 +297,18 @@ namespace attributes {
251297
}
252298

253299
bool empty() const { return name().empty(); }
300+
301+
bool operator==(const Attribute& other) const {
302+
return name_ == other.name_ &&
303+
params_ == other.params_ &&
304+
function_ == other.function_ &&
305+
roxygen_ == other.roxygen_;
306+
};
307+
308+
bool operator!=(const Attribute& other) const {
309+
return !(*this == other);
310+
};
311+
254312

255313
const std::string& name() const { return name_; }
256314

@@ -749,6 +807,9 @@ namespace attributes {
749807
Rcpp::Function filepath = baseEnv["file.path"];
750808
Rcpp::Function normalizePath = baseEnv["normalizePath"];
751809
Rcpp::Function fileExists = baseEnv["file.exists"];
810+
Rcpp::Environment toolsEnv = Rcpp::Environment::namespace_env(
811+
"tools");
812+
Rcpp::Function filePathSansExt = toolsEnv["file_path_sans_ext"];
752813

753814
// get the path to the source file's directory
754815
Rcpp::CharacterVector sourceDir = dirname(sourceFile);
@@ -789,6 +850,25 @@ namespace attributes {
789850
newDependencies.push_back(
790851
FileInfo(Rcpp::as<std::string>(include)));
791852
}
853+
854+
std::vector<std::string> exts;
855+
exts.push_back(".cc");
856+
exts.push_back(".cpp");
857+
for (size_t i = 0; i<exts.size(); ++i) {
858+
859+
// look for corresponding cpp file and add it
860+
std::string file = Rcpp::as<std::string>(
861+
filePathSansExt(include)) + exts[i];
862+
863+
exists = fileExists(file);
864+
if (exists[0]) {
865+
if (addUniqueDependency(file,
866+
pDependencies)) {
867+
FileInfo fileInfo(file);
868+
newDependencies.push_back(fileInfo);
869+
}
870+
}
871+
}
792872
}
793873
}
794874
}
@@ -804,8 +884,17 @@ namespace attributes {
804884
// parse the source dependencies from the passed lines
805885
std::vector<FileInfo> parseSourceDependencies(
806886
const std::string& sourceFile) {
887+
888+
// parse dependencies
807889
std::vector<FileInfo> dependencies;
808890
parseSourceDependencies(sourceFile, &dependencies);
891+
892+
// remove main source file
893+
dependencies.erase(std::remove(dependencies.begin(),
894+
dependencies.end(),
895+
FileInfo(sourceFile)),
896+
dependencies.end());
897+
809898
return dependencies;
810899
}
811900

@@ -912,22 +1001,32 @@ namespace attributes {
9121001
return os;
9131002
}
9141003

915-
// Argument operator <<
916-
std::ostream& operator<<(std::ostream& os, const Argument& argument) {
1004+
// Print argument
1005+
void printArgument(std::ostream& os,
1006+
const Argument& argument,
1007+
bool printDefault = true) {
9171008
if (!argument.empty()) {
9181009
os << argument.type();
9191010
if (!argument.name().empty()) {
9201011
os << " ";
9211012
os << argument.name();
922-
if (!argument.defaultValue().empty())
1013+
if (printDefault && !argument.defaultValue().empty())
9231014
os << " = " << argument.defaultValue();
9241015
}
9251016
}
1017+
}
1018+
1019+
// Argument operator <<
1020+
std::ostream& operator<<(std::ostream& os, const Argument& argument) {
1021+
printArgument(os, argument);
9261022
return os;
9271023
}
9281024

929-
// Function operator <<
930-
std::ostream& operator<<(std::ostream& os, const Function& function) {
1025+
// Print function
1026+
void printFunction(std::ostream& os,
1027+
const Function& function,
1028+
bool printArgDefaults = true) {
1029+
9311030
if (!function.empty()) {
9321031
if (!function.type().empty()) {
9331032
os << function.type();
@@ -937,12 +1036,17 @@ namespace attributes {
9371036
os << "(";
9381037
const std::vector<Argument>& arguments = function.arguments();
9391038
for (std::size_t i = 0; i<arguments.size(); i++) {
940-
os << arguments[i];
1039+
printArgument(os, arguments[i], printArgDefaults);
9411040
if (i != (arguments.size()-1))
9421041
os << ", ";
9431042
}
9441043
os << ")";
9451044
}
1045+
}
1046+
1047+
// Function operator <<
1048+
std::ostream& operator<<(std::ostream& os, const Function& function) {
1049+
printFunction(os, function);
9461050
return os;
9471051
}
9481052

@@ -1075,21 +1179,26 @@ namespace attributes {
10751179
// Recursively parse dependencies if requested
10761180
if (parseDependencies) {
10771181

1078-
// get local includes
1182+
// get source dependencies
10791183
sourceDependencies_ = parseSourceDependencies(sourceFile);
10801184

1081-
// parse attributes and modules from each local include
1185+
// parse attributes and modules from each dependent file
10821186
for (size_t i = 0; i<sourceDependencies_.size(); i++) {
10831187

10841188
// perform parse
10851189
std::string dependency = sourceDependencies_[i].path();
10861190
SourceFileAttributesParser parser(dependency, false);
10871191

1088-
// copy to base attributes
1089-
std::copy(parser.begin(),
1090-
parser.end(),
1091-
std::back_inserter(attributes_));
1092-
1192+
// copy to base attributes (if it's a new attribute)
1193+
for (SourceFileAttributesParser::const_iterator
1194+
it = parser.begin(); it != parser.end(); ++it) {
1195+
if (std::find(attributes_.begin(),
1196+
attributes_.end(),
1197+
*it) == attributes_.end()) {
1198+
attributes_.push_back(*it);
1199+
}
1200+
}
1201+
10931202
// copy to base modules
10941203
std::copy(parser.modules().begin(),
10951204
parser.modules().end(),
@@ -1339,7 +1448,7 @@ namespace attributes {
13391448
arguments.push_back(Argument(name, type, defaultValue));
13401449
}
13411450

1342-
return Function(type, name, arguments, signature);
1451+
return Function(type, name, arguments);
13431452
}
13441453

13451454

@@ -2390,7 +2499,8 @@ namespace attributes {
23902499
// include prototype if requested
23912500
if (includePrototype) {
23922501
ostr << "// " << function.name() << std::endl;
2393-
ostr << function << ";";
2502+
printFunction(ostr, function, false);
2503+
ostr << ";";
23942504
}
23952505

23962506
// write the C++ callable SEXP-based function (this version
@@ -2746,7 +2856,7 @@ namespace {
27462856
// always include Rcpp.h in case the user didn't
27472857
ostr << std::endl << std::endl;
27482858
ostr << "#include <Rcpp.h>" << std::endl;
2749-
generateCpp(ostr, sourceAttributes, false, false, contextId_);
2859+
generateCpp(ostr, sourceAttributes, true, false, contextId_);
27502860
generatedCpp_ = ostr.str();
27512861
std::ofstream cppOfs(generatedCppSourcePath().c_str(),
27522862
std::ofstream::out | std::ofstream::app);
@@ -2813,6 +2923,17 @@ namespace {
28132923
const std::string& cppSourcePath() const {
28142924
return cppSourcePath_;
28152925
}
2926+
2927+
const std::vector<std::string> cppDependencySourcePaths() {
2928+
std::vector<std::string> dependencies;
2929+
for (size_t i = 0; i<sourceDependencies_.size(); ++i) {
2930+
FileInfo dep = sourceDependencies_[i];
2931+
if (dep.extension() == ".cc" || dep.extension() == ".cpp") {
2932+
dependencies.push_back(dep.path());
2933+
}
2934+
}
2935+
return dependencies;
2936+
}
28162937

28172938
std::string buildDirectory() const {
28182939
return buildDirectory_;
@@ -3044,6 +3165,7 @@ BEGIN_RCPP
30443165
return List::create(
30453166
_["contextId"] = pDynlib->contextId(),
30463167
_["cppSourcePath"] = pDynlib->cppSourcePath(),
3168+
_["cppDependencySourcePaths"] = pDynlib->cppDependencySourcePaths(),
30473169
_["buildRequired"] = buildRequired,
30483170
_["buildDirectory"] = pDynlib->buildDirectory(),
30493171
_["generatedCpp"] = pDynlib->generatedCpp(),

0 commit comments

Comments
 (0)