Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 2 commits into from
Sep 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>

* .travis.yml (env): Switch to rcpp/ci for ci use
Expand Down
5 changes: 5 additions & 0 deletions inst/NEWS.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
\item Changes in Rcpp API:
\itemize{
TBD
}
\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);
}
```

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lovely stuff!

## Types in Generated Code

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