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