Daniel Hanson is a full-time lecturer in the Computational Finance & Risk Management program within the Department of Applied Mathematics at the University of Washington.
In the previous post in this series, we looked at some design considerations when integrating standard and reusable C++ code into an R package. Specific emphasis was on Rcpp’s role in facilitating a means of communication between R and the C++ code, particularly highlighting a few of the C++ functions in the Rcpp
namespace that conveniently and efficiently pass data between an R numeric
vector and a C++ std::vector<double>
object.
Today, we will look at a specific example of implementing the interface. We will see how to configure code that allows the use of standard reusable C++ in an R package, without having to modify it with any R or Rcpp-specific syntax. It is admittedly a simple and toy example, but the goal is to provide a starting point that can be easily extended for more real world examples.
The Code
To get started, let’s have a look at the code we will use for our demonstration. It is broken into three categories, consistent with the design considerations from the previous post:
- Standard and reusable C++: No dependence on R or Rcpp
- Interface level C++: Uses functions in the
Rcpp
namespace - R functions exported by the interface level: Same names as in the interface level
Standard and Reusable C++ Code
In this example, we will use a small set of C++ non-member functions, and two classes. There is a declaration (header) file for the non-member functions, say NonmemberCppFcns.h
, and another with class declarations for two shape classes, Square
and Circle
, called ConcreteShapes.h
. Each of these is accompanied by a corresponding implementation file, with file extension .cpp
, as one might expect in a more realistic C++ code base.
Nonmember C++ Functions
These functions are shown and described here, as declared in the following C++ header file:
#include <vector>
// Adds two real numbers
double add(double x, double y);
// Sorts a vector of real numbers and returns it
std::vector<double> sortVec(std::vector<double> v);
// Computes the product of the LCM and GCD of two integers,
// using C++17 functions std::lcm(.) and std::gcd(.)
int prodLcmGcd(int m, int n);
The last function uses recently added features in the C++ Standard Library, to show that we can use C++17.
C++ Classes
The two classes in our reusable code base are declared in the ConcreteShapes.h
file, as shown and described here. Much like textbook C++ examples, we’ll write classes for two geometric shapes, each with a member function to compute the area of its corresponding object.
#include <cmath>
class Circle
{
public:
Circle(double radius);
// Computes the area of a circle with given radius
double area() const;
private:
double radius_;
};
class Square
{
public:
Square(double side);
// Computes the area of a square with given side length
double area() const;
private:
double side_;
};
Interface Level C++
Now, the next step is to employ Rcpp, namely for the following essential tasks:
- Export the interface functions to R
- Facilitate data exchange between R and C++ container objects
An interface file containing functions designated for export to R does not require a header file with declarations; one can think of it as being analogous to a .cpp
file that contains the main()
function in a C++ executable project. In addition, the interface can be contained in one file, or split into multiple files. For demonstration, I have written two such files: CppInterface.cpp
, which provides the interface to the non-member functions above, and CppInterface2.cpp
, which does the same for the two C++ classes.
Interface to Non-Member C++ Functions:
Let’s first have a look the CppInterface.cpp
interface file, which connects R with the nonmember functions in our C++ code base:
#include "NonmemberCppFcns.h"
#include <vector>
#include <Rcpp.h>
// Nonmember Function Interfaces:
// [[Rcpp::export]]
int rAdd(double x, double y)
{
// Call the add(.) function in the reusable C++ code base:
return add(x, y);
}
// [[Rcpp::export]]
Rcpp::NumericVector rSortVec(Rcpp::NumericVector v)
{
// Transfer data from NumericVector to std::vector<double>
auto stlVec = Rcpp::as<std::vector<double>>(v);
// Call the reusable sortVec(.) function, with the expected
// std::vector<double> argument:
stlVec = sortVec(stlVec);
// Reassign the results from the vector<double> return object
// to the same NumericVector v, using Rcpp::wrap(.):
v = Rcpp::wrap(stlVec);
// Return as an Rcpp::NumericVector:
return v;
}
// C++17 example:
// [[Rcpp::export]]
int rProdLcmGcd(int m, int n)
{
return prodLcmGcd(m, n);
}
Included Declarations:
The NonmemberCppFcns.h
declaration file is included at the top with #include
, just as it would in a standalone C++ application, so that the interface will recognize these functions that reside in the reusable code base. The STL vector
declaration is required, as we shall soon see. And, the key in making the interface work resides in the Rcpp.h
file, which provides access to very useful C++ functions in the Rcpp
namespace.
Function implementations:
Each of these functions is designated for export to R when the package is built, by placing the
//
[[Rcpp::export]]
tag just above the each function signature, as shown above. In this particular example, each interface function simply calls a function in the reusable code base. For example, the rAdd(.)
function simply calls the add(.)
function in the reusable C++ code. In the absence of a user-defined namespace, the interface function name must be different from the function it calls to prevent name clash errors during the build, so I have simply chosen to prefix an r
to the name of each interface function.
Note that the rSort(.)
function takes in an Rcpp::NumericVector
object, v
. This type will accept data passed in from R as a numeric
vector and present it as a C++ object. Then, so that we can call a function in our code base, such as sort(.)
, which expects a std::vector<double>
type as its input, Rcpp
also provides the Rcpp::as<.>(.)
function that facilitates the transfer of data from an Rcpp::NumericVector
object to the STL container:
auto stlVec = Rcpp::as<std::vector<double>>(v);
Rcpp
also gives us a function that will transfer data from a std::vector<double>
type being returned from our reusable C++ code back into an Rcpp::NumericVector
, so that the results can be passed back to R as a familiar numeric
vector type:
v = Rcpp::wrap(stlVec);
As the std::vector<double>
object is the workhorse C++ STL containers in quantitative work, these two Rcpp
functions are a godsend.
Remark 1: There is no rule that says an interface function can only call a single function in the reusable code; one can use whichever functions or classes that are needed to get the job done, just like with any other C++ function. I’ve merely kept it simple here for demonstration purposes.
Remark 2: The tag // [[Rcpp::plugins(cpp17)]]
is sometimes placed at the top of a C++ source file in online examples related to Rcpp
and C++17. I have not found this necessary in my own code, however, as long as the Makeconf
file has been updated for C++17, as described in the first post in this series.
Interface to C++ Classes:
We now turn our attention to the second interface file, CppInterface2.cpp
, which connects R with the C++ classes in our reusable code. It is shown here:
#include "ConcreteShapes.h"
// Class Member Function Interfaces:
// Interface to Square member
// function area(.):
// [[Rcpp::export]]
double squareArea(double side)
{
Square sq(side);
return sq.area();
}
// Interface to Circle member
// function area(.):
// [[Rcpp::export]]
double circleArea(double radius)
{
Circle circ(radius);
return circ.area();
}
This is again nothing terribly sophisticated, but the good news is it shows the process of creating instances of classes from the code base is not difficult at all. We first #include
only the header file containing these class declarations; Rcpp.h
is not required here, as we are not using any functions in the the Rcpp namespace.
To compute the area of a square, the side
length is input in R as a simple numeric
type and passed to the interface function as a C++ double
. The Square
object, sq
, is constructed with the side
argument, and its area()
member function performs said calculation and returns the result. The process is trivally similar for the circleArea(.)
function.
R Functions Exported by the Interface Level
To wrap up this discussion, let’s look at the functions an R user will have available after we build the package in RStudio (coming next in this series). Each of these functions will be exported from their respective C++ interface functions as regular R functions, namely:
rAdd(.)
rSortVec(.)
rProdLcmGcd(.)
squareArea(.)
circleArea(.)
The package user will not need to know or care that the core calculations are being performed in C++. Visually, we can represent the associations as shown in the following diagram:
The solid red line represents a “Chinese Wall” that separates our code base from the interface and allows us to maintain it as standard and reusable C++.
Summary
This concludes our example of configuring code that allows the use of standard reusable C++ in an R package, without having to modify it with any R or Rcpp-specific syntax. In the next post, we will examine how to actually build this code into an R package by leveraging the convenience of Rcpp and RStudio, and deploy it for any number of R users. The source code will also be made available so that you can try it out for yourself.
You may leave a comment below or discuss the post in the forum community.rstudio.com.