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