The RcppHeaderSharing R package provides an example of providing a set
of C++ functions in one R package to be used by other R packages. There
are multiple approaches to facilitate such a framework. Within this project, the
focus is on distributing C++ header files within one of the R packages
by making a C++ header-only library through the extensive use of inline
keyword.
To install the package, you must first have a compiler on your system that is compatible with R. For help on obtaining a compiler consult either macOS or Windows guides.
With a compiler in hand, one can then install the package from GitHub by:
# install.packages("remotes")
remotes::install_github("coatless-rd-rcpp/rcpp-shared-cpp-functions")
library("RcppHeaderSharing")This project focuses on showcasing how to package in R a C++ header-only library. By approaching sharing C++ functions in this manner, the complexity is reduced at the expense of compile time during development and initial installation. Complexity reduction arises from the library not needing to be initially compiled, packaged, and then installed on a local user's computer.
Principally, header-only libraries combine both the function definition
and implementation within the header files denoted by .h or .hpp.
In comparison, traditional C++ implementations that rely upon .cpp and .hpp
file pairing result in the header files (.hpp) containing only a minimal
"bones" public-facing implementation while the meat of the implementation is
done in .cpp.
.
├── DESCRIPTION # Package metadata
├── LICENSE # Code license
├── NAMESPACE # Function and dependency registration
├── R # R functions
│ ├── RcppExports.R # Autogenerated R to C++ bindings by Rcpp
│ └── RcppHeaderSharing-package.R # Package documentation
├── README.md
├── RcppHeaderSharing.Rproj
├── inst # C++ Headers
│ └── include
│ ├── RcppHeaderSharing.h # Main header file
│ ├── hello
│ │ └── say_hello.h # Individual header file with inline func
│ └── rcpp_math
│ └── add_numbers.h
├── man # Package Documentation
│ ├── RcppHeaderSharing-package.Rd
│ └── add_numbers.Rd
└── src # Compiled Code
├── Makevars # Set compilation flag to include headers
├── Makevars.win
├── RcppExports.cpp # Autogenerated R Bindings
└── export-inline-header.cpp # Surface into R a C++ header function.The shared C++ code is placed under inst/include/ so that, once the package
is installed, the headers are copied into the installed package's include/
directory, the location other packages reach through LinkingTo. The headers
are organized around a single entry-point header, RcppHeaderSharing.h, that
includes Rcpp.h once and then pulls in each individual header using relative
"" includes:
#ifndef RcppHeaderSharing_RcppHeaderSharing_H
#define RcppHeaderSharing_RcppHeaderSharing_H
#include <Rcpp.h>
#include "hello/say_hello.h"
#include "rcpp_math/add_numbers.h"
#endifEach header is wrapped in an inclusion guard named after the package and the
header (RcppHeaderSharing_<name>_H) so it is processed only once, and each
function is sandboxed inside a RcppHeaderSharing namespace so that consumers
retrieve it with a namespaced call such as RcppHeaderSharing::add_numbers():
#ifndef RcppHeaderSharing_add_numbers_H
#define RcppHeaderSharing_add_numbers_H
namespace RcppHeaderSharing {
inline Rcpp::NumericVector add_numbers(Rcpp::NumericVector x, double y) {
return x + y;
}
}
#endifSo the package can compile against its own headers, src/Makevars (and
src/Makevars.win on Windows) add the include directory to the compiler flags:
PKG_CXXFLAGS=-I../inst/include/Because the function definitions live entirely in the headers, those headers are
#included into multiple translation units, both within this package and in
any package that links against it. Repeating a function's full definition across
translation units would ordinarily violate the One Definition Rule and produce
"multiple definition" errors at link time. Marking each function inline is what
resolves this: it permits the identical definition to appear in every translation
unit that includes the header and instructs the linker to collapse those copies
into one. The inline keyword is therefore the mechanism that makes a
header-only library possible.
// The `inline` keyword lets this definition be included in many translation
// units without a One Definition Rule violation (from inst/include/hello/say_hello.h).
namespace RcppHeaderSharing {
inline void say_hello() {
Rcpp::Rcout << "hello!" << std::endl;
}
}In the other R package, modify the DESCRIPTION file's LinkingTo: field
to include the package name.
LinkingTo: Rcpp, package_name
Imports:
Rcpp (>= 0.12.11)
Within a C++ file in src/, then add:
#include <Rcpp.h>
#include <package_name.h>Retrieve the function definitions with a namespaced function call, e.g.
package_name::function_name()Samples of using making available functions via a C++ header-only library R package:
GPL (>= 2)