This document uses concrete examples to show how to develop in C++ using RStudio as an Integrated Development Environment (IDE).
In general, development of programs in compiled languages such as C++ greatly benefit from using IDEs. Known IDEs that can be used to develop in C++ are the open source project Eclipse and XCode on MacOs. On Windows Visual Studio is probably the most widely used IDE.
With the development of the R-addon package Rcpp, it has become a lot easier to connect C++ code with programs written in R. Given the fact that RStudio is the defacto standard-IDE for writting R-programs, it seams obvious, that we want to use RStudio to develop C++ code.
The aim of this post is to show how RStudio can be used to develop C++ code based on concrete examples.
In this first example, we assume that we start from scratch which means that we do not have any existing code. We are just given a problem and we want to solve that problem using a program that is written in C++.
For our first example, we are using the problem of computing the Fibonacci numbers. The Fibonacci sequence \(F_n\) is defined recursivly by
\[\begin{equation} F_n = F_{n-2} + F_{n-1} \label{eq:FibDef} \end{equation}\]with initial conditions \(F_0 = 0\) and \(F_1 = 1\).
The following R-function computes the number \(F_n\) using the definition given in the equation above. For the moment, we are just using the recursive computation shown below as an instructive example. We are not discussing the problem that many of the Fibonacci numbers are computed several times.
fibR <- function(n){
if (n == 0) return(0)
if (n == 1) return(1)
return(fibR(n-1) + fibR(n-2))
}
Using the above function, the first ten numbers of the Fibonacci sequence can easily be computed as
sapply(1:10, fibR, USE.NAMES = FALSE)
## [1] 1 1 2 3 5 8 13 21 34 55
The computation of \(F_n\) in C++ can be done at different levels. The easiest would be to write the analogous of the R-function fibR()
in C++ and call that function from R using packages inline
and Rcpp
. This solution is shown below
### # start with pure C/C++ function and assign the source code to
### # a variable which we call here includesrc
includesrc <- '
int fibonacci(const int x){
if (x == 0) return(0);
if (x == 1) return(1);
return fibonacci(x-1) + fibonacci(x-2);
}'
### # define the body of the C/C++ program
fibCppBody <- '
int x = Rcpp::as<int>(xs);
return Rcpp::wrap( fibonacci(x) );'
### # pass the above C/C++ function as an argument
### # to cxxfunction()
fibRcpp <- inline::cxxfunction(sig = signature(xs = "int"),
plugin = "Rcpp",
incl = includesrc,
body = fibCppBody)
The function fibRcpp()
is an ordinary R-function, but it uses a C/C++
-function to actually compute the value of \(F_n\). Again computing the first ten numbers of the Fibonacci sequence can be done with
sapply(1:10, fibRcpp, USE.NAMES = FALSE)
## [1] 1 1 2 3 5 8 13 21 34 55
From the simple computation of the first ten numbers of the Fibonacci sequence, we do not see any difference. But when it comes to computing times, there should be a difference. Hence we are comparing the difference between the two function fibR()
and fibRcpp()
for the first 30 numbers in the Fibonacci sequence.
system.time(sapply(1:30, fibR, USE.NAMES = FALSE))
## user system elapsed
## 6.886 0.034 6.945
system.time(sapply(1:30, fibRcpp, USE.NAMES = FALSE))
## user system elapsed
## 0.016 0.000 0.016
The advantage of this first solution is that it is easy to transfer existing functions from R to C/C++. But the C/C++ functions as such cannot be used outside R without the packages inline
and Rcpp
. Hence we would want to get to a solution that can also be used outside of R. But this requires a clearer separation of the C/C++ functions from the part that is done in R. This separation also requires more work and more infrastructure. RStudio can help us to provide some of the additional infrastructure that is needed. Furthermore, we also want to benefit from the R-packaging mechanism and from the R-package devtools
.
Besides the separation between the C/C++ code and the part implemented in R, we also want to come up with an object-oriented way of computing the number \(F_n\). This requires to define a class which includes the computation in one of its method. The advantage of using a class, becomes more apparent, when extending the simple computation with some helper-constructs that prevents us from computing most of the Fibonacci numbers more than once.
But first things first, let us get started with a simple C++-class that computes \(F_n\) inside one of its methods. This can easily be done by starting a new project in RStudio, as shown in the following screenshots.
In side that new project, we create a new class called Fibonacci
which has just one method int getFibnacciNumber(int n)
which returns the value for \(F_n\). The new class is declared in a header-file called Fibonacci.h
and implementated in a cpp-file called Fibonacci.cpp
. Header-files and cpp-files can easily be created using RStudios File
-menu such as shown in the screen shot below.
The step shown above creates an empty C++ file with some default text. Most of that text can be deleted and our own code can be inserted. For our example of computing the Fibonacci number \(F_n\), the content of the two files is shown below. We start with the header file.
## // Header file defining class Fibonacci
## //
## // The current version of the class has just one
## // method which computes the n-th Fibonacci number
## //
## #ifndef Fibonacci_h
## #define Fibonacci_h
## #include <stdio.h>
## #endif /* Fibonacci_h */
##
## class Fibonacci {
##
## public:
## int getFibonacciNumber(int n);
## };
The cpp-file contains the implementation of what was declared in the header file
## // Implementation of class Fibonacci
## //
## // The method int Fibonacci::getFibonacciNumber(int n)
## // computes the n-th Fibonacci number recursively.
## //
## #include "Fibonacci.h"
##
##
## int Fibonacci::getFibonacciNumber(int n){
## if (n == 0) return 0;
## if (n == 1) return 1;
## return getFibonacciNumber(n-2) + getFibonacciNumber(n-1);
## }
At this point we are almost done. All that remains to be done, is to write a small function that actually uses our class to do the computation. In a pure C++ project this would be the function int main()
. Here we can write an equivalent function. For our example we call that function in fibonacci_main()
and we save it in a separate file called fibonacci_main.cpp
. the content of this file is listed below.
## #include <Rcpp.h>
## using namespace Rcpp;
##
## #include "Fibonacci.h"
##
##
## // [[Rcpp::export]]
## int fibonacci_main(int x) {
## // declare object of class Fibonacci
## Fibonacci fib;
## return fib.getFibonacciNumber(x);
##
## }
##
##
## // You can include R code blocks in C++ files processed with sourceCpp
## // (useful for testing and development). The R code will be automatically
## // run after the compilation.
## //
##
## /*** R
## fibonacci_main(5)
## */
The above shown code now uses the functionality provided by the R-Package Rcpp
to connect the C++-code to R and makes it easy to run the C++-functionality from inside of R. The first two lines of fibonacci_main.cpp
contain the necessary include
-statements that are required by Rcpp. The C++ comment // [[Rcpp::export]]
is a Rcpp-attribute that denotes to Rcpp that the following function should be exported to be used from R.
Before, we can use the C++-functionality, we have to compile the C++-code. Usually, this can be quite painful, but here we can use all the infrastructure that is coming with the R/RStudio packaging and with the R-package devtools
. All we have to enter in our RStudio-project within which we have written all our C++-code is the following statement on the RStudio console
Once the compilation is done, we are ready to call the function fibonacci_main()
from the R-console, either to compute a single number or to get the complete sequence of numbers.