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