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