Skip to content

Commit 404e1ef

Browse files
jjallaireeddelbuettel
authored andcommitted
Add [[Rcpp::init]] attribute for registering functions to run on package init (#903)
* Add [[Rcpp::init]] attribute for registering functions to run on package init * don't bump version
1 parent 3924240 commit 404e1ef

File tree

4 files changed

+62
-3
lines changed

4 files changed

+62
-3
lines changed

ChangeLog

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
2018-09-18 JJ Allaire <[email protected]>
2+
3+
* src/attributes.cpp: Add support [[Rcpp::init]] attribute
4+
* vignettes/Rcpp-attributes.Rmd: Documentation for [[Rcpp::init]] attribute
5+
16
2018-09-17 Dirk Eddelbuettel <[email protected]>
27

38
* inst/include/Rcpp/r/headers.h: Define STRICT_R_HEADERS, but until

inst/NEWS.Rd

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111
now; until then we protect it via \code{RCPP_NO_STRICT_HEADERS}
1212
which can then be used to avoid the definition; downstream
1313
maintainers are encouraged to update their packages as needed
14+
}
15+
\item Changes in Rcpp Attributes:
16+
\itemize{
17+
\item Added \code{[[Rcpp::init]]} attribute for registering C++ functions to
18+
run during package initialization.
1419
}
1520
\item Changes in Rcpp Modules:
1621
\itemize{

src/attributes.cpp

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ namespace attributes {
153153
const char * const kExportAttribute = "export";
154154
const char * const kExportName = "name";
155155
const char * const kExportRng = "rng";
156+
const char * const kInitAttribute = "init";
156157
const char * const kDependsAttribute = "depends";
157158
const char * const kPluginsAttribute = "plugins";
158159
const char * const kInterfacesAttribute = "interfaces";
@@ -673,6 +674,9 @@ namespace attributes {
673674
const std::string& name) const;
674675

675676
private:
677+
// for generating calls to init functions
678+
std::vector<Attribute> initFunctions_;
679+
676680
// for generating C++ interfaces
677681
std::vector<Attribute> cppExports_;
678682

@@ -1323,8 +1327,8 @@ namespace attributes {
13231327
// and it doesn't appear at the end of the file
13241328
Function function;
13251329

1326-
// special handling for export
1327-
if (name == kExportAttribute) {
1330+
// special handling for export and init
1331+
if (name == kExportAttribute || name == kInitAttribute) {
13281332

13291333
// parse the function (unless we are at the end of the file in
13301334
// which case we print a warning)
@@ -1673,6 +1677,7 @@ namespace attributes {
16731677
bool SourceFileAttributesParser::isKnownAttribute(const std::string& name)
16741678
const {
16751679
return name == kExportAttribute ||
1680+
name == kInitAttribute ||
16761681
name == kDependsAttribute ||
16771682
name == kPluginsAttribute ||
16781683
name == kInterfacesAttribute;
@@ -1894,6 +1899,8 @@ namespace attributes {
18941899

18951900
// add it to the native routines list
18961901
nativeRoutines_.push_back(*it);
1902+
} else if (it->name() == kInitAttribute) {
1903+
initFunctions_.push_back(*it);
18971904
}
18981905
} // #nocov end
18991906

@@ -1967,7 +1974,7 @@ namespace attributes {
19671974
}
19681975

19691976
// write native routines
1970-
if (!hasPackageInit && (!nativeRoutines_.empty() || !modules_.empty())) {
1977+
if (!hasPackageInit && (!nativeRoutines_.empty() || !modules_.empty() || !initFunctions_.empty())) {
19711978

19721979
// build list of routines we will register
19731980
std::vector<std::string> routineNames;
@@ -2022,11 +2029,29 @@ namespace attributes {
20222029

20232030
ostr() << std::endl;
20242031

2032+
// write prototypes for init functions
2033+
for (std::size_t i = 0; i<initFunctions_.size(); i++) {
2034+
const Function& function = initFunctions_[i].function();
2035+
printFunction(ostr(), function, false);
2036+
ostr() << ";" << std::endl;
2037+
}
2038+
20252039
ostr() << "RcppExport void R_init_" << packageCpp() << "(DllInfo *dll) {" << std::endl;
20262040
ostr() << " R_registerRoutines(dll, NULL, CallEntries, NULL, NULL);" << std::endl;
20272041
ostr() << " R_useDynamicSymbols(dll, FALSE);" << std::endl;
2042+
// call init functions
2043+
for (std::size_t i = 0; i<initFunctions_.size(); i++) {
2044+
const Function& function = initFunctions_[i].function();
2045+
ostr() << " " << function.name() << "(dll);" << std::endl;
2046+
}
20282047
ostr() << "}" << std::endl;
20292048
}
2049+
2050+
// warn if both a hand-written package init function and Rcpp::init are used
2051+
if (hasPackageInit && !initFunctions_.empty()) {
2052+
showWarning("[[Rcpp::init]] attribute used in a package with an explicit "
2053+
"R_init function (Rcpp::init functions will not be called)");
2054+
}
20302055
}
20312056

20322057
std::string CppExportsGenerator::registerCCallable(

vignettes/Rcpp-attributes.Rmd

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,30 @@ step may be required. Specifically, if your package `NAMESPACE` file
650650
does not use a pattern to export functions then you should add an explicit
651651
entry to `NAMESPACE` for each R function you want publicly available.
652652

653+
## Package Init Functions
654+
655+
Rcpp attribute compilation will automatically generate a package R_init function that does native routine registration as described here: <https://cran.r-project.org/doc/manuals/r-release/R-exts.html#Registering-native-routines>.
656+
657+
You may however want to add additional C++ code to the package initialization sequence. To do this, you can add the `[[Rcpp::init]]` attribute to functions within your package. For example:
658+
659+
```{cpp, eval = FALSE}
660+
// [[Rcpp::init]]
661+
void my_package_init(DllInfo *dll) {
662+
// initialization code here
663+
}
664+
```
665+
666+
In this case, a call to `my_package_init()` will be added to the end of the automatically generated R_init function within RcppExports.cpp. For example:
667+
668+
```{cpp, eval = FALSE}
669+
void my_package_init(DllInfo *dll);
670+
RcppExport void R_init_pkgname(DllInfo *dll) {
671+
R_registerRoutines(dll, NULL, CallEntries, NULL, NULL);
672+
R_useDynamicSymbols(dll, FALSE);
673+
my_package_init(dll);
674+
}
675+
```
676+
653677
## Types in Generated Code
654678

655679
In some cases the signatures of the C++ functions that are generated within

0 commit comments

Comments
 (0)