Skip to content

Commit

Permalink
Add [[Rcpp::init]] attribute for registering functions to run on pack…
Browse files Browse the repository at this point in the history
…age init (#903)

* Add [[Rcpp::init]] attribute for registering functions to run on package init

* don't bump version
  • Loading branch information
jjallaire authored and eddelbuettel committed Sep 18, 2018
1 parent 3924240 commit 404e1ef
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 3 deletions.
5 changes: 5 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
2018-09-18 JJ Allaire <jj@rstudio.com>

* src/attributes.cpp: Add support [[Rcpp::init]] attribute
* vignettes/Rcpp-attributes.Rmd: Documentation for [[Rcpp::init]] attribute

2018-09-17 Dirk Eddelbuettel <edd@debian.org>

* inst/include/Rcpp/r/headers.h: Define STRICT_R_HEADERS, but until
Expand Down
5 changes: 5 additions & 0 deletions inst/NEWS.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
now; until then we protect it via \code{RCPP_NO_STRICT_HEADERS}
which can then be used to avoid the definition; downstream
maintainers are encouraged to update their packages as needed
}
\item Changes in Rcpp Attributes:
\itemize{
\item Added \code{[[Rcpp::init]]} attribute for registering C++ functions to
run during package initialization.
}
\item Changes in Rcpp Modules:
\itemize{
Expand Down
31 changes: 28 additions & 3 deletions src/attributes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ namespace attributes {
const char * const kExportAttribute = "export";
const char * const kExportName = "name";
const char * const kExportRng = "rng";
const char * const kInitAttribute = "init";
const char * const kDependsAttribute = "depends";
const char * const kPluginsAttribute = "plugins";
const char * const kInterfacesAttribute = "interfaces";
Expand Down Expand Up @@ -673,6 +674,9 @@ namespace attributes {
const std::string& name) const;

private:
// for generating calls to init functions
std::vector<Attribute> initFunctions_;

// for generating C++ interfaces
std::vector<Attribute> cppExports_;

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

// special handling for export
if (name == kExportAttribute) {
// special handling for export and init
if (name == kExportAttribute || name == kInitAttribute) {

// parse the function (unless we are at the end of the file in
// which case we print a warning)
Expand Down Expand Up @@ -1673,6 +1677,7 @@ namespace attributes {
bool SourceFileAttributesParser::isKnownAttribute(const std::string& name)
const {
return name == kExportAttribute ||
name == kInitAttribute ||
name == kDependsAttribute ||
name == kPluginsAttribute ||
name == kInterfacesAttribute;
Expand Down Expand Up @@ -1894,6 +1899,8 @@ namespace attributes {

// add it to the native routines list
nativeRoutines_.push_back(*it);
} else if (it->name() == kInitAttribute) {
initFunctions_.push_back(*it);
}
} // #nocov end

Expand Down Expand Up @@ -1967,7 +1974,7 @@ namespace attributes {
}

// write native routines
if (!hasPackageInit && (!nativeRoutines_.empty() || !modules_.empty())) {
if (!hasPackageInit && (!nativeRoutines_.empty() || !modules_.empty() || !initFunctions_.empty())) {

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

ostr() << std::endl;

// write prototypes for init functions
for (std::size_t i = 0; i<initFunctions_.size(); i++) {
const Function& function = initFunctions_[i].function();
printFunction(ostr(), function, false);
ostr() << ";" << std::endl;
}

ostr() << "RcppExport void R_init_" << packageCpp() << "(DllInfo *dll) {" << std::endl;
ostr() << " R_registerRoutines(dll, NULL, CallEntries, NULL, NULL);" << std::endl;
ostr() << " R_useDynamicSymbols(dll, FALSE);" << std::endl;
// call init functions
for (std::size_t i = 0; i<initFunctions_.size(); i++) {
const Function& function = initFunctions_[i].function();
ostr() << " " << function.name() << "(dll);" << std::endl;
}
ostr() << "}" << std::endl;
}

// warn if both a hand-written package init function and Rcpp::init are used
if (hasPackageInit && !initFunctions_.empty()) {
showWarning("[[Rcpp::init]] attribute used in a package with an explicit "
"R_init function (Rcpp::init functions will not be called)");
}
}

std::string CppExportsGenerator::registerCCallable(
Expand Down
24 changes: 24 additions & 0 deletions vignettes/Rcpp-attributes.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,30 @@ step may be required. Specifically, if your package `NAMESPACE` file
does not use a pattern to export functions then you should add an explicit
entry to `NAMESPACE` for each R function you want publicly available.

## Package Init Functions

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>.

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:

```{cpp, eval = FALSE}
// [[Rcpp::init]]
void my_package_init(DllInfo *dll) {
// initialization code here
}
```

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:

```{cpp, eval = FALSE}
void my_package_init(DllInfo *dll);
RcppExport void R_init_pkgname(DllInfo *dll) {
R_registerRoutines(dll, NULL, CallEntries, NULL, NULL);
R_useDynamicSymbols(dll, FALSE);
my_package_init(dll);
}
```

## Types in Generated Code

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

0 comments on commit 404e1ef

Please sign in to comment.