Rcpp Version 1.0.0
attributes.cpp
Go to the documentation of this file.
1 // -*- mode: C++; c-indent-level: 4; c-basic-offset: 4; indent-tabs-mode: nil; -*-
2 //
3 // attributes.cpp: Rcpp R/C++ interface class library -- Rcpp attributes
4 //
5 // Copyright (C) 2012 - 2017 JJ Allaire, Dirk Eddelbuettel and Romain Francois
6 //
7 // This file is part of Rcpp.
8 //
9 // Rcpp is free software: you can redistribute it and/or modify it
10 // under the terms of the GNU General Public License as published by
11 // the Free Software Foundation, either version 2 of the License, or
12 // (at your option) any later version.
13 //
14 // Rcpp is distributed in the hope that it will be useful, but
15 // WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with Rcpp. If not, see <http://www.gnu.org/licenses/>.
21 
22 #define COMPILING_RCPP
23 
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <errno.h>
27 
28 #include <cstring>
29 
30 #include <string>
31 #include <vector>
32 #include <map>
33 #include <set>
34 #include <algorithm>
35 #include <fstream>
36 #include <sstream>
37 
38 #define RCPP_NO_SUGAR
39 #include <Rcpp.h>
40 
41 /*******************************************************************
42  * AttributesUtil.h
43  *******************************************************************/
44 
45 namespace Rcpp {
46 namespace attributes {
47 
48  // Utility class for getting file existence and last modified time
49  class FileInfo {
50  public:
51 
52  // create from path
53  explicit FileInfo(const std::string& path);
54 
55  // create from R list
56  explicit FileInfo(const List& fileInfo) { // #nocov start
57  path_ = as<std::string>(fileInfo["path"]);
58  exists_ = as<bool>(fileInfo["exists"]);
59  lastModified_ = as<double>(fileInfo["lastModified"]);
60  } // #nocov end
61 
62  // convert to R list
63  List toList() const {
64  List fileInfo;
65  fileInfo["path"] = path_;
66  fileInfo["exists"] = exists_;
67  fileInfo["lastModified"] = lastModified_;
68  return fileInfo;
69  }
70 
71  std::string path() const { return path_; }
72  bool exists() const { return exists_; }
73  double lastModified() const { return lastModified_; }
74 
75  std::string extension() const {
76  std::string::size_type pos = path_.find_last_of('.');
77  if (pos != std::string::npos)
78  return path_.substr(pos);
79  else
80  return ""; // #nocov
81  }
82 
83  bool operator<(const FileInfo& other) const {
84  return path_ < other.path_;
85  };
86 
87  bool operator==(const FileInfo& other) const {
88  return path_ == other.path_ &&
89  exists_ == other.exists_ &&
91  };
92 
93  bool operator!=(const FileInfo& other) const {
94  return ! (*this == other);
95  };
96 
97  std::ostream& operator<<(std::ostream& os) const {
98  os << path_;
99  return os;
100  }
101 
102  private:
103  std::string path_;
104  bool exists_;
106  };
107 
108  // Remove a file
109  bool removeFile(const std::string& path);
110 
111  // Recursively create a directory
112  void createDirectory(const std::string& path);
113 
114  // Known whitespace chars
115  extern const char * const kWhitespaceChars;
116 
117  // Query whether a character is whitespace
118  bool isWhitespace(char ch);
119 
120  // Trim a string
121  void trimWhitespace(std::string* pStr);
122 
123  // Strip trailing line comments
124  void stripTrailingLineComments(std::string* pStr);
125 
126  // Strip balanced quotes from around a string (assumes already trimmed)
127  void stripQuotes(std::string* pStr);
128 
129  // is the passed string quoted?
130  bool isQuoted(const std::string& str);
131 
132  // does a string end with another string?
133  bool endsWith(const std::string& str, const std::string& suffix);
134 
135  // show a warning message
136  void showWarning(const std::string& msg);
137 
138  // is the line a C++ roxygen comment? (started with //')
139  bool isRoxygenCpp(const std::string& str);
140 
141 } // namespace attributes
142 } // namespace Rcpp
143 
144 
145 /*******************************************************************
146  * AttributesTypes.h
147  *******************************************************************/
148 
149 namespace Rcpp {
150 namespace attributes {
151 
152  // Known attribute names & parameters
153  const char * const kExportAttribute = "export";
154  const char * const kExportName = "name";
155  const char * const kExportRng = "rng";
156  const char * const kInitAttribute = "init";
157  const char * const kDependsAttribute = "depends";
158  const char * const kPluginsAttribute = "plugins";
159  const char * const kInterfacesAttribute = "interfaces";
160  const char * const kInterfaceR = "r";
161  const char * const kInterfaceCpp = "cpp";
162  const char * const kParamValueFalse = "false";
163  const char * const kParamValueTrue = "true";
164  const char * const kParamValueFALSE = "FALSE";
165  const char * const kParamValueTRUE = "TRUE";
166 
167  // Type info
168  class Type {
169  public:
170  Type() {}
171  Type(const std::string& name, bool isConst, bool isReference)
172  : name_(name), isConst_(isConst), isReference_(isReference)
173  {
174  }
175  bool empty() const { return name().empty(); }
176 
177  bool operator==(const Type& other) const { // #nocov start
178  return name_ == other.name_ &&
179  isConst_ == other.isConst_ &&
180  isReference_ == other.isReference_;
181  }; // #nocov end
182 
183  bool operator!=(const Type& other) const {
184  return !(*this == other);
185  };
186 
187  const std::string& name() const { return name_; }
188  std::string full_name() const {
189  std::string res ;
190  if( isConst() ) res += "const " ;
191  res += name() ;
192  if( isReference() ) res += "&" ;
193  return res ;
194  }
195 
196  bool isVoid() const { return name() == "void"; }
197  bool isConst() const { return isConst_; }
198  bool isReference() const { return isReference_; }
199 
200  private:
201  std::string name_;
202  bool isConst_;
204  };
205 
206  // Argument info
207  class Argument {
208  public:
209  Argument() {}
210  Argument(const std::string& name,
211  const Type& type,
212  const std::string& defaultValue)
213  : name_(name), type_(type), defaultValue_(defaultValue)
214  {
215  }
216 
217  bool empty() const { return type().empty(); }
218 
219  bool operator==(const Argument& other) const { // #nocov start
220  return name_ == other.name_ &&
221  type_ == other.type_ &&
222  defaultValue_ == other.defaultValue_;
223  }; // #nocov end
224 
225  bool operator!=(const Argument& other) const {
226  return !(*this == other);
227  };
228 
229 
230  const std::string& name() const { return name_; }
231  const Type& type() const { return type_; }
232  const std::string& defaultValue() const { return defaultValue_; }
233 
234  private:
235  std::string name_;
237  std::string defaultValue_;
238  };
239 
240  // Function info
241  class Function {
242  public:
243  Function() {}
244  Function(const Type& type,
245  const std::string& name,
246  const std::vector<Argument>& arguments)
247  : type_(type), name_(name), arguments_(arguments)
248  {
249  }
250 
251  Function renamedTo(const std::string& name) const { // #nocov start
252  return Function(type(), name, arguments());
253  }
254 
255  std::string signature() const { return signature(name()); }
256  std::string signature(const std::string& name) const;
257 
258  bool isHidden() const {
259  return name().find_first_of('.') == 0;
260  } // #nocov end
261 
262  bool empty() const { return name().empty(); }
263 
264  bool operator==(const Function& other) const { // #nocov start
265  return type_ == other.type_ &&
266  name_ == other.name_ &&
267  arguments_ == other.arguments_;
268  }; // #nocov end
269 
270  bool operator!=(const Function& other) const {
271  return !(*this == other);
272  };
273 
274  const Type& type() const { return type_; }
275  const std::string& name() const { return name_; }
276  const std::vector<Argument>& arguments() const { return arguments_; }
277 
278  private:
280  std::string name_;
281  std::vector<Argument> arguments_;
282  };
283 
284  // Attribute parameter (with optional value)
285  class Param {
286  public:
287  Param() {}
288  explicit Param(const std::string& paramText);
289  bool empty() const { return name().empty(); }
290 
291  bool operator==(const Param& other) const { // #nocov start
292  return name_ == other.name_ &&
293  value_ == other.value_;
294  }; // #nocov end
295 
296  bool operator!=(const Param& other) const {
297  return !(*this == other);
298  };
299 
300 
301  const std::string& name() const { return name_; }
302  const std::string& value() const { return value_; } // #nocov
303 
304  private:
305  std::string name_;
306  std::string value_;
307  };
308 
309  // Attribute (w/ optional params and signature of function it qualifies)
310  class Attribute {
311  public:
313  Attribute(const std::string& name,
314  const std::vector<Param>& params,
315  const Function& function,
316  const std::vector<std::string>& roxygen)
317  : name_(name), params_(params), function_(function), roxygen_(roxygen)
318  {
319  }
320 
321  bool empty() const { return name().empty(); } // #nocov start
322 
323  bool operator==(const Attribute& other) const {
324  return name_ == other.name_ &&
325  params_ == other.params_ &&
326  function_ == other.function_ &&
327  roxygen_ == other.roxygen_;
328  }; // #nocov end
329 
330  bool operator!=(const Attribute& other) const {
331  return !(*this == other);
332  };
333 
334 
335  const std::string& name() const { return name_; }
336 
337  const std::vector<Param>& params() const { return params_; }
338 
339  Param paramNamed(const std::string& name) const;
340 
341  bool hasParameter(const std::string& name) const {
342  return !paramNamed(name).empty();
343  }
344 
345  const Function& function() const { return function_; }
346 
347  bool isExportedFunction() const {
348  return (name() == kExportAttribute) && !function().empty();
349  }
350 
351  std::string exportedName() const {
352 
353  // check for explicit name parameter
354  if (hasParameter(kExportName))
355  {
356  return paramNamed(kExportName).value(); // #nocov
357  }
358  // otherwise un-named parameter in the first slot
359  else if (!params().empty() && params()[0].value().empty())
360  {
361  return params()[0].name(); // #nocov
362  }
363  // otherwise the actual function name
364  {
365  return function().name();
366  }
367  }
368 
369  std::string exportedCppName() const { // #nocov start
370  std::string name = exportedName();
371  std::replace(name.begin(), name.end(), '.', '_');
372  return name;
373  } // #nocov end
374 
375  bool rng() const {
376  Param rngParam = paramNamed(kExportRng);
377  if (!rngParam.empty())
378  return rngParam.value() == kParamValueTrue || // #nocov
379  rngParam.value() == kParamValueTRUE; // #nocov
380  else
381  return true;
382  }
383 
384  const std::vector<std::string>& roxygen() const { return roxygen_; }
385 
386  private:
387  std::string name_;
388  std::vector<Param> params_;
390  std::vector<std::string> roxygen_;
391  };
392 
393  // Operator << for parsed types
394  std::ostream& operator<<(std::ostream& os, const Type& type);
395  std::ostream& operator<<(std::ostream& os, const Argument& argument);
396  std::ostream& operator<<(std::ostream& os, const Function& function);
397  std::ostream& operator<<(std::ostream& os, const Param& param);
398  std::ostream& operator<<(std::ostream& os, const Attribute& attribute);
399 
400  // interface to source file attributes
402  {
403  public:
404  virtual ~SourceFileAttributes() {};
405  virtual const std::string& sourceFile() const = 0;
406  virtual bool hasInterface(const std::string& name) const = 0;
407 
408  typedef std::vector<Attribute>::const_iterator const_iterator;
409  virtual const_iterator begin() const = 0;
410  virtual const_iterator end() const = 0;
411 
412  virtual const std::vector<std::string>& modules() const = 0;
413 
414  virtual const std::vector<std::vector<std::string> >& roxygenChunks() const = 0;
415 
416  virtual bool hasGeneratorOutput() const = 0;
417 
418  virtual bool hasPackageInit() const = 0;
419  };
420 
421 
422 } // namespace attributes
423 } // namespace Rcpp
424 
425 
426 
427 /*******************************************************************
428  * AttributesParser.h
429  *******************************************************************/
430 
431 namespace Rcpp {
432 namespace attributes {
433 
434  // Helper class for determining whether we are in a comment
435  class CommentState {
436  public:
437  CommentState() : inComment_(false) {}
438  private:
439  // prohibit copying
440  CommentState(const CommentState&);
441  CommentState& operator=(const CommentState&);
442  public:
443  bool inComment() const { return inComment_; }
444  void submitLine(const std::string& line);
445  void reset() { inComment_ = false; }
446  private:
448  };
449 
450  // Class used to parse and return attribute information from a source file
452  public:
453  explicit SourceFileAttributesParser(const std::string& sourceFile,
454  const std::string& packageFile,
455  bool parseDependencies);
456 
457  private:
458  // prohibit copying
461 
462  public:
463  // implemetnation of SourceFileAttributes interface
464  virtual const std::string& sourceFile() const { // #nocov
465  return sourceFile_; // #nocov
466  }
467  virtual const_iterator begin() const { return attributes_.begin(); }
468  virtual const_iterator end() const { return attributes_.end(); }
469 
470  virtual const std::vector<std::string>& modules() const
471  {
472  return modules_;
473  }
474 
475  virtual const std::vector<std::vector<std::string> >& roxygenChunks() const {
476  return roxygenChunks_;
477  }
478 
479  virtual bool hasGeneratorOutput() const
480  {
481  return !attributes_.empty() ||
482  !modules_.empty() ||
483  !roxygenChunks_.empty();
484  }
485 
486  virtual bool hasInterface(const std::string& name) const {
487 
488  for (const_iterator it=begin(); it != end(); ++it) {
489  if (it->name() == kInterfacesAttribute) {
490  return it->hasParameter(name); // #nocov
491  }
492  }
493 
494  // if there's no interfaces attrbute we default to R
495  if (name == kInterfaceR)
496  return true;
497  else
498  return false;
499  }
500 
501  // Was a package init function found?
502  bool hasPackageInit() const {
503  return hasPackageInit_;
504  }
505 
506  // Get lines of embedded R code
507  const std::vector<std::string>& embeddedR() const {
508  return embeddedR_;
509  }
510 
511  // Get source dependencies
512  const std::vector<FileInfo>& sourceDependencies() const {
513  return sourceDependencies_;
514  };
515 
516  private:
517 
518  // Parsing helpers
519  Attribute parseAttribute(const std::vector<std::string>& match,
520  int lineNumber);
521  std::vector<Param> parseParameters(const std::string& input);
522  Function parseFunction(size_t lineNumber);
523  std::string parseSignature(size_t lineNumber);
524  std::vector<std::string> parseArguments(const std::string& argText);
525  Type parseType(const std::string& text);
526 
527  // Validation helpers
528  bool isKnownAttribute(const std::string& name) const;
529  void attributeWarning(const std::string& message,
530  const std::string& attribute,
531  size_t lineNumber);
532  void attributeWarning(const std::string& message, size_t lineNumber);
533  void rcppExportWarning(const std::string& message, size_t lineNumber);
534  void rcppExportNoFunctionFoundWarning(size_t lineNumber);
535  void rcppExportInvalidParameterWarning(const std::string& param,
536  size_t lineNumber);
537  void rcppInterfacesWarning(const std::string& message,
538  size_t lineNumber);
539 
540  private:
541  std::string sourceFile_;
543  std::vector<Attribute> attributes_;
544  std::vector<std::string> modules_;
546  std::vector<std::string> embeddedR_;
547  std::vector<FileInfo> sourceDependencies_;
548  std::vector<std::vector<std::string> > roxygenChunks_;
549  std::vector<std::string> roxygenBuffer_;
550  };
551 
552 } // namespace attributes
553 } // namespace Rcpp
554 
555 
556 /*******************************************************************
557  * AttributesGen.h
558  *******************************************************************/
559 
560 namespace Rcpp {
561 namespace attributes {
562 
563  // Abstract class which manages writing of code for compileAttributes
565  protected:
566  ExportsGenerator(const std::string& targetFile,
567  const std::string& package,
568  const std::string& commentPrefix);
569 
570  private:
571  // prohibit copying
573  ExportsGenerator& operator=(const ExportsGenerator&);
574 
575  public:
576  virtual ~ExportsGenerator() {}
577 
578  // Name of target file and package
579  const std::string& targetFile() const { return targetFile_; }
580  const std::string& package() const { return package_; }
581  const std::string& packageCpp() const { return packageCpp_; }
582  const std::string packageCppPrefix() const { return "_" + packageCpp(); }
583 
584  // Abstract interface for code generation
585  virtual void writeBegin() = 0;
586  void writeFunctions(const SourceFileAttributes& attributes,
587  bool verbose); // see doWriteFunctions below
588  virtual void writeEnd(bool hasPackageInit) = 0;
589 
590  virtual bool commit(const std::vector<std::string>& includes) = 0;
591 
592  // Remove the generated file entirely
593  bool remove();
594 
595  // Allow generator to appear as a std::ostream&
596  operator std::ostream&() {
597  return codeStream_;
598  }
599 
600  protected:
601 
602  // Allow access to the output stream
603  std::ostream& ostr() {
604  return codeStream_;
605  }
606 
607  bool hasCppInterface() const {
608  return hasCppInterface_;
609  }
610 
611  // Shared knowledge about function namees
612  std::string exportValidationFunction() {
613  return "RcppExport_validate";
614  }
616  return packageCppPrefix() + "_" + exportValidationFunction();
617  }
618  std::string registerCCallableExportedName() { // #nocov
619  return packageCppPrefix() + "_RcppExport_registerCCallable"; // #nocov
620  }
621 
622  // Commit the stream -- is a no-op if the existing code is identical
623  // to the generated code. Returns true if data was written and false
624  // if it wasn't (throws exception on io error)
625  bool commit(const std::string& preamble = std::string());
626 
627  private:
628 
629  // Private virtual for doWriteFunctions so the base class
630  // can always intercept writeFunctions
631  virtual void doWriteFunctions(const SourceFileAttributes& attributes,
632  bool verbose) = 0;
633 
634  // Check whether it's safe to overwrite this file (i.e. whether we
635  // generated the file in the first place)
636  bool isSafeToOverwrite() const {
637  return existingCode_.empty() ||
638  (existingCode_.find(generatorToken()) != std::string::npos);
639  }
640 
641  // UUID that we write into a comment within the file (so that we can
642  // strongly identify that a file was generated by us before overwriting it)
643  std::string generatorToken() const {
644  return "10BE3573-1514-4C36-9D1C-5A225CD40393";
645  }
646 
647  private:
648  std::string targetFile_;
649  std::string package_;
650  std::string packageCpp_;
651  std::string commentPrefix_;
652  std::string existingCode_;
653  std::ostringstream codeStream_;
655  };
656 
657  // Class which manages generating RcppExports.cpp
659  public:
660  explicit CppExportsGenerator(const std::string& packageDir,
661  const std::string& package,
662  const std::string& fileSep);
663 
664  virtual void writeBegin() {};
665  virtual void writeEnd(bool hasPackageInit);
666  virtual bool commit(const std::vector<std::string>& includes);
667 
668  private:
669  virtual void doWriteFunctions(const SourceFileAttributes& attributes,
670  bool verbose);
671 
672  std::string registerCCallable(size_t indent,
673  const std::string& exportedName,
674  const std::string& name) const;
675 
676  private:
677  // for generating calls to init functions
678  std::vector<Attribute> initFunctions_;
679 
680  // for generating C++ interfaces
681  std::vector<Attribute> cppExports_;
682 
683  // for generating Rcpp::export native routine registration
684  std::vector<Attribute> nativeRoutines_;
685 
686  // for generating module native routine registration
687  std::vector<std::string> modules_;
688  };
689 
690  // Class which manages generating PackageName_RcppExports.h header file
692  public:
693  CppExportsIncludeGenerator(const std::string& packageDir,
694  const std::string& package,
695  const std::string& fileSep);
696 
697  virtual void writeBegin();
698  virtual void writeEnd(bool hasPackageInit);
699  virtual bool commit(const std::vector<std::string>& includes);
700 
701  private:
702  virtual void doWriteFunctions(const SourceFileAttributes& attributes,
703  bool verbose);
704  std::string getCCallable(const std::string& function) const;
705  std::string getHeaderGuard() const;
706 
707  private:
708  std::string includeDir_;
709  };
710 
711  // Class which manages generating PackageName_RcppExports.h header file
713  public:
714  CppPackageIncludeGenerator(const std::string& packageDir,
715  const std::string& package,
716  const std::string& fileSep);
717 
718  virtual void writeBegin() {}
719  virtual void writeEnd(bool hasPackageInit);
720  virtual bool commit(const std::vector<std::string>& includes);
721 
722  private:
723  virtual void doWriteFunctions(const SourceFileAttributes&, bool) {}
724  std::string getHeaderGuard() const;
725 
726  private:
727  std::string includeDir_;
728  };
729 
730 
731  // Class which manages generator RcppExports.R
733  public:
734  RExportsGenerator(const std::string& packageDir,
735  const std::string& package,
736  bool registration,
737  const std::string& fileSep);
738 
739  virtual void writeBegin() {}
740  virtual void writeEnd(bool hasPackageInit);
741  virtual bool commit(const std::vector<std::string>& includes);
742 
743  private:
744  virtual void doWriteFunctions(const SourceFileAttributes& attributes,
745  bool verbose);
746 
748 
749  };
750 
751  // Class to manage and dispatch to a list of generators
753  public:
754  typedef std::vector<ExportsGenerator*>::iterator Itr;
755 
757  virtual ~ExportsGenerators();
758 
759  void add(ExportsGenerator* pGenerator);
760 
761  void writeBegin();
762  void writeFunctions(const SourceFileAttributes& attributes,
763  bool verbose);
764  void writeEnd(bool hasPackageInit);
765 
766  // Commit and return a list of the files that were updated
767  std::vector<std::string> commit(
768  const std::vector<std::string>& includes);
769 
770  // Remove and return a list of files that were removed
771  std::vector<std::string> remove();
772 
773  private:
774  // prohibit copying
776  ExportsGenerators& operator=(const ExportsGenerators&);
777 
778  private:
779  std::vector<ExportsGenerator*> generators_;
780  };
781 
782  // Standalone generation helpers (used by sourceCpp)
783 
784  std::string generateRArgList(const Function& function);
785 
786  void generateCpp(std::ostream& ostr,
787  const SourceFileAttributes& attributes,
788  bool includePrototype,
789  bool cppInterface,
790  const std::string& contextId);
791 
792 } // namespace attributes
793 } // namespace Rcpp
794 
795 
796 /*******************************************************************
797  * AttributesParser.cpp
798  *******************************************************************/
799 
800 namespace Rcpp {
801 namespace attributes {
802 
803  namespace {
804 
805  Rcpp::List regexMatches(Rcpp::CharacterVector lines,
806  const std::string& regex)
807  {
808  Rcpp::Environment base("package:base");
809  Rcpp::Function regexec = base["regexec"];
810  Rcpp::Function regmatches = base["regmatches"];
811  Rcpp::RObject result = regexec(regex, lines);
812  Rcpp::List matches = regmatches(lines, result);
813  return matches;
814  }
815 
816  template <typename Stream>
817  void readFile(const std::string& file, Stream& os) {
818  std::ifstream ifs(file.c_str());
819  if (ifs.fail())
820  throw Rcpp::file_io_error(file); // #nocov
821  os << ifs.rdbuf();
822  ifs.close();
823  }
824 
825  template <typename Collection>
826  void readLines(std::istream& is, Collection* pLines) {
827  pLines->clear();
828  std::string line;
829  while(std::getline(is, line)) {
830  // strip \r (for the case of windows line terminators on posix)
831  if (line.length() > 0 && *line.rbegin() == '\r')
832  line.erase(line.length()-1, 1);
834  pLines->push_back(line);
835  }
836  }
837 
838  bool addUniqueDependency(Rcpp::CharacterVector include,
839  std::vector<FileInfo>* pDependencies) {
840 
841  // return false if we already have this include
842  std::string path = Rcpp::as<std::string>(include);
843  for (size_t i = 0; i<pDependencies->size(); ++i) {
844  if (pDependencies->at(i).path() == path)
845  return false;
846  }
847 
848  // add it and return true
849  pDependencies->push_back(FileInfo(path));
850  return true;
851  }
852 
853  void parseSourceDependencies(const std::string& sourceFile,
854  std::vector<FileInfo>* pDependencies) {
855 
856  // import R functions
858  Rcpp::Function dirname = baseEnv["dirname"];
859  Rcpp::Function filepath = baseEnv["file.path"];
860  Rcpp::Function normalizePath = baseEnv["normalizePath"];
861  Rcpp::Function fileExists = baseEnv["file.exists"];
863  "tools");
864  Rcpp::Function filePathSansExt = toolsEnv["file_path_sans_ext"];
865 
866  // get the path to the source file's directory
867  Rcpp::CharacterVector sourceDir = dirname(sourceFile);
868 
869  // read the source file into a buffer
870  std::stringstream buffer;
871  readFile(sourceFile, buffer);
872 
873  // Now read into a list of strings (which we can pass to regexec)
874  // First read into a std::deque (which will handle lots of append
875  // operations efficiently) then copy into an R chracter vector
876  std::deque<std::string> lines;
877  readLines(buffer, &lines);
878  Rcpp::CharacterVector linesVector = Rcpp::wrap(lines);
879 
880  // look for local includes
881  Rcpp::List matches = regexMatches(
882  linesVector, "^\\s*#include\\s*\"([^\"]+)\"\\s*$");
883 
884  // accumulate local includes (skip commented sections)
885  CommentState commentState;
886  std::vector<FileInfo> newDependencies;
887  for (int i = 0; i<matches.size(); i++) {
888  std::string line = lines[i];
889  commentState.submitLine(line);
890  if (!commentState.inComment()) {
891  // get the match
892  const Rcpp::CharacterVector match = matches[i];
893  if (match.size() == 2) {
894  // compose a full file path for the match
895  Rcpp::CharacterVector include =
896  filepath(sourceDir, std::string(match[1]));
897  // if it exists then normalize and add to our list
898  LogicalVector exists = fileExists(include);
899  if (exists[0]) {
900  include = normalizePath(include, "/");
901  if (addUniqueDependency(include, pDependencies)) {
902  newDependencies.push_back(
903  FileInfo(Rcpp::as<std::string>(include)));
904  }
905 
906  std::vector<std::string> exts;
907  exts.push_back(".cc");
908  exts.push_back(".cpp");
909  for (size_t i = 0; i<exts.size(); ++i) {
910 
911  // look for corresponding cpp file and add it
912  std::string file = Rcpp::as<std::string>(
913  filePathSansExt(include)) + exts[i];
914 
915  exists = fileExists(file);
916  if (exists[0]) {
917  if (addUniqueDependency(file,
918  pDependencies)) {
919  FileInfo fileInfo(file);
920  newDependencies.push_back(fileInfo);
921  }
922  }
923  }
924  }
925  }
926  }
927  }
928 
929  // look for dependencies recursively
930  for (size_t i = 0; i<newDependencies.size(); i++) {
931  FileInfo dependency = newDependencies[i];
932  parseSourceDependencies(dependency.path(), pDependencies);
933  }
934  }
935 
936  // parse the source dependencies from the passed lines
937  std::vector<FileInfo> parseSourceDependencies(
938  std::string sourceFile) {
939 
940  // normalize source file
942  Rcpp::Function normalizePath = baseEnv["normalizePath"];
943  sourceFile = Rcpp::as<std::string>(normalizePath(sourceFile, "/"));
944 
945  // parse dependencies
946  std::vector<FileInfo> dependencies;
947  parseSourceDependencies(sourceFile, &dependencies);
948 
949  // remove main source file
950  dependencies.erase(std::remove(dependencies.begin(),
951  dependencies.end(),
952  FileInfo(sourceFile)),
953  dependencies.end());
954 
955  return dependencies;
956  }
957 
958  // Parse embedded R code chunks from a file (receives the lines of the
959  // file as a CharcterVector for using with regexec and as a standard
960  // stl vector for traversal/insepection)
961  std::vector<std::string> parseEmbeddedR(
962  Rcpp::CharacterVector linesVector,
963  const std::deque<std::string>& lines) {
964  Rcpp::List matches = regexMatches(linesVector,
965  "^\\s*/\\*{3,}\\s*[Rr]\\s*$");
966  bool withinRBlock = false;
967  CommentState commentState;
968  std::vector<std::string> embeddedR;
969 
970  for (int i = 0; i<matches.size(); i++) {
971 
972  // track comment state
973  std::string line = lines[i];
974  commentState.submitLine(line);
975 
976  // is this a line that begins an R code block?
977  const Rcpp::CharacterVector match = matches[i];
978  bool beginRBlock = match.size() > 0;
979 
980  // check state and do the right thing
981  if (beginRBlock) {
982  withinRBlock = true; // #nocov
983  }
984  else if (withinRBlock) {
985  if (commentState.inComment()) // #nocov start
986  embeddedR.push_back(line);
987  else
988  withinRBlock = false; // #nocov end
989  }
990  }
991 
992  return embeddedR;
993  }
994 
995  } // anonymous namespace
996 
997 
998  // Generate a type signature for the function with the provided name
999  // (type signature == function pointer declaration)
1000  std::string Function::signature(const std::string& name) const { // #nocov start
1001 
1002  std::ostringstream ostr;
1003 
1004  ostr << type() << "(*" << name << ")(";
1005 
1006  const std::vector<Argument>& args = arguments();
1007  for (std::size_t i = 0; i<args.size(); i++) {
1008  ostr << args[i].type();
1009  if (i != (args.size()-1))
1010  ostr << ",";
1011  }
1012  ostr << ")";
1013 
1014  return ostr.str(); // #nocov end
1015  }
1016 
1017 
1018  // Parse attribute parameter from parameter text
1019  Param::Param(const std::string& paramText)
1020  {
1021  // parse out name/value pair if there is one
1022  std::string::size_type pos = paramText.find("=") ;
1023  if ( pos != std::string::npos ) {
1024  // name
1025  name_ = paramText.substr(0, pos); // #nocov start
1026  trimWhitespace(&name_);
1027  // value
1028  value_ = paramText.substr(pos + 1) ;
1029  trimWhitespace(&value_);
1030  stripQuotes(&value_); // #nocov end
1031  }
1032  else {
1033  name_ = paramText;
1034  trimWhitespace(&name_);
1035  stripQuotes(&name_);
1036  }
1037  }
1038 
1039  // Check if the attribute has a parameter of a paricular name
1040  Param Attribute::paramNamed(const std::string& name) const {
1041  for (std::vector<Param>::const_iterator
1042  it = params_.begin(); it != params_.end(); ++it) {
1043  if (it->name() == name) // #nocov
1044  return *it; // #nocov
1045  }
1046  return Param();
1047  }
1048 
1049  // Type operator <<
1050  std::ostream& operator<<(std::ostream& os, const Type& type) {
1051  if (!type.empty()) {
1052  if (type.isConst())
1053  os << "const ";
1054  os << type.name();
1055  if (type.isReference())
1056  os << "&";
1057  }
1058  return os;
1059  }
1060 
1061  // Print argument
1062  void printArgument(std::ostream& os,
1063  const Argument& argument,
1064  bool printDefault = true) {
1065  if (!argument.empty()) {
1066  os << argument.type();
1067  if (!argument.name().empty()) {
1068  os << " ";
1069  os << argument.name();
1070  if (printDefault && !argument.defaultValue().empty())
1071  os << " = " << argument.defaultValue(); // #nocov
1072  }
1073  }
1074  }
1075 
1076  // Argument operator <<
1077  std::ostream& operator<<(std::ostream& os, const Argument& argument) {// #nocov start
1078  printArgument(os, argument);
1079  return os; // #nocov end
1080  }
1081 
1082  // Print function
1083  void printFunction(std::ostream& os,
1084  const Function& function,
1085  bool printArgDefaults = true) {
1086 
1087  if (!function.empty()) {
1088  if (!function.type().empty()) {
1089  os << function.type();
1090  os << " ";
1091  }
1092  os << function.name();
1093  os << "(";
1094  const std::vector<Argument>& arguments = function.arguments();
1095  for (std::size_t i = 0; i<arguments.size(); i++) {
1096  printArgument(os, arguments[i], printArgDefaults);
1097  if (i != (arguments.size()-1))
1098  os << ", ";
1099  }
1100  os << ")";
1101  }
1102  }
1103 
1104  // Function operator <<
1105  std::ostream& operator<<(std::ostream& os, const Function& function) {// #nocov start
1106  printFunction(os, function);
1107  return os;
1108  }
1109 
1110  // Param operator <<
1111  std::ostream& operator<<(std::ostream& os, const Param& param) {
1112  if (!param.empty()) {
1113  os << param.name();
1114  if (!param.value().empty())
1115  os << "=" << param.value();
1116  }
1117  return os;
1118  }
1119 
1120  // Attribute operator <<
1121  std::ostream& operator<<(std::ostream& os, const Attribute& attribute) {
1122  if (!attribute.empty()) {
1123  os << "[[Rcpp::" << attribute.name();
1124  const std::vector<Param>& params = attribute.params();
1125  if (params.size() > 0) {
1126  os << "(";
1127  for (std::size_t i = 0; i<params.size(); i++) {
1128  os << params[i];
1129  if (i != (params.size()-1))
1130  os << ",";
1131  }
1132  os << ")";
1133  }
1134  os << "]]";
1135 
1136  if (!attribute.function().empty())
1137  os << " " << attribute.function();
1138  }
1139  return os; // #nocov end
1140  }
1141 
1142  // Parse the attributes from a source file
1144  const std::string& sourceFile,
1145  const std::string& packageName,
1146  bool parseDependencies)
1147  : sourceFile_(sourceFile), hasPackageInit_(false)
1148  {
1149 
1150  // transform packageName to valid C++ symbol
1151  std::string packageNameCpp = packageName;
1152  std::replace(packageNameCpp.begin(), packageNameCpp.end(), '.', '_');
1153 
1154  // First read the entire file into a std::stringstream so we can check
1155  // it for attributes (we don't want to do any of our more expensive
1156  // processing steps if there are no attributes to parse)
1157  std::stringstream buffer;
1158  readFile(sourceFile_, buffer);
1159  std::string contents = buffer.str();
1160 
1161  // Check for attribute signature
1162  if (contents.find("[[Rcpp::") != std::string::npos ||
1163  contents.find("RCPP_MODULE") != std::string::npos ||
1164  contents.find("R_init_" + packageNameCpp) != std::string::npos) {
1165 
1166  // Now read into a list of strings (which we can pass to regexec)
1167  // First read into a std::deque (which will handle lots of append
1168  // operations efficiently) then copy into an R chracter vector
1169  std::deque<std::string> lines;
1170  readLines(buffer, &lines);
1171  lines_ = Rcpp::wrap(lines);
1172 
1173  // Scan for attributes
1174  CommentState commentState;
1175  Rcpp::List matches = regexMatches(lines_,
1176  "^\\s*//\\s*\\[\\[Rcpp::(\\w+)(\\(.*?\\))?\\]\\]\\s*$");
1177  for (int i = 0; i<matches.size(); i++) {
1178 
1179  // track whether we are in a comment and bail if we are in one
1180  std::string line = lines[i];
1181  commentState.submitLine(line);
1182  if (commentState.inComment())
1183  continue;
1184 
1185  // attribute line
1186  const Rcpp::CharacterVector match = matches[i];
1187  if (match.size() > 0) {
1188 
1189  // if the match size isn't 3 then regmatches has not behaved
1190  // as expected (it should return a vector of either 0 or 3
1191  // elements). we don't ever expect this to occur but if it
1192  // does let's not crash
1193  if (match.size() != 3)
1194  continue; // #nocov
1195 
1196  // add the attribute
1197  Attribute attr = parseAttribute(
1198  Rcpp::as<std::vector<std::string> >(match), i);
1199  attributes_.push_back(attr);
1200  }
1201 
1202  // if it's not an attribute line then it could still be a
1203  // line of interest (e.g. roxygen comment)
1204  else {
1205 
1206  // save roxygen comments
1207  if (line.find("//'") == 0) {
1208  std::string roxLine = "#" + line.substr(2);
1209  roxygenBuffer_.push_back(roxLine);
1210  }
1211 
1212  // a non-roxygen line causes us to clear the roxygen buffer
1213  else if (!roxygenBuffer_.empty()) {
1214  roxygenChunks_.push_back(roxygenBuffer_); // #nocov
1215  roxygenBuffer_.clear(); // #nocov
1216  }
1217  }
1218  }
1219 
1220  // Scan for Rcpp modules
1221  commentState.reset();
1222  Rcpp::List modMatches = regexMatches(lines_,
1223  "^\\s*RCPP_MODULE\\s*\\(\\s*(\\w+)\\s*\\).*$");
1224  for (int i = 0; i<modMatches.size(); i++) {
1225 
1226  // track whether we are in a comment and bail if we are in one
1227  std::string line = lines[i];
1228  commentState.submitLine(line);
1229  if (commentState.inComment())
1230  continue;
1231 
1232  // get the module declaration
1233  Rcpp::CharacterVector match = modMatches[i];
1234  if (match.size() > 0) {
1235  const char * name = match[1];
1236  modules_.push_back(name);
1237  }
1238  }
1239 
1240  // Scan for package init function
1241  hasPackageInit_ = false;
1242  commentState.reset();
1243  std::string pkgInit = "R_init_" + packageNameCpp;
1244  Rcpp::List initMatches = regexMatches(lines_, "^[^/]+" + pkgInit + ".*DllInfo.*$");
1245  for (int i = 0; i<initMatches.size(); i++) {
1246 
1247  // track whether we are in a comment and bail if we are in one
1248  std::string line = lines[i];
1249  commentState.submitLine(line);
1250  if (commentState.inComment())
1251  continue;
1252 
1253  // check for a match
1254  Rcpp::CharacterVector match = initMatches[i];
1255  if (match.size() > 0) {
1256  hasPackageInit_ = true;
1257  break;
1258  }
1259  }
1260 
1261  // Parse embedded R
1262  embeddedR_ = parseEmbeddedR(lines_, lines);
1263 
1264  // Recursively parse dependencies if requested
1265  if (parseDependencies) {
1266 
1267  // get source dependencies
1268  sourceDependencies_ = parseSourceDependencies(sourceFile);
1269 
1270  // parse attributes and modules from each dependent file
1271  for (size_t i = 0; i<sourceDependencies_.size(); i++) {
1272 
1273  // perform parse
1274  std::string dependency = sourceDependencies_[i].path();
1275  SourceFileAttributesParser parser(dependency, packageName, false);
1276 
1277  // copy to base attributes (if it's a new attribute)
1279  it = parser.begin(); it != parser.end(); ++it) {
1280  if (std::find(attributes_.begin(), // #nocov start
1281  attributes_.end(),
1282  *it) == attributes_.end()) {
1283  attributes_.push_back(*it); // #nocov end
1284  }
1285  }
1286 
1287  // copy to base modules
1288  std::copy(parser.modules().begin(),
1289  parser.modules().end(),
1290  std::back_inserter(modules_));
1291  }
1292  }
1293  }
1294  }
1295 
1296  // Parse an attribute from the vector returned by regmatches
1298  const std::vector<std::string>& match,
1299  int lineNumber) {
1300 
1301  // Attribute name
1302  std::string name = match[1];
1303 
1304  // Warn if this is an unknown attribute
1305  if (!isKnownAttribute(name)) {
1306  attributeWarning("Unrecognized attribute Rcpp::" + name, // #nocov
1307  lineNumber); // #nocov
1308  }
1309 
1310  // Extract params if we've got them
1311  std::vector<Param> params;
1312  std::string paramsText = match[2];
1313  if (!paramsText.empty()) {
1314 
1315  // we know from the regex that it's enclosed in parens so remove
1316  // trim before we do this just in case someone updates the regex
1317  // to allow for whitespace around the call
1318  trimWhitespace(&paramsText);
1319 
1320  paramsText = paramsText.substr(1, paramsText.size()-2);
1321 
1322  // parse the parameters
1323  params = parseParameters(paramsText);
1324  }
1325 
1326  // Extract function signature if this is a function attribute
1327  // and it doesn't appear at the end of the file
1328  Function function;
1329 
1330  // special handling for export and init
1331  if (name == kExportAttribute || name == kInitAttribute) {
1332 
1333  // parse the function (unless we are at the end of the file in
1334  // which case we print a warning)
1335  if ((lineNumber + 1) < lines_.size())
1336  function = parseFunction(lineNumber + 1);
1337  else
1338  rcppExportWarning("No function found", lineNumber); // #nocov
1339 
1340  // validate parameters
1341  for (std::size_t i=0; i<params.size(); i++) {
1342 
1343  std::string name = params[i].name(); // #nocov start
1344  std::string value = params[i].value();
1345 
1346  // un-named parameter that isn't the first parameter
1347  if (value.empty() && (i > 0)) {
1348  rcppExportWarning("No value specified for parameter '" +
1349  name + "'",
1350  lineNumber);
1351  }
1352  // parameter that isn't name or rng
1353  else if (!value.empty() &&
1354  (name != kExportName) &&
1355  (name != kExportRng)) {
1356  rcppExportWarning("Unrecognized parameter '" + name + "'",
1357  lineNumber);
1358  }
1359  // rng that isn't true or false
1360  else if (name == kExportRng) {
1361  if (value != kParamValueFalse &&
1362  value != kParamValueTrue &&
1363  value != kParamValueFALSE &&
1364  value != kParamValueTRUE) {
1365  rcppExportWarning("rng value must be true or false",
1366  lineNumber); // #nocov end
1367  }
1368  }
1369  }
1370  }
1371 
1372  // validate interfaces parameter
1373  else if (name == kInterfacesAttribute) {
1374  if (params.empty()) { // #nocov start
1375  rcppInterfacesWarning("No interfaces specified", lineNumber);//
1376  }
1377  else {
1378  for (std::size_t i=0; i<params.size(); i++) {
1379  std::string param = params[i].name();
1380  if (param != kInterfaceR && param != kInterfaceCpp) {
1382  "Unknown interface '" + param + "'", lineNumber);
1383  } // #nocov end
1384  }
1385  }
1386 
1387 
1388  }
1389 
1390  // Return attribute
1391  Attribute attribute = Attribute(name, params, function, roxygenBuffer_);
1392  roxygenBuffer_.clear();
1393  return attribute;
1394  }
1395 
1396  // Parse attribute parameters
1398  const std::string& input) {
1399 
1400  const std::string delimiters(",");
1401 
1402  std::vector<Param> params;
1403  std::string::size_type current;
1404  std::string::size_type next = -1;
1405  do { // #nocov
1406  next = input.find_first_not_of(delimiters, next + 1);
1407  if (next == std::string::npos)
1408  break; // #nocov
1409  next -= 1;
1410  current = next + 1;
1411  next = input.find_first_of(delimiters, current);
1412  params.push_back(Param(input.substr(current, next - current)));
1413  } while(next != std::string::npos);
1414 
1415  return params;
1416  }
1417 
1418  // Parse a function from the specified spot in the source file
1420 
1421  // Establish the text to parse for the signature
1422  std::string signature = parseSignature(lineNumber);
1423  if (signature.empty()) {
1424  rcppExportNoFunctionFoundWarning(lineNumber); // #nocov
1425  return Function(); // #nocov
1426  }
1427 
1428  // Start at the end and look for the () that deliniates the arguments
1429  // (bail with an empty result if we can't find them)
1430  std::string::size_type endParenLoc = signature.find_last_of(')');
1431  std::string::size_type beginParenLoc = signature.find_first_of('(');
1432  if (endParenLoc == std::string::npos ||
1433  beginParenLoc == std::string::npos ||
1434  endParenLoc < beginParenLoc) {
1435 
1437  return Function();
1438  }
1439 
1440  // Find the type and name by scanning backwards for the whitespace that
1441  // delimites the type and name
1442  Type type;
1443  std::string name;
1444  const std::string preambleText = signature.substr(0, beginParenLoc);
1445  for (std::string::const_reverse_iterator
1446  it = preambleText.rbegin(); it != preambleText.rend(); ++it) {
1447  char ch = *it;
1448  if (isWhitespace(ch)) {
1449  if (!name.empty()) {
1450  // we are at the break between type and name so we can also
1451  // extract the type
1452  std::string typeText;
1453  while (++it != preambleText.rend())
1454  typeText.insert(0U, 1U, *it);
1455  type = parseType(typeText);
1456 
1457  // break (since we now have the name and the type)
1458  break;
1459  }
1460  else
1461  continue; // #nocov
1462  } else {
1463  name.insert(0U, 1U, ch);
1464  }
1465  }
1466 
1467  // If we didn't find a name then bail
1468  if (name.empty()) {
1469  rcppExportNoFunctionFoundWarning(lineNumber); // #nocov
1470  return Function(); // #nocov
1471  }
1472 
1473  // If we didn't find a type then bail
1474  if (type.empty()) { // #nocov start
1475  rcppExportWarning("No function return type found", lineNumber);
1476  return Function(); // #nocov end
1477  }
1478 
1479  // Now scan for arguments
1480  std::vector<Argument> arguments;
1481  std::string argsText = signature.substr(beginParenLoc + 1,
1482  endParenLoc-beginParenLoc-1);
1483  std::vector<std::string> args = parseArguments(argsText);
1484  for (std::vector<std::string>::const_iterator it =
1485  args.begin(); it != args.end(); ++it) {
1486 
1487  // Get argument sans whitespace (bail if the arg is empty)
1488  std::string arg = *it;
1489  trimWhitespace(&arg);
1490  if (arg.empty()) {
1491  // we don't warn here because the compilation will fail anyway
1492  continue;
1493  }
1494 
1495  // If the argument has an = within it then it has a default value
1496  std::string defaultValue;
1497  std::string::size_type eqPos = arg.find_first_of('=');
1498  if ( (eqPos != std::string::npos) && ((eqPos + 1) < arg.size()) ) {
1499  defaultValue = arg.substr(eqPos+1);
1500  trimWhitespace(&defaultValue);
1501  arg = arg.substr(0, eqPos);
1502  trimWhitespace(&arg);
1503  }
1504 
1505  // Scan backwards for whitespace to determine where the type ends
1506  // (we go backwards because whitespace is valid inside the type
1507  // identifier but invalid inside the variable name). Note that if
1508  // there is no whitespace we'll end up taking the whole string,
1509  // which allows us to capture a type with no variable (but note
1510  // we'll ultimately fail to parse types with no variable if they
1511  // have embedded whitespace)
1512  std::string::size_type pos = arg.find_last_of(kWhitespaceChars);
1513 
1514  // check for name
1515  std::string name;
1516  if (pos != std::string::npos) {
1517  // insert whitespace if variables are joint with '&'
1518  std::string::size_type ref_pos = arg.substr(pos).find_last_of("&");
1519  if (ref_pos != std::string::npos) {
1520  pos += ref_pos + 1; // #nocov
1521  arg.insert(pos, " "); // #nocov
1522  }
1523 
1524  name = arg.substr(pos);
1525  trimWhitespace(&name);
1526  }
1527  if (name.empty()) { // #nocov start
1528  rcppExportInvalidParameterWarning(arg, lineNumber);
1529  return Function(); // #nocov end
1530  }
1531 
1532  // check for type string
1533  Type type = parseType(arg.substr(0, pos));
1534  if (type.empty()) { // #nocov start
1535  rcppExportInvalidParameterWarning(arg, lineNumber);
1536  return Function(); // #nocov end
1537  }
1538 
1539  // add argument
1540  arguments.push_back(Argument(name, type, defaultValue));
1541  }
1542 
1543  return Function(type, name, arguments);
1544  }
1545 
1546 
1547  // Parse the text of a function signature from the specified line
1548  std::string SourceFileAttributesParser::parseSignature(size_t lineNumber) {
1549 
1550  // Look for the signature termination ({ or ; not inside quotes)
1551  // on this line and then subsequent lines if necessary
1552  std::string signature;
1553  for (int i = lineNumber; i<lines_.size(); i++) {
1554  std::string line;
1555  line = lines_[i];
1556  bool insideQuotes = false;
1557  char prevChar = 0;
1558  // scan for { or ; not inside quotes
1559  for (size_t c = 0; c < line.length(); ++c) {
1560  // alias character
1561  char ch = line.at(c);
1562  // update quotes state
1563  if (ch == '"' && prevChar != '\\')
1564  insideQuotes = !insideQuotes;
1565  // found signature termination, append and return
1566  if (!insideQuotes && ((ch == '{') || (ch == ';'))) {
1567  signature.append(line.substr(0, c));
1568  return signature;
1569  }
1570  // record prev char (used to check for escaped quote i.e. \")
1571  prevChar = ch;
1572  }
1573 
1574  // if we didn't find a terminator on this line then just append the line
1575  // and move on to the next line
1576  signature.append(line);
1577  signature.push_back(' ');
1578  }
1579 
1580  // Not found
1581  return std::string(); // #nocov
1582  }
1583 
1584 
1585  // Parse arguments from function signature. This is tricky because commas
1586  // are used to delimit arguments but are also valid inside template type
1587  // qualifiers.
1589  const std::string& argText) {
1590 
1591  int templateCount = 0;
1592  int parenCount = 0;
1593  bool insideQuotes = false;
1594  std::string currentArg;
1595  std::vector<std::string> args;
1596  char prevChar = 0;
1597  for (std::string::const_iterator
1598  it = argText.begin(); it != argText.end(); ++it) {
1599  char ch = *it;
1600 
1601  if (ch == '"' && prevChar != '\\') {
1602  insideQuotes = !insideQuotes;
1603  }
1604 
1605  if ((ch == ',') &&
1606  (templateCount == 0) &&
1607  (parenCount == 0) &&
1608  !insideQuotes) {
1609  args.push_back(currentArg);
1610  currentArg.clear();
1611  continue;
1612  } else {
1613  currentArg.push_back(ch);
1614  switch(ch) {
1615  case '<':
1616  templateCount++;
1617  break;
1618  case '>':
1619  templateCount--;
1620  break;
1621  case '(':
1622  parenCount++; // #nocov
1623  break; // #nocov
1624  case ')':
1625  parenCount--; // #nocov
1626  break; // #nocov
1627  }
1628  }
1629 
1630  prevChar = ch;
1631  }
1632 
1633  if (!currentArg.empty())
1634  args.push_back(currentArg);
1635 
1636  return args;
1637  }
1638 
1639  Type SourceFileAttributesParser::parseType(const std::string& text) {
1640 
1641  const std::string constQualifier("const");
1642  const std::string referenceQualifier("&");
1643 
1644  // trim whitespace
1645  std::string type = text;
1646  trimWhitespace(&type);
1647 
1648  // check for const and reference
1649  bool isConst = false;
1650  bool isReference = false;
1651  if (type.find(constQualifier) == 0) {
1652  isConst = true;
1653  type.erase(0, constQualifier.length());
1654  }
1655 
1656  // if the type is now empty (because it was detected as only const)
1657  // then this is an invalid state so we bail
1658  if (type.empty())
1659  return Type(); // #nocov
1660 
1661  if (type.find(referenceQualifier) ==
1662  (type.length() - referenceQualifier.length())) {
1663  isReference = true;
1664  type.erase(type.length() - referenceQualifier.length());
1665  }
1666  trimWhitespace(&type);
1667 
1668  // if the type is now empty because of some strange parse then bail
1669  if (type.empty())
1670  return Type(); // #nocov
1671 
1672  return Type(type, isConst, isReference);
1673  }
1674 
1675  // Validation helpers
1676 
1677  bool SourceFileAttributesParser::isKnownAttribute(const std::string& name)
1678  const {
1679  return name == kExportAttribute ||
1680  name == kInitAttribute ||
1681  name == kDependsAttribute ||
1682  name == kPluginsAttribute ||
1683  name == kInterfacesAttribute;
1684  }
1685 
1686  // Print an attribute parsing related warning
1688  const std::string& message,
1689  const std::string& attribute,
1690  size_t lineNumber) {
1691 
1692  // get basename of source file for warning message
1693  Rcpp::Function basename = Rcpp::Environment::base_env()["basename"];
1694  std::string file = Rcpp::as<std::string>(basename(sourceFile_));
1695 
1696  std::ostringstream ostr;
1697  ostr << message;
1698  if (!attribute.empty())
1699  ostr << " for " << attribute << " attribute";
1700  ostr << " at " << file << ":" << lineNumber;
1701 
1702  showWarning(ostr.str());
1703  }
1704 
1706  const std::string& message,
1707  size_t lineNumber) {
1708  attributeWarning(message, "", lineNumber);
1709  }
1710 
1712  const std::string& message,
1713  size_t lineNumber) {
1714  attributeWarning(message, "Rcpp::export", lineNumber);
1715  }
1716 
1718  size_t lineNumber) {
1719  rcppExportWarning("No function found", lineNumber);
1720  }
1721 
1723  const std::string& param,
1724  size_t lineNumber) {
1725  rcppExportWarning("Invalid parameter: "
1726  "'" + param + "'", lineNumber);
1727  }
1728 
1730  const std::string& message,
1731  size_t lineNumber) {
1732  attributeWarning(message + " (valid interfaces are 'r' and 'cpp')",
1733  "Rcpp::interfaces", lineNumber);
1734  } // #nocov end
1735 
1736 
1737  // Track /* */ comment state
1738  void CommentState::submitLine(const std::string& line) {
1739  std::size_t pos = 0;
1740  while (pos != std::string::npos) {
1741 
1742  // check for a // which would invalidate any other token found
1743  std::size_t lineCommentPos = line.find("//", pos);
1744 
1745  // look for the next token
1746  std::string token = inComment() ? "*/" : "/*";
1747  pos = line.find(token, pos);
1748 
1749  // process the comment token if found
1750  if (pos != std::string::npos) {
1751 
1752  // break if the line comment precedes the comment token
1753  if (lineCommentPos != std::string::npos && lineCommentPos < pos)
1754  break; // #nocov
1755 
1756  inComment_ = !inComment_;
1757  pos += token.size();
1758  }
1759  }
1760  }
1761 
1762 } // namespace attributes
1763 } // namespace Rcpp
1764 
1765 
1766 /*******************************************************************
1767  * AttributesGen.cpp
1768  *******************************************************************/
1769 
1770 namespace Rcpp {
1771 namespace attributes {
1772 
1773  // constants
1774  namespace {
1775  const char * const kRcppExportsSuffix = "_RcppExports.h";
1776  const char * const kTrySuffix = "_try";
1777  }
1778 
1779  ExportsGenerator::ExportsGenerator(const std::string& targetFile,
1780  const std::string& package,
1781  const std::string& commentPrefix)
1782  : targetFile_(targetFile),
1783  package_(package),
1784  packageCpp_(package),
1785  commentPrefix_(commentPrefix),
1786  hasCppInterface_(false) {
1787 
1788  // read the existing target file if it exists
1789  if (FileInfo(targetFile_).exists()) {
1790  std::ifstream ifs(targetFile_.c_str()); // #nocov start
1791  if (ifs.fail())
1793  std::stringstream buffer;
1794  buffer << ifs.rdbuf();
1795  existingCode_ = buffer.str(); // #nocov end
1796  }
1797 
1798  std::replace(packageCpp_.begin(), packageCpp_.end(), '.', '_');
1799 
1800  // see if this is safe to overwite and throw if it isn't
1801  if (!isSafeToOverwrite())
1802  throw Rcpp::file_exists(targetFile_); // #nocov
1803  }
1804 
1806  const SourceFileAttributes& attributes,
1807  bool verbose) {
1808 
1809  if (attributes.hasInterface(kInterfaceCpp))
1810  hasCppInterface_ = true; // #nocov
1811 
1812  doWriteFunctions(attributes, verbose);
1813  }
1814 
1815  // Commit the stream -- is a no-op if the existing code is identical
1816  // to the generated code. Returns true if data was written and false
1817  // if it wasn't (throws exception on io error)
1818  bool ExportsGenerator::commit(const std::string& preamble) {
1819 
1820  // get the generated code
1821  std::string code = codeStream_.str();
1822 
1823  // if there is no generated code AND the exports file does not
1824  // currently exist then do nothing
1825  if (code.empty() && !FileInfo(targetFile_).exists())
1826  return false; // #nocov
1827 
1828  // write header/preamble
1829  std::ostringstream headerStream;
1830  headerStream << commentPrefix_ << " Generated by using "
1831  << "Rcpp::compileAttributes()"
1832  << " -> do not edit by hand" << std::endl;
1833  headerStream << commentPrefix_ << " Generator token: "
1834  << generatorToken() << std::endl << std::endl;
1835  if (!preamble.empty())
1836  headerStream << preamble;
1837 
1838  // get generated code and only write it if there was a change
1839  std::string generatedCode = headerStream.str() + code;
1840  if (generatedCode != existingCode_) {
1841  // open the file
1842  std::ofstream ofs(targetFile_.c_str(),
1843  std::ofstream::out | std::ofstream::trunc);
1844  if (ofs.fail())
1845  throw Rcpp::file_io_error(targetFile_); // #nocov
1846 
1847  // write generated code and return
1848  ofs << generatedCode;
1849  ofs.close();
1850  return true;
1851  }
1852  else {
1853  return false; // #nocov
1854  }
1855  }
1856 
1857  // Remove the generated file entirely
1859  return removeFile(targetFile_);
1860  }
1861 
1862  CppExportsGenerator::CppExportsGenerator(const std::string& packageDir,
1863  const std::string& package,
1864  const std::string& fileSep)
1865  : ExportsGenerator(
1866  packageDir + fileSep + "src" + fileSep + "RcppExports.cpp",
1867  package,
1868  "//")
1869  {
1870  }
1871 
1873  const SourceFileAttributes& attributes,
1874  bool verbose) {
1875 
1876  // generate functions
1877  generateCpp(ostr(),
1878  attributes,
1879  true,
1880  attributes.hasInterface(kInterfaceCpp),
1881  packageCppPrefix());
1882 
1883  // track cppExports, signatures, and native routines (we use these
1884  // at the end to generate the ValidateSignature and RegisterCCallable
1885  // functions, and to generate a package init function with native
1886  // routine registration)
1887  for (SourceFileAttributes::const_iterator // #nocov start
1888  it = attributes.begin(); it != attributes.end(); ++it) {
1889 
1890  if (it->isExportedFunction()) {
1891 
1892  // add it to the cpp exports list if we are generating
1893  // a C++ interface and it's not hidden
1894  if (attributes.hasInterface(kInterfaceCpp)) {
1895  Function fun = it->function().renamedTo(it->exportedCppName());
1896  if (!fun.isHidden())
1897  cppExports_.push_back(*it);
1898  }
1899 
1900  // add it to the native routines list
1901  nativeRoutines_.push_back(*it);
1902  } else if (it->name() == kInitAttribute) {
1903  initFunctions_.push_back(*it);
1904  }
1905  } // #nocov end
1906 
1907  // record modules
1908  const std::vector<std::string>& modules = attributes.modules();
1909  modules_.insert(modules_.end(), modules.begin(), modules.end());
1910 
1911  // verbose if requested
1912  if (verbose) { // #nocov start
1913  Rcpp::Rcout << "Exports from " << attributes.sourceFile() << ":"
1914  << std::endl;
1915  for (std::vector<Attribute>::const_iterator
1916  it = attributes.begin(); it != attributes.end(); ++it) {
1917  if (it->isExportedFunction())
1918  Rcpp::Rcout << " " << it->function() << std::endl;
1919  }
1920  Rcpp::Rcout << std::endl; // #nocov end
1921  }
1922  }
1923 
1924  void CppExportsGenerator::writeEnd(bool hasPackageInit)
1925  {
1926  // generate a function that can be used to validate exported
1927  // functions and their signatures prior to looking up with
1928  // GetCppCallable (otherwise inconsistent signatures between
1929  // client and library would cause a crash)
1930  if (hasCppInterface()) {
1931 
1932  ostr() << std::endl; // #nocov start
1933  ostr() << "// validate"
1934  << " (ensure exported C++ functions exist before "
1935  << "calling them)" << std::endl;
1936  ostr() << "static int " << exportValidationFunctionRegisteredName()
1937  << "(const char* sig) { " << std::endl;
1938  ostr() << " static std::set<std::string> signatures;"
1939  << std::endl;
1940  ostr() << " if (signatures.empty()) {" << std::endl;
1941 
1942  for (std::size_t i=0;i<cppExports_.size(); i++) {
1943  const Attribute& attr = cppExports_[i];
1944  ostr() << " signatures.insert(\""
1945  << attr.function().signature(attr.exportedName())
1946  << "\");" << std::endl;
1947  }
1948  ostr() << " }" << std::endl;
1949  ostr() << " return signatures.find(sig) != signatures.end();"
1950  << std::endl;
1951  ostr() << "}" << std::endl;
1952 
1953  // generate a function that will register all of our C++
1954  // exports as C-callable from other packages
1955  ostr() << std::endl;
1956  ostr() << "// registerCCallable (register entry points for "
1957  "exported C++ functions)" << std::endl;
1958  ostr() << "RcppExport SEXP " << registerCCallableExportedName()
1959  << "() { " << std::endl;
1960  for (std::size_t i=0;i<cppExports_.size(); i++) {
1961  const Attribute& attr = cppExports_[i];
1962  ostr() << registerCCallable(
1963  4,
1964  attr.exportedName(),
1965  attr.function().name() + kTrySuffix);
1966  ostr() << std::endl;
1967  }
1968  ostr() << registerCCallable(4,
1971  ostr() << std::endl;
1972  ostr() << " return R_NilValue;" << std::endl;
1973  ostr() << "}" << std::endl;
1974  }
1975 
1976  // write native routines
1977  if (!hasPackageInit && (!nativeRoutines_.empty() || !modules_.empty() || !initFunctions_.empty())) {
1978 
1979  // build list of routines we will register
1980  std::vector<std::string> routineNames;
1981  std::vector<std::size_t> routineArgs;
1982  for (std::size_t i=0;i<nativeRoutines_.size(); i++) {
1983  const Attribute& attr = nativeRoutines_[i];
1984  routineNames.push_back(packageCppPrefix() + "_" + attr.function().name());
1985  routineArgs.push_back(attr.function().arguments().size());
1986  }
1987  std::string kRcppModuleBoot = "_rcpp_module_boot_";
1988  for (std::size_t i=0;i<modules_.size(); i++) {
1989  routineNames.push_back(kRcppModuleBoot + modules_[i]);
1990  routineArgs.push_back(0);
1991  }
1992  if (hasCppInterface()) {
1993  routineNames.push_back(registerCCallableExportedName());
1994  routineArgs.push_back(0);
1995  }
1996 
1997  // see if there are additional registrations to perform
1998  Rcpp::Function extraRoutinesFunc = Environment::namespace_env("Rcpp")[".extraRoutineRegistrations"];
1999  List extraRoutines = extraRoutinesFunc(targetFile(), routineNames);
2000  std::vector<std::string> declarations = extraRoutines["declarations"];
2001  std::vector<std::string> callEntries = extraRoutines["call_entries"];
2002 
2003  // add declarations for modules
2004  for (std::size_t i=0;i<modules_.size(); i++) {
2005  declarations.push_back("RcppExport SEXP " + kRcppModuleBoot + modules_[i] + "();");
2006  }
2007 
2008  // generate declarations
2009  if (declarations.size() > 0) {
2010  ostr() << std::endl;
2011  for (std::size_t i = 0; i<declarations.size(); i++)
2012  ostr() << declarations[i] << std::endl;
2013  }
2014 
2015  // generate registration code
2016  ostr() << std::endl;
2017  ostr() << "static const R_CallMethodDef CallEntries[] = {" << std::endl;
2018  for (std::size_t i=0;i<routineNames.size(); i++) {
2019  ostr() << " {\"" << routineNames[i] << "\", " <<
2020  "(DL_FUNC) &" << routineNames[i] << ", " <<
2021  routineArgs[i] << "}," << std::endl;
2022  }
2023  if (callEntries.size() > 0) {
2024  for (std::size_t i = 0; i<callEntries.size(); i++)
2025  ostr() << callEntries[i] << std::endl;
2026  }
2027  ostr() << " {NULL, NULL, 0}" << std::endl;
2028  ostr() << "};" << std::endl;
2029 
2030  ostr() << std::endl;
2031 
2032  // write prototypes for init functions
2033  for (std::size_t i = 0; i<initFunctions_.size(); i++) {
2034  const Function& function = initFunctions_[i].function();
2035  printFunction(ostr(), function, false);
2036  ostr() << ";" << std::endl;
2037  }
2038 
2039  ostr() << "RcppExport void R_init_" << packageCpp() << "(DllInfo *dll) {" << std::endl;
2040  ostr() << " R_registerRoutines(dll, NULL, CallEntries, NULL, NULL);" << std::endl;
2041  ostr() << " R_useDynamicSymbols(dll, FALSE);" << std::endl;
2042  // call init functions
2043  for (std::size_t i = 0; i<initFunctions_.size(); i++) {
2044  const Function& function = initFunctions_[i].function();
2045  ostr() << " " << function.name() << "(dll);" << std::endl;
2046  }
2047  ostr() << "}" << std::endl;
2048  }
2049 
2050  // warn if both a hand-written package init function and Rcpp::init are used
2051  if (hasPackageInit && !initFunctions_.empty()) {
2052  showWarning("[[Rcpp::init]] attribute used in a package with an explicit "
2053  "R_init function (Rcpp::init functions will not be called)");
2054  }
2055  }
2056 
2058  size_t indent,
2059  const std::string& exportedName,
2060  const std::string& name) const {
2061  std::ostringstream ostr;
2062  std::string indentStr(indent, ' ');
2063  ostr << indentStr << "R_RegisterCCallable(\"" << package() << "\", "
2064  << "\"" << packageCppPrefix() << "_" << exportedName << "\", "
2065  << "(DL_FUNC)" << packageCppPrefix() << "_" << name << ");";
2066  return ostr.str(); // #nocov end
2067  }
2068 
2069  bool CppExportsGenerator::commit(const std::vector<std::string>& includes) {
2070 
2071  // includes
2072  std::ostringstream ostr;
2073  if (!includes.empty()) {
2074  for (std::size_t i=0;i<includes.size(); i++)
2075  ostr << includes[i] << std::endl;
2076  }
2077  if (hasCppInterface()) {
2078  ostr << "#include <string>" << std::endl; // #nocov
2079  ostr << "#include <set>" << std::endl; // #nocov
2080  }
2081  ostr << std::endl;
2082 
2083  // always bring in Rcpp
2084  ostr << "using namespace Rcpp;" << std::endl << std::endl;
2085 
2086  // commit with preamble
2087  return ExportsGenerator::commit(ostr.str());
2088  }
2089 
2091  const std::string& packageDir,
2092  const std::string& package,
2093  const std::string& fileSep)
2094  : ExportsGenerator(
2095  packageDir + fileSep + "inst" + fileSep + "include" +
2096  fileSep + package + kRcppExportsSuffix,
2097  package,
2098  "//")
2099  {
2100  includeDir_ = packageDir + fileSep + "inst" + fileSep + "include";
2101  }
2102 
2104 
2105  ostr() << "namespace " << packageCpp() << " {"
2106  << std::endl << std::endl;
2107 
2108  // Import Rcpp into this namespace. This allows declarations to
2109  // be written without fully qualifying all Rcpp types. The only
2110  // negative side-effect is that when this package's namespace
2111  // is imported it will also pull in Rcpp. However since this is
2112  // opt-in and represents a general desire to do namespace aliasing
2113  // this seems okay
2114  ostr() << " using namespace Rcpp;" << std::endl << std::endl;
2115 
2116  // Write our export validation helper function. Putting it in
2117  // an anonymous namespace will hide it from callers and give
2118  // it per-translation unit linkage
2119  ostr() << " namespace {" << std::endl;
2120  ostr() << " void validateSignature(const char* sig) {"
2121  << std::endl;
2122  ostr() << " Rcpp::Function require = "
2123  << "Rcpp::Environment::base_env()[\"require\"];"
2124  << std::endl;
2125  ostr() << " require(\"" << package() << "\", "
2126  << "Rcpp::Named(\"quietly\") = true);"
2127  << std::endl;
2128 
2129  std::string validate = "validate";
2130  std::string fnType = "Ptr_" + validate;
2131  ostr() << " typedef int(*" << fnType << ")(const char*);"
2132  << std::endl;
2133 
2134  std::string ptrName = "p_" + validate;
2135  ostr() << " static " << fnType << " " << ptrName << " = "
2136  << "(" << fnType << ")" << std::endl
2137  << " "
2139  << ";" << std::endl;
2140  ostr() << " if (!" << ptrName << "(sig)) {" << std::endl;
2141  ostr() << " throw Rcpp::function_not_exported("
2142  << std::endl
2143  << " "
2144  << "\"C++ function with signature '\" + std::string(sig) + \"' not found in " << package()
2145  << "\");" << std::endl;
2146  ostr() << " }" << std::endl;
2147  ostr() << " }" << std::endl;
2148 
2149  ostr() << " }" << std::endl << std::endl;
2150  }
2151 
2153  const SourceFileAttributes& attributes,
2154  bool) {
2155 
2156  // don't write anything if there is no C++ interface
2157  if (!attributes.hasInterface(kInterfaceCpp))
2158  return;
2159 
2160  for(std::vector<Attribute>::const_iterator // #nocov start
2161  it = attributes.begin(); it != attributes.end(); ++it) {
2162 
2163  if (it->isExportedFunction()) {
2164 
2165  Function function =
2166  it->function().renamedTo(it->exportedCppName());
2167 
2168  // if it's hidden then don't generate a C++ interface
2169  if (function.isHidden())
2170  continue;
2171 
2172  ostr() << " inline " << function << " {"
2173  << std::endl;
2174 
2175  std::string fnType = "Ptr_" + function.name();
2176  ostr() << " typedef SEXP(*" << fnType << ")(";
2177  for (size_t i=0; i<function.arguments().size(); i++) {
2178  ostr() << "SEXP";
2179  if (i != (function.arguments().size()-1))
2180  ostr() << ",";
2181  }
2182  ostr() << ");" << std::endl;
2183 
2184  std::string ptrName = "p_" + function.name();
2185  ostr() << " static " << fnType << " "
2186  << ptrName << " = NULL;"
2187  << std::endl;
2188  ostr() << " if (" << ptrName << " == NULL) {"
2189  << std::endl;
2190  ostr() << " validateSignature"
2191  << "(\"" << function.signature() << "\");"
2192  << std::endl;
2193  ostr() << " " << ptrName << " = "
2194  << "(" << fnType << ")"
2195  << getCCallable(packageCppPrefix() + "_" + function.name()) << ";"
2196  << std::endl;
2197  ostr() << " }" << std::endl;
2198  ostr() << " RObject rcpp_result_gen;" << std::endl;
2199  ostr() << " {" << std::endl;
2200  if (it->rng())
2201  ostr() << " RNGScope RCPP_rngScope_gen;" << std::endl;
2202  ostr() << " rcpp_result_gen = " << ptrName << "(";
2203 
2204  const std::vector<Argument>& args = function.arguments();
2205  for (std::size_t i = 0; i<args.size(); i++) {
2206  ostr() << "Shield<SEXP>(Rcpp::wrap(" << args[i].name() << "))";
2207  if (i != (args.size()-1))
2208  ostr() << ", ";
2209  }
2210 
2211  ostr() << ");" << std::endl;
2212  ostr() << " }" << std::endl;
2213 
2214  ostr() << " if (rcpp_result_gen.inherits(\"interrupted-error\"))"
2215  << std::endl
2216  << " throw Rcpp::internal::InterruptedException();"
2217  << std::endl;
2218  ostr() << " if (Rcpp::internal::isLongjumpSentinel(rcpp_result_gen))"
2219  << std::endl
2220  << " throw Rcpp::LongjumpException(rcpp_result_gen);"
2221  << std::endl;
2222  ostr() << " if (rcpp_result_gen.inherits(\"try-error\"))"
2223  << std::endl
2224  << " throw Rcpp::exception(Rcpp::as<std::string>("
2225  << "rcpp_result_gen).c_str());"
2226  << std::endl;
2227  if (!function.type().isVoid()) {
2228  ostr() << " return Rcpp::as<" << function.type() << " >"
2229  << "(rcpp_result_gen);" << std::endl;
2230  }
2231 
2232  ostr() << " }" << std::endl << std::endl; // #nocov end
2233  }
2234  }
2235  }
2236 
2238  ostr() << "}" << std::endl;
2239  ostr() << std::endl;
2240  ostr() << "#endif // " << getHeaderGuard() << std::endl;
2241  }
2242 
2244  const std::vector<std::string>& includes) {
2245 
2246  if (hasCppInterface()) {
2247 
2248  // create the include dir if necessary
2249  createDirectory(includeDir_); // #nocov start
2250 
2251  // generate preamble
2252  std::ostringstream ostr;
2253 
2254  // header guard
2255  std::string guard = getHeaderGuard();
2256  ostr << "#ifndef " << guard << std::endl;
2257  ostr << "#define " << guard << std::endl << std::endl;
2258 
2259  // includes
2260  if (!includes.empty()) {
2261  for (std::size_t i=0;i<includes.size(); i++)
2262  {
2263  // some special processing is required here. we exclude
2264  // the package header file (since it includes this file)
2265  // and we transorm _types includes into local includes
2266  std::string preamble = "#include \"../inst/include/";
2267  std::string pkgInclude = preamble + packageCpp() + ".h\"";
2268  if (includes[i] == pkgInclude)
2269  continue;
2270 
2271  // check for _types
2272  std::string typesInclude = preamble + packageCpp() + "_types.h";
2273  if (includes[i].find(typesInclude) != std::string::npos)
2274  {
2275  std::string include = "#include \"" +
2276  includes[i].substr(preamble.length());
2277  ostr << include << std::endl;
2278  }
2279  else
2280  {
2281  ostr << includes[i] << std::endl;
2282  }
2283  }
2284  ostr << std::endl;
2285  }
2286 
2287  // commit with preamble
2288  return ExportsGenerator::commit(ostr.str()); // #nocov end
2289  }
2290  else {
2291  return ExportsGenerator::remove();
2292  }
2293  }
2294 
2296  const std::string& function) const {
2297  std::ostringstream ostr;
2298  ostr << "R_GetCCallable"
2299  << "(\"" << package() << "\", "
2300  << "\"" << function << "\")";
2301  return ostr.str();
2302  }
2303 
2305  return "RCPP_" + packageCpp() + "_RCPPEXPORTS_H_GEN_";
2306  }
2307 
2309  const std::string& packageDir,
2310  const std::string& package,
2311  const std::string& fileSep)
2312  : ExportsGenerator(
2313  packageDir + fileSep + "inst" + fileSep + "include" +
2314  fileSep + package + ".h",
2315  package,
2316  "//")
2317  {
2318  includeDir_ = packageDir + fileSep + "inst" + fileSep + "include";
2319  }
2320 
2322  if (hasCppInterface()) {
2323  // header guard
2324  std::string guard = getHeaderGuard(); // #nocov start
2325  ostr() << "#ifndef " << guard << std::endl;
2326  ostr() << "#define " << guard << std::endl << std::endl;
2327 
2328  ostr() << "#include \"" << packageCpp() << kRcppExportsSuffix
2329  << "\"" << std::endl;
2330 
2331  ostr() << std::endl;
2332  ostr() << "#endif // " << getHeaderGuard() << std::endl; // #nocov end
2333  }
2334  }
2335 
2336  bool CppPackageIncludeGenerator::commit(const std::vector<std::string>&) {
2337  if (hasCppInterface()) {
2338 
2339  // create the include dir if necessary
2340  createDirectory(includeDir_); // #nocov
2341 
2342  // commit
2343  return ExportsGenerator::commit(); // #nocov
2344  }
2345  else {
2346  return ExportsGenerator::remove();
2347  }
2348  }
2349 
2350  std::string CppPackageIncludeGenerator::getHeaderGuard() const { // #nocov
2351  return "RCPP_" + packageCpp() + "_H_GEN_"; // #nocov
2352  }
2353 
2354  RExportsGenerator::RExportsGenerator(const std::string& packageDir,
2355  const std::string& package,
2356  bool registration,
2357  const std::string& fileSep)
2358  : ExportsGenerator(
2359  packageDir + fileSep + "R" + fileSep + "RcppExports.R",
2360  package,
2361  "#"),
2362  registration_(registration)
2363  {
2364  }
2365 
2367  const SourceFileAttributes& attributes,
2368  bool) {
2369  // write standalone roxygen chunks
2370  const std::vector<std::vector<std::string> >& roxygenChunks =
2371  attributes.roxygenChunks();
2372  for (std::size_t i = 0; i<roxygenChunks.size(); i++) {
2373  const std::vector<std::string>& chunk = roxygenChunks[i]; // #nocov start
2374  for (std::size_t l = 0; l < chunk.size(); l++)
2375  ostr() << chunk[l] << std::endl;
2376  ostr() << "NULL" << std::endl << std::endl; // #nocov end
2377  }
2378 
2379  // write exported functions
2380  if (attributes.hasInterface(kInterfaceR)) {
2381  // process each attribute
2382  for(std::vector<Attribute>::const_iterator
2383  it = attributes.begin(); it != attributes.end(); ++it) {
2384 
2385  // alias the attribute and function (bail if not export)
2386  const Attribute& attribute = *it;
2387  if (!attribute.isExportedFunction())
2388  continue; // #nocov
2389  const Function& function = attribute.function();
2390 
2391  // print roxygen lines
2392  for (size_t i=0; i<attribute.roxygen().size(); i++)
2393  ostr() << attribute.roxygen()[i] << std::endl; // #nocov
2394 
2395  // build the parameter list
2396  std::string args = generateRArgList(function);
2397 
2398  // determine the function name
2399  std::string name = attribute.exportedName();
2400 
2401  // write the function
2402  ostr() << name << " <- function(" << args << ") {"
2403  << std::endl;
2404  ostr() << " ";
2405  if (function.type().isVoid())
2406  ostr() << "invisible("; // #nocov
2407  ostr() << ".Call(";
2408  if (!registration_)
2409  ostr() << "'";
2410  else
2411  ostr() << "`";
2412  ostr() << packageCppPrefix() << "_" << function.name();
2413  if (!registration_)
2414  ostr() << "', " << "PACKAGE = '" << package() << "'";
2415  else
2416  ostr() << "`";
2417 
2418  // add arguments
2419  const std::vector<Argument>& arguments = function.arguments();
2420  for (size_t i = 0; i<arguments.size(); i++)
2421  ostr() << ", " << arguments[i].name(); // #nocov
2422  ostr() << ")";
2423  if (function.type().isVoid())
2424  ostr() << ")"; // #nocov
2425  ostr() << std::endl;
2426 
2427  ostr() << "}" << std::endl << std::endl;
2428  }
2429  }
2430  }
2431 
2433  if (hasCppInterface()) { // #nocov start
2434  // register all C-callable functions
2435  ostr() << "# Register entry points for exported C++ functions"
2436  << std::endl;
2437  ostr() << "methods::setLoadAction(function(ns) {" << std::endl;
2438  ostr() << " .Call('" << registerCCallableExportedName()
2439  << "', PACKAGE = '" << package() << "')"
2440  << std::endl << "})" << std::endl; // #nocov end
2441  }
2442  }
2443 
2444  bool RExportsGenerator::commit(const std::vector<std::string>&) {
2445  return ExportsGenerator::commit();
2446  }
2447 
2449  try {
2450  for(Itr it = generators_.begin(); it != generators_.end(); ++it)
2451  delete *it;
2452  generators_.clear();
2453  }
2454  catch(...) {}
2455  }
2456 
2458  generators_.push_back(pGenerator);
2459  }
2460 
2462  for(Itr it = generators_.begin(); it != generators_.end(); ++it)
2463  (*it)->writeBegin();
2464  }
2465 
2467  const SourceFileAttributes& attributes,
2468  bool verbose) {
2469  for(Itr it = generators_.begin(); it != generators_.end(); ++it)
2470  (*it)->writeFunctions(attributes, verbose);
2471  }
2472 
2473  void ExportsGenerators::writeEnd(bool hasPackageInit) {
2474  for(Itr it = generators_.begin(); it != generators_.end(); ++it)
2475  (*it)->writeEnd(hasPackageInit);
2476  }
2477 
2478  // Commit and return a list of the files that were updated
2479  std::vector<std::string> ExportsGenerators::commit(
2480  const std::vector<std::string>& includes) {
2481 
2482  std::vector<std::string> updated;
2483 
2484  for(Itr it = generators_.begin(); it != generators_.end(); ++it) {
2485  if ((*it)->commit(includes))
2486  updated.push_back((*it)->targetFile());
2487  }
2488 
2489  return updated;
2490  }
2491 
2492  // Remove and return a list of files that were removed
2493  std::vector<std::string> ExportsGenerators::remove() { // #nocov start
2494  std::vector<std::string> removed;
2495  for(Itr it = generators_.begin(); it != generators_.end(); ++it) {
2496  if ((*it)->remove())
2497  removed.push_back((*it)->targetFile());
2498  }
2499  return removed;
2500  }
2501 
2502 
2503  // Helpers for converting C++ default arguments to R default arguments
2504  namespace {
2505 
2506  // convert a C++ numeric argument to an R argument value
2507  // (returns empty string if no conversion is possible)
2508  std::string cppNumericArgToRArg(const std::string& type,
2509  const std::string& cppArg) {
2510  // check for a number
2511  double num;
2512  std::stringstream argStream(cppArg);
2513  if ((argStream >> num)) {
2514 
2515  // L suffix means return the value literally
2516  if (!argStream.eof()) {
2517  std::string suffix;
2518  argStream >> suffix;
2519  if (argStream.eof() && suffix == "L")
2520  return cppArg;
2521  }
2522 
2523  // no decimal and the type isn't explicitly double or
2524  // float means integer
2525  if (cppArg.find('.') == std::string::npos &&
2526  type != "double" && type != "float")
2527  return cppArg + "L";
2528 
2529  // otherwise return arg literally
2530  else
2531  return cppArg;
2532  }
2533  else {
2534  return std::string();
2535  }
2536  }
2537 
2538  // convert a C++ ::create style argument value to an R argument
2539  // value (returns empty string if no conversion is possible)
2540  std::string cppCreateArgToRArg(const std::string& cppArg) {
2541 
2542  std::string create = "::create";
2543  size_t createLoc = cppArg.find(create);
2544  if (createLoc == std::string::npos ||
2545  ((createLoc + create.length()) >= cppArg.size())) {
2546  return std::string();
2547  }
2548 
2549  std::string type = cppArg.substr(0, createLoc);
2550  std::string rcppScope = "Rcpp::";
2551  size_t rcppLoc = type.find(rcppScope);
2552  if (rcppLoc == 0 && type.size() > rcppScope.length())
2553  type = type.substr(rcppScope.length());
2554 
2555  std::string args = cppArg.substr(createLoc + create.length());
2556  if (type == "CharacterVector")
2557  return "as.character( c" + args + ")";
2558  if (type == "IntegerVector")
2559  return "as.integer( c" + args + ")";
2560  if (type == "NumericVector")
2561  return "as.numeric( c" + args + ")";
2562  if (type == "LogicalVector")
2563  return "as.logical( c" + args + ")";
2564 
2565  return std::string();
2566  }
2567 
2568  // convert a C++ Matrix to an R argument (returns empty string
2569  // if no conversion possible)
2570  std::string cppMatrixArgToRArg(const std::string& cppArg) {
2571 
2572  // look for Matrix
2573  std::string matrix = "Matrix";
2574  size_t matrixLoc = cppArg.find(matrix);
2575  if (matrixLoc == std::string::npos ||
2576  ((matrixLoc + matrix.length()) >= cppArg.size())) {
2577  return std::string();
2578  }
2579 
2580  std::string args = cppArg.substr(matrixLoc + matrix.length());
2581  return "matrix" + args; // #nocov end
2582  }
2583 
2584  // convert a C++ literal to an R argument (returns empty string
2585  // if no conversion possible)
2586  std::string cppLiteralArgToRArg(const std::string& cppArg) {
2587  if (cppArg == "true")
2588  return "TRUE";
2589  else if (cppArg == "false")
2590  return "FALSE";
2591  else if (cppArg == "R_NilValue")
2592  return "NULL";
2593  else if (cppArg == "NA_STRING") // #nocov start
2594  return "NA_character_";
2595  else if (cppArg == "NA_INTEGER")
2596  return "NA_integer_";
2597  else if (cppArg == "NA_LOGICAL")
2598  return "NA_integer_";
2599  else if (cppArg == "NA_REAL")
2600  return "NA_real_";
2601  else
2602  return std::string();
2603  }
2604 
2605  // convert an Rcpp container constructor to an R argument
2606  // (returns empty string if no conversion possible)
2607  std::string cppConstructorArgToRArg(const std::string& cppArg) {
2608 
2609  // map Rcpp containers to R default initializers
2610  static std::map<std::string, std::string> RcppContainerToR;
2611  RcppContainerToR.insert(std::make_pair("NumericVector", "numeric"));
2612  RcppContainerToR.insert(std::make_pair("DoubleVector", "numeric"));
2613  RcppContainerToR.insert(std::make_pair("CharacterVector", "character"));
2614  RcppContainerToR.insert(std::make_pair("IntegerVector", "integer"));
2615  RcppContainerToR.insert(std::make_pair("LogicalVector", "logical"));
2616  RcppContainerToR.insert(std::make_pair("ComplexVector", "complex"));
2617 
2618  // for each entry in the map above, see if we find it; if we do,
2619  // return the R version
2620  typedef std::map<std::string, std::string>::const_iterator Iterator;
2621  for (Iterator it = RcppContainerToR.begin(); it != RcppContainerToR.end(); ++it) {
2622  size_t loc = cppArg.find(it->first);
2623  if (loc != std::string::npos) {
2624  return it->second + cppArg.substr(it->first.size(), std::string::npos);
2625  }
2626  }
2627 
2628  return std::string(); // #nocov end
2629 
2630  }
2631 
2632  // convert a C++ argument value to an R argument value (returns empty
2633  // string if no conversion is possible)
2634  std::string cppArgToRArg(const std::string& type,
2635  const std::string& cppArg) {
2636 
2637  // try for quoted string
2638  if (isQuoted(cppArg))
2639  return cppArg;
2640 
2641  // try for literal
2642  std::string rArg = cppLiteralArgToRArg(cppArg);
2643  if (!rArg.empty())
2644  return rArg;
2645 
2646  // try for a create arg
2647  rArg = cppCreateArgToRArg(cppArg); // #nocov start
2648  if (!rArg.empty())
2649  return rArg;
2650 
2651  // try for a matrix arg
2652  rArg = cppMatrixArgToRArg(cppArg);
2653  if (!rArg.empty())
2654  return rArg;
2655 
2656  // try for a numeric arg
2657  rArg = cppNumericArgToRArg(type, cppArg);
2658  if (!rArg.empty())
2659  return rArg;
2660 
2661  // try for a constructor arg
2662  rArg = cppConstructorArgToRArg(cppArg);
2663  if (!rArg.empty())
2664  return rArg;
2665 
2666  // couldn't parse the arg
2667  return std::string(); // #nocov end
2668  }
2669 
2670  } // anonymous namespace
2671 
2672  // Generate an R argument list for a function
2673  std::string generateRArgList(const Function& function) {
2674  std::ostringstream argsOstr;
2675  const std::vector<Argument>& arguments = function.arguments();
2676  for (size_t i = 0; i<arguments.size(); i++) {
2677  const Argument& argument = arguments[i];
2678  argsOstr << argument.name();
2679  if (!argument.defaultValue().empty()) {
2680  std::string rArg = cppArgToRArg(argument.type().name(),
2681  argument.defaultValue());
2682  if (!rArg.empty()) {
2683  argsOstr << " = " << rArg;
2684  } else {
2685  showWarning("Unable to parse C++ default value '" + // #nocov start
2686  argument.defaultValue() + "' for argument "+
2687  argument.name() + " of function " +
2688  function.name()); // #nocov end
2689  }
2690  }
2691 
2692  if (i != (arguments.size()-1))
2693  argsOstr << ", ";
2694  }
2695  return argsOstr.str();
2696  }
2697 
2698  // Generate the C++ code required to make [[Rcpp::export]] functions
2699  // available as C symbols with SEXP parameters and return
2700  void generateCpp(std::ostream& ostr,
2701  const SourceFileAttributes& attributes,
2702  bool includePrototype,
2703  bool cppInterface,
2704  const std::string& contextId) {
2705 
2706  // process each attribute
2707  for(std::vector<Attribute>::const_iterator
2708  it = attributes.begin(); it != attributes.end(); ++it) {
2709 
2710  // alias the attribute and function (bail if not export)
2711  const Attribute& attribute = *it;
2712  if (!attribute.isExportedFunction())
2713  continue;
2714  const Function& function = attribute.function();
2715 
2716  // include prototype if requested
2717  if (includePrototype) {
2718  ostr << "// " << function.name() << std::endl;
2719  printFunction(ostr, function, false);
2720  ostr << ";";
2721  }
2722 
2723  // write the C++ callable SEXP-based function (this version
2724  // returns errors via "try-error")
2725  ostr << std::endl;
2726  ostr << (cppInterface ? "static" : "RcppExport");
2727  ostr << " SEXP ";
2728  std::string funcName = contextId + "_" + function.name();
2729  ostr << funcName;
2730  if (cppInterface)
2731  ostr << kTrySuffix; // #nocov
2732  ostr << "(";
2733  std::ostringstream ostrArgs;
2734  const std::vector<Argument>& arguments = function.arguments();
2735  for (size_t i = 0; i<arguments.size(); i++) {
2736  const Argument& argument = arguments[i];
2737  ostrArgs << "SEXP " << argument.name() << "SEXP";
2738  if (i != (arguments.size()-1))
2739  ostrArgs << ", ";
2740  }
2741  std::string args = ostrArgs.str();
2742  ostr << args << ") {" << std::endl;
2743  ostr << "BEGIN_RCPP" << std::endl;
2744  if (!function.type().isVoid())
2745  ostr << " Rcpp::RObject rcpp_result_gen;" << std::endl;
2746  if (!cppInterface && attribute.rng())
2747  ostr << " Rcpp::RNGScope rcpp_rngScope_gen;" << std::endl;
2748  for (size_t i = 0; i<arguments.size(); i++) {
2749  const Argument& argument = arguments[i];
2750 
2751  ostr << " Rcpp::traits::input_parameter< "
2752  << argument.type().full_name() << " >::type " << argument.name()
2753  << "(" << argument.name() << "SEXP);" << std::endl;
2754  }
2755 
2756  ostr << " ";
2757  if (!function.type().isVoid())
2758  ostr << "rcpp_result_gen = Rcpp::wrap(";
2759  ostr << function.name() << "(";
2760  for (size_t i = 0; i<arguments.size(); i++) {
2761  const Argument& argument = arguments[i];
2762  ostr << argument.name();
2763  if (i != (arguments.size()-1))
2764  ostr << ", ";
2765  }
2766  if (!function.type().isVoid())
2767  ostr << ")";
2768  ostr << ");" << std::endl;
2769 
2770  if (!function.type().isVoid())
2771  {
2772  ostr << " return rcpp_result_gen;" << std::endl;
2773  }
2774  else
2775  {
2776  ostr << " return R_NilValue;" << std::endl;
2777  }
2778  ostr << (cppInterface ? "END_RCPP_RETURN_ERROR" : "END_RCPP")
2779  << std::endl;
2780  ostr << "}" << std::endl;
2781 
2782  // Now write an R wrapper that returns error via Rf_error
2783  if (cppInterface) {
2784  ostr << "RcppExport SEXP " << funcName << "(" << args << ") {" // #nocov start
2785  << std::endl;
2786  ostr << " SEXP rcpp_result_gen;" << std::endl;
2787  ostr << " {" << std::endl;
2788  if (attribute.rng())
2789  ostr << " Rcpp::RNGScope rcpp_rngScope_gen;" << std::endl;
2790  ostr << " rcpp_result_gen = PROTECT(" << funcName
2791  << kTrySuffix << "(";
2792  for (size_t i = 0; i<arguments.size(); i++) {
2793  const Argument& argument = arguments[i];
2794  ostr << argument.name() << "SEXP";
2795  if (i != (arguments.size()-1))
2796  ostr << ", ";
2797  }
2798  ostr << "));" << std::endl;
2799  ostr << " }" << std::endl;
2800  ostr << " Rboolean rcpp_isInterrupt_gen = Rf_inherits(rcpp_result_gen, \"interrupted-error\");"
2801  << std::endl
2802  << " if (rcpp_isInterrupt_gen) {" << std::endl
2803  << " UNPROTECT(1);" << std::endl
2804  << " Rf_onintr();" << std::endl
2805  << " }" << std::endl
2806  << " bool rcpp_isLongjump_gen = Rcpp::internal::isLongjumpSentinel(rcpp_result_gen);" << std::endl
2807  << " if (rcpp_isLongjump_gen) {" << std::endl
2808  // No need to unprotect before jump
2809  << " Rcpp::internal::resumeJump(rcpp_result_gen);" << std::endl
2810  << " }" << std::endl
2811  << " Rboolean rcpp_isError_gen = Rf_inherits(rcpp_result_gen, \"try-error\");"
2812  << std::endl
2813  << " if (rcpp_isError_gen) {" << std::endl
2814  << " SEXP rcpp_msgSEXP_gen = Rf_asChar(rcpp_result_gen);" << std::endl
2815  << " UNPROTECT(1);" << std::endl
2816  << " Rf_error(CHAR(rcpp_msgSEXP_gen));" << std::endl
2817  << " }" << std::endl
2818  << " UNPROTECT(1);" << std::endl
2819  << " return rcpp_result_gen;" << std::endl
2820  << "}" << std::endl; // #nocov end
2821  }
2822  }
2823  }
2824 
2825 } // namespace attributes
2826 } // namespace Rcpp
2827 
2828 
2829 
2830 
2831 
2832 // provide implementations for util
2833 namespace Rcpp {
2834 namespace attributes {
2835 
2836  // Utility class for getting file existence and last modified time
2837  FileInfo::FileInfo(const std::string& path)
2838  : path_(path), exists_(false), lastModified_(0)
2839  {
2840  #ifdef _WIN32
2841  struct _stat buffer;
2842  int result = _stat(path.c_str(), &buffer);
2843  #else
2844  struct stat buffer;
2845  int result = stat(path.c_str(), &buffer);
2846  #endif
2847  if (result != 0) {
2848  if (errno == ENOENT)
2849  exists_ = false;
2850  else
2851  throw Rcpp::file_io_error(errno, path); // #nocov
2852  } else {
2853  exists_ = true;
2854  lastModified_ = static_cast<double>(buffer.st_mtime);
2855  }
2856  }
2857 
2858  // Remove a file (call back into R for this)
2859  bool removeFile(const std::string& path) {
2860  if (FileInfo(path).exists()) {
2861  Rcpp::Function rm = Rcpp::Environment::base_env()["file.remove"]; // #nocov start
2862  rm(path);
2863  return true; // #nocov end
2864  }
2865  else {
2866  return false;
2867  }
2868  }
2869 
2870  // Recursively create a directory (call back into R for this)
2871  void createDirectory(const std::string& path) { // #nocov start
2872  if (!FileInfo(path).exists()) {
2873  Rcpp::Function mkdir = Rcpp::Environment::base_env()["dir.create"];
2874  mkdir(path, Rcpp::Named("recursive") = true);
2875  }
2876  } // #nocov end
2877 
2878  // Known whitespace chars
2879  const char * const kWhitespaceChars = " \f\n\r\t\v";
2880 
2881  // Query whether a character is whitespace
2882  bool isWhitespace(char ch) {
2883  return std::strchr(kWhitespaceChars, ch) != NULL;
2884  }
2885 
2886  // Remove trailing line comments -- ie, find comments that don't begin
2887  // a line, and remove them. We avoid stripping attributes.
2888  void stripTrailingLineComments(std::string* pStr) {
2889 
2890  if (pStr->empty()) return;
2891 
2892  size_t len = pStr->length();
2893  bool inString = false;
2894  size_t idx = 0;
2895 
2896  // if this is an roxygen comment, then bail
2897  if (isRoxygenCpp(*pStr)) return;
2898 
2899  // skip over initial whitespace
2900  idx = pStr->find_first_not_of(kWhitespaceChars);
2901  if (idx == std::string::npos) return;
2902 
2903  // skip over a first comment
2904  if (idx + 1 < len && pStr->at(idx) == '/' && pStr->at(idx + 1) == '/') {
2905  idx = idx + 2;
2906  }
2907 
2908  // since we are searching for "//", we iterate up to 2nd last character
2909  while (idx < len - 1) {
2910 
2911  if (inString) {
2912  if (pStr->at(idx) == '"' && pStr->at(idx - 1) != '\\') {
2913  inString = false;
2914  }
2915  } else {
2916  if (pStr->at(idx) == '"') {
2917  inString = true;
2918  }
2919  }
2920 
2921  if (!inString &&
2922  pStr->at(idx) == '/' &&
2923  pStr->at(idx + 1) == '/') {
2924  pStr->erase(idx);
2925  return;
2926  }
2927  ++idx;
2928  }
2929  }
2930 
2931  // Trim a string
2932  void trimWhitespace(std::string* pStr) {
2933 
2934  // skip empty case
2935  if (pStr->empty())
2936  return;
2937 
2938  // trim right
2939  std::string::size_type pos = pStr->find_last_not_of(kWhitespaceChars);
2940  if (pos != std::string::npos)
2941  pStr->erase(pos + 1);
2942 
2943  // trim left
2944  pos = pStr->find_first_not_of(kWhitespaceChars);
2945  pStr->erase(0, pos);
2946  }
2947 
2948  // Strip balanced quotes from around a string (assumes already trimmed)
2949  void stripQuotes(std::string* pStr) {
2950  if (pStr->length() < 2)
2951  return;
2952  char quote = *(pStr->begin());
2953  if ( (quote == '\'' || quote == '\"') && (*(pStr->rbegin()) == quote) )
2954  *pStr = pStr->substr(1, pStr->length()-2); // #nocov
2955  }
2956 
2957  // is the passed string quoted?
2958  bool isQuoted(const std::string& str) {
2959  if (str.length() < 2)
2960  return false; // #nocov
2961  char quote = *(str.begin());
2962  return (quote == '\'' || quote == '\"') && (*(str.rbegin()) == quote);
2963  }
2964 
2965  // does a string end with another string?
2966  bool endsWith(const std::string& str, const std::string& suffix)
2967  {
2968  return str.size() >= suffix.size() &&
2969  str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
2970  }
2971 
2972  // show a warning message
2973  void showWarning(const std::string& msg) { // #nocov start
2975  warning(msg, Rcpp::Named("call.") = false);
2976  } // #nocov end
2977 
2978  bool isRoxygenCpp(const std::string& str) {
2979  size_t len = str.length();
2980  if (len < 3) return false;
2981  size_t idx = str.find_first_not_of(kWhitespaceChars);
2982  if (idx == std::string::npos) return false;
2983 
2984  // make sure there are characters to check
2985  if (len - 2 < idx) return false;
2986 
2987  if (str[idx] == '/' &&
2988  str[idx + 1] == '/' &&
2989  str[idx + 2] == '\'') {
2990  return true;
2991  }
2992 
2993  return false;
2994 
2995  }
2996 
2997 } // namespace attributes
2998 } // namespace Rcpp
2999 
3000 
3001 /*******************************************************************
3002  * Attributes.cpp
3003  *******************************************************************/
3004 
3005 using namespace Rcpp::attributes;
3006 
3007 // Implementation helpers for sourceCppContext
3008 namespace {
3009 
3010  // Class that manages generation of source code for the sourceCpp dynlib
3011  class SourceCppDynlib {
3012  public:
3013  SourceCppDynlib() {}
3014 
3015  SourceCppDynlib(const std::string& cacheDir,
3016  const std::string& cppSourcePath,
3017  Rcpp::List platform)
3018  : cppSourcePath_(cppSourcePath)
3019 
3020  {
3021  // get cpp source file info
3022  FileInfo cppSourceFilenameInfo(cppSourcePath_);
3023  if (!cppSourceFilenameInfo.exists())
3024  throw Rcpp::file_not_found(cppSourcePath_); // #nocov
3025 
3026  // record the base name of the source file
3027  Rcpp::Function basename = Rcpp::Environment::base_env()["basename"];
3028  cppSourceFilename_ = Rcpp::as<std::string>(basename(cppSourcePath_));
3029 
3030  // get platform info
3031  fileSep_ = Rcpp::as<std::string>(platform["file.sep"]);
3032  dynlibExt_ = Rcpp::as<std::string>(platform["dynlib.ext"]);
3033 
3034  // generate temp directory
3035  Rcpp::Function tempfile = Rcpp::Environment::base_env()["tempfile"];
3036  buildDirectory_ = Rcpp::as<std::string>(tempfile("sourcecpp_", cacheDir));
3037  std::replace(buildDirectory_.begin(), buildDirectory_.end(), '\\', '/');
3038  Rcpp::Function dircreate = Rcpp::Environment::base_env()["dir.create"];
3039  dircreate(buildDirectory_);
3040 
3041  // generate a random context id
3042  contextId_ = "sourceCpp_" + uniqueToken(cacheDir);
3043 
3044  // regenerate the source code
3045  regenerateSource(cacheDir);
3046  }
3047 
3048  // create from list
3049  explicit SourceCppDynlib(const Rcpp::List& dynlib)
3050  {
3051  using namespace Rcpp;
3052 
3053  cppSourcePath_ = as<std::string>(dynlib["cppSourcePath"]);
3054  generatedCpp_ = as<std::string>(dynlib["generatedCpp"]);
3055  cppSourceFilename_ = as<std::string>(dynlib["cppSourceFilename"]);
3056  contextId_ = as<std::string>(dynlib["contextId"]);
3057  buildDirectory_ = as<std::string>(dynlib["buildDirectory"]);
3058  fileSep_ = as<std::string>(dynlib["fileSep"]);
3059  dynlibFilename_ = as<std::string>(dynlib["dynlibFilename"]);
3060  previousDynlibFilename_ = as<std::string>(dynlib["previousDynlibFilename"]);
3061  dynlibExt_ = as<std::string>(dynlib["dynlibExt"]);
3062  exportedFunctions_ = as<std::vector<std::string> >(dynlib["exportedFunctions"]);
3063  modules_ = as<std::vector<std::string> >(dynlib["modules"]);
3064  depends_ = as<std::vector<std::string> >(dynlib["depends"]);
3065  plugins_ = as<std::vector<std::string> >(dynlib["plugins"]);
3066  embeddedR_ = as<std::vector<std::string> >(dynlib["embeddedR"]);
3067  List sourceDependencies = as<List>(dynlib["sourceDependencies"]);
3068  for (R_xlen_t i = 0; i<sourceDependencies.length(); i++) {
3069  List fileInfo = as<List>(sourceDependencies.at(i)); // #nocov
3070  sourceDependencies_.push_back(FileInfo(fileInfo)); // #nocov
3071  }
3072  }
3073 
3074  // convert to list
3075  Rcpp::List toList() const {
3076  using namespace Rcpp;
3077  List dynlib;
3078  dynlib["cppSourcePath"] = cppSourcePath_;
3079  dynlib["generatedCpp"] = generatedCpp_;
3080  dynlib["cppSourceFilename"] = cppSourceFilename_;
3081  dynlib["contextId"] = contextId_;
3082  dynlib["buildDirectory"] = buildDirectory_;
3083  dynlib["fileSep"] = fileSep_;
3084  dynlib["dynlibFilename"] = dynlibFilename_;
3085  dynlib["previousDynlibFilename"] = previousDynlibFilename_;
3086  dynlib["dynlibExt"] = dynlibExt_;
3087  dynlib["exportedFunctions"] = exportedFunctions_;
3088  dynlib["modules"] = modules_;
3089  dynlib["depends"] = depends_;
3090  dynlib["plugins"] = plugins_;
3091  dynlib["embeddedR"] = embeddedR_;
3092  List sourceDependencies;
3093  for (std::size_t i = 0; i<sourceDependencies_.size(); i++) {
3094  FileInfo fileInfo = sourceDependencies_.at(i);
3095  sourceDependencies.push_back(fileInfo.toList());
3096  }
3097  dynlib["sourceDependencies"] = sourceDependencies;
3098 
3099  return dynlib;
3100  }
3101 
3102  bool isEmpty() const { return cppSourcePath_.empty(); }
3103 
3104  bool isBuilt() const { return FileInfo(dynlibPath()).exists(); };
3105 
3106  bool isSourceDirty() const {
3107  // source file out of date means we're dirty
3108  if (FileInfo(cppSourcePath_).lastModified() >
3109  FileInfo(generatedCppSourcePath()).lastModified())
3110  return true; // #nocov
3111 
3112  // no dynlib means we're dirty
3113  if (!FileInfo(dynlibPath()).exists())
3114  return true; // #nocov
3115 
3116  // variation in source dependencies means we're dirty
3117  std::vector<FileInfo> sourceDependencies = parseSourceDependencies(
3118  cppSourcePath_);
3119  if (sourceDependencies != sourceDependencies_)
3120  return true; // #nocov
3121 
3122  // not dirty
3123  return false;
3124  }
3125 
3126  void regenerateSource(const std::string& cacheDir) {
3127 
3128  // create new dynlib filename
3129  previousDynlibFilename_ = dynlibFilename_;
3130  dynlibFilename_ = "sourceCpp_" + uniqueToken(cacheDir) + dynlibExt_;
3131 
3132  // copy the source file to the build dir
3133  Rcpp::Function filecopy = Rcpp::Environment::base_env()["file.copy"];
3134  filecopy(cppSourcePath_, generatedCppSourcePath(), true);
3135 
3136  // parse attributes
3137  SourceFileAttributesParser sourceAttributes(cppSourcePath_, "", true);
3138 
3139  // generate cpp for attributes and append them
3140  std::ostringstream ostr;
3141  // always include Rcpp.h in case the user didn't
3142  ostr << std::endl << std::endl;
3143  ostr << "#include <Rcpp.h>" << std::endl;
3144  generateCpp(ostr, sourceAttributes, true, false, contextId_);
3145  generatedCpp_ = ostr.str();
3146  std::ofstream cppOfs(generatedCppSourcePath().c_str(),
3147  std::ofstream::out | std::ofstream::app);
3148  if (cppOfs.fail())
3149  throw Rcpp::file_io_error(generatedCppSourcePath()); // #nocov
3150  cppOfs << generatedCpp_;
3151  cppOfs.close();
3152 
3153  // generate R for attributes and write it into the build directory
3154  std::ofstream rOfs(generatedRSourcePath().c_str(),
3155  std::ofstream::out | std::ofstream::trunc);
3156  if (rOfs.fail())
3157  throw Rcpp::file_io_error(generatedRSourcePath()); // #nocov
3158 
3159  // DLLInfo - hide using . and ensure uniqueness using contextId
3160  std::string dllInfo = "`." + contextId_ + "_DLLInfo`";
3161  rOfs << dllInfo << " <- dyn.load('" << dynlibPath() << "')"
3162  << std::endl << std::endl;
3163 
3164  // Generate R functions
3165  generateR(rOfs, sourceAttributes, dllInfo);
3166 
3167  // remove the DLLInfo
3168  rOfs << std::endl << "rm(" << dllInfo << ")"
3169  << std::endl;
3170 
3171  rOfs.close();
3172 
3173  // discover exported functions and dependencies
3174  exportedFunctions_.clear();
3175  depends_.clear();
3176  plugins_.clear();
3178  it = sourceAttributes.begin(); it != sourceAttributes.end(); ++it) {
3179 
3180  if (it->name() == kExportAttribute && !it->function().empty())
3181  exportedFunctions_.push_back(it->exportedName());
3182 
3183  else if (it->name() == kDependsAttribute) {
3184  for (size_t i = 0; i<it->params().size(); ++i) // #nocov
3185  depends_.push_back(it->params()[i].name()); // #nocov
3186  }
3187 
3188  else if (it->name() == kPluginsAttribute) {
3189  for (size_t i = 0; i<it->params().size(); ++i)
3190  plugins_.push_back(it->params()[i].name());
3191  }
3192  }
3193 
3194  // capture modules
3195  modules_ = sourceAttributes.modules();
3196 
3197  // capture embededded R
3198  embeddedR_ = sourceAttributes.embeddedR();
3199 
3200  // capture source dependencies
3201  sourceDependencies_ = sourceAttributes.sourceDependencies();
3202  }
3203 
3204  const std::string& contextId() const {
3205  return contextId_;
3206  }
3207 
3208  const std::string& cppSourcePath() const {
3209  return cppSourcePath_;
3210  }
3211 
3212  const std::vector<std::string> cppDependencySourcePaths() {
3213  std::vector<std::string> dependencies;
3214  for (size_t i = 0; i<sourceDependencies_.size(); ++i) {
3215  FileInfo dep = sourceDependencies_[i];
3216  if (dep.extension() == ".cc" || dep.extension() == ".cpp") {
3217  dependencies.push_back(dep.path()); // #nocov
3218  }
3219  }
3220  return dependencies;
3221  }
3222 
3223  std::string buildDirectory() const {
3224  return buildDirectory_;
3225  }
3226 
3227  std::string generatedCpp() const {
3228  return generatedCpp_;
3229  }
3230 
3231  std::string cppSourceFilename() const {
3232  return cppSourceFilename_;
3233  }
3234 
3235  std::string rSourceFilename() const {
3236  return cppSourceFilename() + ".R";
3237  }
3238 
3239  std::string dynlibFilename() const {
3240  return dynlibFilename_;
3241  }
3242 
3243  std::string dynlibPath() const {
3244  return buildDirectory_ + fileSep_ + dynlibFilename();
3245  }
3246 
3247  std::string previousDynlibPath() const {
3248  if (!previousDynlibFilename_.empty())
3249  return buildDirectory_ + fileSep_ + previousDynlibFilename_; // #nocov
3250  else
3251  return std::string();
3252  }
3253 
3254  const std::vector<std::string>& exportedFunctions() const {
3255  return exportedFunctions_;
3256  }
3257 
3258  const std::vector<std::string>& modules() const {
3259  return modules_;
3260  }
3261 
3262  const std::vector<std::string>& depends() const { return depends_; };
3263 
3264  const std::vector<std::string>& plugins() const { return plugins_; };
3265 
3266  const std::vector<std::string>& embeddedR() const { return embeddedR_; }
3267 
3268  private:
3269 
3270  std::string generatedCppSourcePath() const {
3271  return buildDirectory_ + fileSep_ + cppSourceFilename();
3272  }
3273 
3274  std::string generatedRSourcePath() const {
3275  return buildDirectory_ + fileSep_ + rSourceFilename();
3276  }
3277 
3278  void generateR(std::ostream& ostr,
3279  const SourceFileAttributes& attributes,
3280  const std::string& dllInfo) const
3281  {
3282  // process each attribute
3283  for(std::vector<Attribute>::const_iterator
3284  it = attributes.begin(); it != attributes.end(); ++it) {
3285 
3286  // alias the attribute and function (bail if not export)
3287  const Attribute& attribute = *it;
3288  if (!attribute.isExportedFunction())
3289  continue;
3290  const Function& function = attribute.function();
3291 
3292  // export the function
3293  ostr << attribute.exportedName()
3294  << " <- Rcpp:::sourceCppFunction("
3295  << "function(" << generateRArgList(function) << ") {}, "
3296  << (function.type().isVoid() ? "TRUE" : "FALSE") << ", "
3297  << dllInfo << ", "
3298  << "'" << contextId_ + "_" + function.name()
3299  << "')" << std::endl;
3300  }
3301 
3302  // modules
3303  std::vector<std::string> modules = attributes.modules();
3304  if (modules.size() > 0)
3305  {
3306  // modules require definition of C++Object to be loaded
3307  ostr << "library(Rcpp)" << std::endl;
3308 
3309  // load each module
3310  for (std::vector<std::string>::const_iterator
3311  it = modules.begin(); it != modules.end(); ++it)
3312  {
3313  ostr << " populate( Rcpp::Module(\"" << *it << "\","
3314  << dllInfo << "), environment() ) " << std::endl;
3315  }
3316  }
3317 
3318  }
3319 
3320  std::string uniqueToken(const std::string& cacheDir) {
3322  Rcpp::Function uniqueTokenFunc = rcppEnv[".sourceCppDynlibUniqueToken"];
3323  return Rcpp::as<std::string>(uniqueTokenFunc(cacheDir));
3324  }
3325 
3326  private:
3327  std::string cppSourcePath_;
3328  std::string generatedCpp_;
3329  std::string cppSourceFilename_;
3330  std::string contextId_;
3331  std::string buildDirectory_;
3332  std::string fileSep_;
3333  std::string dynlibFilename_;
3334  std::string previousDynlibFilename_;
3335  std::string dynlibExt_;
3336  std::vector<std::string> exportedFunctions_;
3337  std::vector<std::string> modules_;
3338  std::vector<std::string> depends_;
3339  std::vector<std::string> plugins_;
3340  std::vector<std::string> embeddedR_;
3341  std::vector<FileInfo> sourceDependencies_;
3342  };
3343 
3344  // Dynlib cache that allows lookup by either file path or code contents
3345 
3346  void dynlibCacheInsert(const std::string& cacheDir,
3347  const std::string& file,
3348  const std::string& code,
3349  const SourceCppDynlib& dynlib)
3350  {
3352  Rcpp::Function dynlibInsertFunc = rcppEnv[".sourceCppDynlibInsert"];
3353  dynlibInsertFunc(cacheDir, file, code, dynlib.toList());
3354  }
3355 
3356  void dynlibCacheInsertFile(const std::string& cacheDir,
3357  const std::string& file,
3358  const SourceCppDynlib& dynlib)
3359  {
3360  dynlibCacheInsert(cacheDir, file, "", dynlib);
3361  }
3362 
3363  void dynlibCacheInsertCode(const std::string& cacheDir,
3364  const std::string& code,
3365  const SourceCppDynlib& dynlib)
3366  {
3367  dynlibCacheInsert(cacheDir, "", code, dynlib);
3368  }
3369 
3370  SourceCppDynlib dynlibCacheLookup(const std::string& cacheDir,
3371  const std::string& file,
3372  const std::string& code)
3373  {
3375  Rcpp::Function dynlibLookupFunc = rcppEnv[".sourceCppDynlibLookup"];
3376  Rcpp::List dynlibList = dynlibLookupFunc(cacheDir, file, code);
3377  if (dynlibList.length() > 0)
3378  return SourceCppDynlib(dynlibList);
3379  else
3380  return SourceCppDynlib();
3381  }
3382 
3383  SourceCppDynlib dynlibCacheLookupByFile(const std::string& cacheDir,
3384  const std::string& file)
3385  {
3386  return dynlibCacheLookup(cacheDir, file, "");
3387  }
3388 
3389  SourceCppDynlib dynlibCacheLookupByCode(const std::string& cacheDir,
3390  const std::string& code)
3391  {
3392  return dynlibCacheLookup(cacheDir, "", code);
3393  }
3394 
3395 } // anonymous namespace
3396 
3397 // Create temporary build directory, generate code as necessary, and return
3398 // the context required for the sourceCpp function to complete it's work
3399 RcppExport SEXP sourceCppContext(SEXP sFile, SEXP sCode,
3400  SEXP sRebuild, SEXP sCacheDir, SEXP sPlatform) {
3401 BEGIN_RCPP
3402  // parameters
3403  std::string file = Rcpp::as<std::string>(sFile);
3404  std::string code = sCode != R_NilValue ? Rcpp::as<std::string>(sCode) : "";
3405  bool rebuild = Rcpp::as<bool>(sRebuild);
3406  std::string cacheDir = Rcpp::as<std::string>(sCacheDir);
3407  Rcpp::List platform = Rcpp::as<Rcpp::List>(sPlatform);
3408 
3409  // get dynlib (using cache if possible)
3410  SourceCppDynlib dynlib = !code.empty() ? dynlibCacheLookupByCode(cacheDir, code)
3411  : dynlibCacheLookupByFile(cacheDir, file);
3412 
3413  // check dynlib build state
3414  bool buildRequired = false;
3415 
3416  // if there is no dynlib in the cache then create a new one
3417  if (dynlib.isEmpty()) {
3418  buildRequired = true;
3419  dynlib = SourceCppDynlib(cacheDir, file, platform);
3420  }
3421 
3422  // if the cached dynlib is dirty then regenerate the source
3423  else if (rebuild || dynlib.isSourceDirty()) {
3424  buildRequired = true; // #nocov
3425  dynlib.regenerateSource(cacheDir); // #nocov
3426  }
3427 
3428  // if the dynlib hasn't yet been built then note that
3429  else if (!dynlib.isBuilt()) {
3430  buildRequired = true; // #nocov
3431  }
3432 
3433  // save the dynlib to the cache
3434  if (!code.empty())
3435  dynlibCacheInsertCode(cacheDir, code, dynlib);
3436  else
3437  dynlibCacheInsertFile(cacheDir, file, dynlib);
3438 
3439  // return context as a list
3440  using namespace Rcpp;
3441  return List::create(
3442  _["contextId"] = dynlib.contextId(),
3443  _["cppSourcePath"] = dynlib.cppSourcePath(),
3444  _["cppDependencySourcePaths"] = dynlib.cppDependencySourcePaths(),
3445  _["buildRequired"] = buildRequired,
3446  _["buildDirectory"] = dynlib.buildDirectory(),
3447  _["generatedCpp"] = dynlib.generatedCpp(),
3448  _["exportedFunctions"] = dynlib.exportedFunctions(),
3449  _["modules"] = dynlib.modules(),
3450  _["cppSourceFilename"] = dynlib.cppSourceFilename(),
3451  _["rSourceFilename"] = dynlib.rSourceFilename(),
3452  _["dynlibFilename"] = dynlib.dynlibFilename(),
3453  _["dynlibPath"] = dynlib.dynlibPath(),
3454  _["previousDynlibPath"] = dynlib.previousDynlibPath(),
3455  _["depends"] = dynlib.depends(),
3456  _["plugins"] = dynlib.plugins(),
3457  _["embeddedR"] = dynlib.embeddedR());
3458 END_RCPP
3459 }
3460 
3461 // Compile the attributes within the specified package directory into
3462 // RcppExports.cpp and RcppExports.R
3463 RcppExport SEXP compileAttributes(SEXP sPackageDir,
3464  SEXP sPackageName,
3465  SEXP sDepends,
3466  SEXP sRegistration,
3467  SEXP sCppFiles,
3468  SEXP sCppFileBasenames,
3469  SEXP sIncludes,
3470  SEXP sVerbose,
3471  SEXP sPlatform) {
3472 BEGIN_RCPP
3473  // arguments
3474  std::string packageDir = Rcpp::as<std::string>(sPackageDir);
3475  std::string packageName = Rcpp::as<std::string>(sPackageName);
3476 
3477  Rcpp::CharacterVector vDepends = Rcpp::as<Rcpp::CharacterVector>(sDepends);
3478  std::set<std::string> depends;
3480  it = vDepends.begin(); it != vDepends.end(); ++it) {
3481  depends.insert(std::string(*it));
3482  }
3483 
3484  bool registration = Rcpp::as<bool>(sRegistration);
3485 
3486  std::vector<std::string> cppFiles =
3487  Rcpp::as<std::vector<std::string> >(sCppFiles);
3488  std::vector<std::string> cppFileBasenames =
3489  Rcpp::as<std::vector<std::string> >(sCppFileBasenames);
3490  std::vector<std::string> includes =
3491  Rcpp::as<std::vector<std::string> >(sIncludes);
3492  bool verbose = Rcpp::as<bool>(sVerbose);
3493  Rcpp::List platform = Rcpp::as<Rcpp::List>(sPlatform);
3494  std::string fileSep = Rcpp::as<std::string>(platform["file.sep"]);
3495 
3496  // initialize generators
3497  ExportsGenerators generators;
3498  generators.add(new CppExportsGenerator(packageDir, packageName, fileSep));
3499  generators.add(new RExportsGenerator(packageDir, packageName, registration, fileSep));
3500 
3501  // catch file exists exception if the include file already exists
3502  // and we are unable to overwrite it
3503  try {
3504  generators.add(new CppExportsIncludeGenerator(packageDir,
3505  packageName,
3506  fileSep));
3507  }
3508  catch(const Rcpp::file_exists& e) {
3509  std::string msg =
3510  "The header file '" + e.filePath() + "' already exists so "
3511  "cannot be overwritten by Rcpp::interfaces";
3512  throw Rcpp::exception(msg.c_str(), __FILE__, __LINE__);
3513  }
3514 
3515  // catch file exists exception for package include (because if it
3516  // already exists we simply leave it alone)
3517  try {
3518  generators.add(new CppPackageIncludeGenerator(packageDir,
3519  packageName,
3520  fileSep));
3521  }
3522  catch(const Rcpp::file_exists& e) {}
3523 
3524  // write begin
3525  generators.writeBegin();
3526 
3527  // Parse attributes from each file and generate code as required.
3528  bool hasPackageInit = false;
3529  bool haveAttributes = false;
3530  std::set<std::string> dependsAttribs;
3531  for (std::size_t i=0; i<cppFiles.size(); i++) {
3532 
3533  // don't process RcppExports.cpp
3534  std::string cppFile = cppFiles[i];
3535  if (endsWith(cppFile, "RcppExports.cpp"))
3536  continue;
3537 
3538  // parse file
3539  SourceFileAttributesParser attributes(cppFile, packageName, false);
3540 
3541  // note if we found a package init function
3542  if (!hasPackageInit && attributes.hasPackageInit())
3543  hasPackageInit = true;
3544 
3545  // continue if no generator output
3546  if (!attributes.hasGeneratorOutput())
3547  continue; // #nocov
3548 
3549  // confirm we have attributes
3550  haveAttributes = true;
3551 
3552  // write functions
3553  generators.writeFunctions(attributes, verbose);
3554 
3555  // track depends
3557  it = attributes.begin(); it != attributes.end(); ++it) {
3558  if (it->name() == kDependsAttribute) {
3559  for (size_t i = 0; i<it->params().size(); ++i) // #nocov
3560  dependsAttribs.insert(it->params()[i].name()); // #nocov
3561  }
3562  }
3563  }
3564 
3565  // write end
3566  generators.writeEnd(hasPackageInit);
3567 
3568  // commit or remove
3569  std::vector<std::string> updated;
3570  if (haveAttributes)
3571  updated = generators.commit(includes);
3572  else
3573  updated = generators.remove(); // #nocov
3574 
3575  // print warning if there are depends attributes that don't have
3576  // corresponding entries in the DESCRIPTION file
3577  std::vector<std::string> diff;
3578  std::set_difference(dependsAttribs.begin(), dependsAttribs.end(),
3579  depends.begin(), depends.end(),
3580  std::back_inserter(diff));
3581  if (!diff.empty()) {
3582  std::string msg =
3583  "The following packages are referenced using Rcpp::depends "
3584  "attributes however are not listed in the Depends, Imports or "
3585  "LinkingTo fields of the package DESCRIPTION file: ";
3586  for (size_t i=0; i<diff.size(); i++) {
3587  msg += diff[i];
3588  if (i != (diff.size()-1))
3589  msg += ", ";
3590  }
3591  showWarning(msg);
3592  }
3593 
3594  // verbose output
3595  if (verbose) {
3596  for (size_t i=0; i<updated.size(); i++)
3597  Rcpp::Rcout << updated[i] << " updated." << std::endl;
3598  }
3599 
3600  // return files updated
3601  return Rcpp::wrap<std::vector<std::string> >(updated);
3602 END_RCPP
3603 }
3604 
std::vector< Attribute >::const_iterator const_iterator
Definition: attributes.cpp:408
void stripTrailingLineComments(std::string *pStr)
const char *const kInitAttribute
Definition: attributes.cpp:156
Function(const Type &type, const std::string &name, const std::vector< Argument > &arguments)
Definition: attributes.cpp:244
const Function & function() const
Definition: attributes.cpp:345
void printFunction(std::ostream &os, const Function &function, bool printArgDefaults=true)
bool operator!=(const Type &other) const
Definition: attributes.cpp:183
virtual const std::string & sourceFile() const
Definition: attributes.cpp:464
std::vector< std::string > roxygen_
Definition: attributes.cpp:390
virtual bool commit(const std::vector< std::string > &includes)
std::string exportedName() const
Definition: attributes.cpp:351
std::vector< Param > parseParameters(const std::string &input)
Proxy at(const size_t &i)
Definition: Vector.h:349
T as(SEXP x)
Definition: as.h:151
std::string path() const
Definition: attributes.cpp:71
Attribute(const std::string &name, const std::vector< Param > &params, const Function &function, const std::vector< std::string > &roxygen)
Definition: attributes.cpp:313
void trimWhitespace(std::string *pStr)
void rcppExportNoFunctionFoundWarning(size_t lineNumber)
const std::string packageCppPrefix() const
Definition: attributes.cpp:582
static internal::NamedPlaceHolder _
Definition: Named.h:64
bool operator==(const Param &other) const
Definition: attributes.cpp:291
const std::vector< Param > & params() const
Definition: attributes.cpp:337
virtual const std::vector< std::string > & modules() const =0
virtual void writeEnd(bool hasPackageInit)
bool operator==(const FileInfo &other) const
Definition: attributes.cpp:87
const char *const kExportName
Definition: attributes.cpp:154
std::vector< Attribute > initFunctions_
Definition: attributes.cpp:678
double lastModified() const
Definition: attributes.cpp:73
const char *const kParamValueTRUE
Definition: attributes.cpp:165
virtual bool commit(const std::vector< std::string > &includes)
const Type & type() const
Definition: attributes.cpp:231
std::vector< ExportsGenerator * > generators_
Definition: attributes.cpp:779
void printArgument(std::ostream &os, const Argument &argument, bool printDefault=true)
const Type & type() const
Definition: attributes.cpp:274
const char *const kInterfaceCpp
Definition: attributes.cpp:161
std::string exportValidationFunctionRegisteredName()
Definition: attributes.cpp:615
const char *const kExportRng
Definition: attributes.cpp:155
std::vector< std::string > parseArguments(const std::string &argText)
void showWarning(const std::string &msg)
Type(const std::string &name, bool isConst, bool isReference)
Definition: attributes.cpp:171
std::string getCCallable(const std::string &function) const
bool hasParameter(const std::string &name) const
Definition: attributes.cpp:341
std::vector< std::string > commit(const std::vector< std::string > &includes)
virtual const std::vector< std::string > & modules() const
Definition: attributes.cpp:470
const std::string & name() const
Definition: attributes.cpp:301
Argument Named(const std::string &name)
Definition: Named.h:40
const char *const kWhitespaceChars
bool operator!=(const Function &other) const
Definition: attributes.cpp:270
std::vector< Argument > arguments_
Definition: attributes.cpp:281
const std::string & defaultValue() const
Definition: attributes.cpp:232
virtual const std::vector< std::vector< std::string > > & roxygenChunks() const =0
CppExportsGenerator(const std::string &packageDir, const std::string &package, const std::string &fileSep)
std::vector< ExportsGenerator * >::iterator Itr
Definition: attributes.cpp:754
R_xlen_t size() const
Definition: Vector.h:274
static R_CallMethodDef callEntries[]
Definition: Rcpp_init.cpp:31
const char *const kInterfaceR
Definition: attributes.cpp:160
Function_Impl< PreserveStorage > Function
Definition: Function.h:114
std::vector< std::string > modules_
Definition: attributes.cpp:687
bool endsWith(const std::string &str, const std::string &suffix)
#define RcppExport
Definition: RcppCommon.h:139
bool isQuoted(const std::string &str)
const std::vector< FileInfo > & sourceDependencies() const
Definition: attributes.cpp:512
void rcppExportWarning(const std::string &message, size_t lineNumber)
virtual const std::vector< std::vector< std::string > > & roxygenChunks() const
Definition: attributes.cpp:475
const char *const kPluginsAttribute
Definition: attributes.cpp:158
const std::string & name() const
Definition: attributes.cpp:230
const std::string & name() const
Definition: attributes.cpp:335
Type parseType(const std::string &text)
virtual bool commit(const std::vector< std::string > &includes)
std::string parseSignature(size_t lineNumber)
void rcppInterfacesWarning(const std::string &message, size_t lineNumber)
virtual void doWriteFunctions(const SourceFileAttributes &attributes, bool verbose)
virtual const_iterator begin() const =0
void attributeWarning(const std::string &message, const std::string &attribute, size_t lineNumber)
const char *const kParamValueFALSE
Definition: attributes.cpp:164
std::vector< Param > params_
Definition: attributes.cpp:388
const char *const kParamValueFalse
Definition: attributes.cpp:162
bool operator==(const Argument &other) const
Definition: attributes.cpp:219
traits::r_vector_iterator< RTYPE, PreserveStorage >::type iterator
Definition: Vector.h:47
#define BEGIN_RCPP
Definition: macros.h:30
std::string extension() const
Definition: attributes.cpp:75
ExportsGenerator(const std::string &targetFile, const std::string &package, const std::string &commentPrefix)
FileInfo(const List &fileInfo)
Definition: attributes.cpp:56
const char *const kInterfacesAttribute
Definition: attributes.cpp:159
iterator insert(iterator position, const T &object)
Definition: Vector.h:477
virtual const std::string & sourceFile() const =0
void add(ExportsGenerator *pGenerator)
const std::string & name() const
Definition: attributes.cpp:187
std::vector< std::string > remove()
bool isRoxygenCpp(const std::string &str)
static Environment_Impl base_env()
Definition: Environment.h:347
bool operator==(const Function &other) const
Definition: attributes.cpp:264
bool isReference() const
Definition: attributes.cpp:198
virtual void doWriteFunctions(const SourceFileAttributes &attributes, bool verbose)
virtual void writeEnd(bool hasPackageInit)
Attribute parseAttribute(const std::vector< std::string > &match, int lineNumber)
R_xlen_t length() const
Definition: Vector.h:267
virtual bool hasInterface(const std::string &name) const =0
std::vector< Attribute > nativeRoutines_
Definition: attributes.cpp:684
void push_back(const T &object)
Definition: Vector.h:450
virtual void doWriteFunctions(const SourceFileAttributes &attributes, bool verbose)=0
std::vector< std::vector< std::string > > roxygenChunks_
Definition: attributes.cpp:548
bool operator!=(const Param &other) const
Definition: attributes.cpp:296
RcppExport SEXP compileAttributes(SEXP sPackageDir, SEXP sPackageName, SEXP sDepends, SEXP sRegistration, SEXP sCppFiles, SEXP sCppFileBasenames, SEXP sIncludes, SEXP sVerbose, SEXP sPlatform)
std::string exportedCppName() const
Definition: attributes.cpp:369
CppPackageIncludeGenerator(const std::string &packageDir, const std::string &package, const std::string &fileSep)
std::string generatorToken() const
Definition: attributes.cpp:643
void createDirectory(const std::string &path)
Param paramNamed(const std::string &name) const
static Vector create()
Definition: Vector.h:1120
const std::vector< Argument > & arguments() const
Definition: attributes.cpp:276
void stripQuotes(std::string *pStr)
CppExportsIncludeGenerator(const std::string &packageDir, const std::string &package, const std::string &fileSep)
void submitLine(const std::string &line)
std::string registerCCallable(size_t indent, const std::string &exportedName, const std::string &name) const
bool is(SEXP x)
Definition: is.h:53
std::vector< std::string > roxygenBuffer_
Definition: attributes.cpp:549
const std::vector< std::string > & embeddedR() const
Definition: attributes.cpp:507
FileInfo(const std::string &path)
#define END_RCPP
Definition: macros.h:74
void rcppExportInvalidParameterWarning(const std::string &param, size_t lineNumber)
virtual const_iterator end() const =0
const std::string & targetFile() const
Definition: attributes.cpp:579
virtual bool commit(const std::vector< std::string > &includes)
void warning(const char *fmt, Args &&... args)
Definition: exceptions.h:46
SEXP wrap(const Date &date)
Definition: Date.h:38
bool removeFile(const std::string &path)
bool operator<(const FileInfo &other) const
Definition: attributes.cpp:83
SourceFileAttributesParser(const std::string &sourceFile, const std::string &packageFile, bool parseDependencies)
bool exists(const std::string &name) const
Definition: Environment.h:190
Argument(const std::string &name, const Type &type, const std::string &defaultValue)
Definition: attributes.cpp:210
bool operator!=(const Argument &other) const
Definition: attributes.cpp:225
std::vector< Attribute > cppExports_
Definition: attributes.cpp:681
bool remove(const std::string &name)
Definition: Environment.h:240
const char *const kExportAttribute
Definition: attributes.cpp:153
std::ostream & operator<<(std::ostream &os) const
Definition: attributes.cpp:97
virtual void doWriteFunctions(const SourceFileAttributes &attributes, bool verbose)
Function renamedTo(const std::string &name) const
Definition: attributes.cpp:251
void generateCpp(std::ostream &ostr, const SourceFileAttributes &attributes, bool includePrototype, bool cppInterface, const std::string &contextId)
void writeFunctions(const SourceFileAttributes &attributes, bool verbose)
RObject_Impl< PreserveStorage > RObject
Definition: RObject.h:56
const std::string & package() const
Definition: attributes.cpp:580
virtual bool commit(const std::vector< std::string > &includes)=0
bool operator!=(const Attribute &other) const
Definition: attributes.cpp:330
const std::string & packageCpp() const
Definition: attributes.cpp:581
const std::string & value() const
Definition: attributes.cpp:302
bool operator!=(const FileInfo &other) const
Definition: attributes.cpp:93
Rcpp API.
Definition: algo.h:28
virtual const_iterator end() const
Definition: attributes.cpp:468
Environment_Impl< PreserveStorage > Environment
Definition: Environment.h:401
RExportsGenerator(const std::string &packageDir, const std::string &package, bool registration, const std::string &fileSep)
IntegerVector match(const VectorBase< RTYPE, NA, T > &x, const VectorBase< RTYPE, RHS_NA, RHS_T > &table_)
Definition: match.h:28
virtual void writeEnd(bool hasPackageInit)
static Rostream< true > Rcout
Definition: Rstreambuf.h:83
void writeEnd(bool hasPackageInit)
RcppExport SEXP sourceCppContext(SEXP sFile, SEXP sCode, SEXP sRebuild, SEXP sCacheDir, SEXP sPlatform)
SEXP find(const std::string &name) const
Definition: Environment.h:145
bool isWhitespace(char ch)
virtual void writeEnd(bool hasPackageInit)
bool operator==(const Attribute &other) const
Definition: attributes.cpp:323
Function parseFunction(size_t lineNumber)
std::string full_name() const
Definition: attributes.cpp:188
const std::string & name() const
Definition: attributes.cpp:275
virtual bool hasInterface(const std::string &name) const
Definition: attributes.cpp:486
const char *const kDependsAttribute
Definition: attributes.cpp:157
std::string generateRArgList(const Function &function)
virtual void doWriteFunctions(const SourceFileAttributes &, bool)
Definition: attributes.cpp:723
bool isKnownAttribute(const std::string &name) const
sugar::Diff< INTSXP, LHS_NA, LHS_T > diff(const VectorBase< INTSXP, LHS_NA, LHS_T > &lhs)
Definition: diff.h:124
std::string signature() const
Definition: attributes.cpp:255
bool operator==(const Type &other) const
Definition: attributes.cpp:177
void writeFunctions(const SourceFileAttributes &attributes, bool verbose)
virtual const_iterator begin() const
Definition: attributes.cpp:467
const char *const kParamValueTrue
Definition: attributes.cpp:163
static Environment_Impl namespace_env(const std::string &package)
Definition: Environment.h:372
std::string filePath() const
Definition: exceptions.h:88
const std::vector< std::string > & roxygen() const
Definition: attributes.cpp:384