An R callable C++ Library Seed

Programming R C++ Library Architecture

Project Summary

An enormous amount of R packages exists, providing all sorts of functionality. At some point one might require additional features, available through external libraries, or performance of the program might be an issue. Especially in the case of speeding up the program C++ is a natural choice, as it easily interfaces with R. Integrating C++ code into an R project is relatively straightforward – the easiest way being the Rcpp package. I recommend having a look at the package’s vignettes if you are interest in further details on Rcpp and its capabilities.

What I would like to present here is a minimal working example of a C++ library callable from R. The use case is as follows: Calling functionality in the C++ library from R results in the creation of C++ objects on the program’s heap. Once the C++ call is finished, we may continue working in our R session running further C++ calls. Usually the objects of our previous C++ call will no longer be available. The program architecture presented here makes all objects created in C++ available to the R session via string handles.

Creating the R Project

In a first step we have to create a new R project. The project comes with some default test code which we later replace by our own C++ code. This is how creating an R package is done:

  1. Click File > New Project. The following menu opens. Choose New Directory.
    Screenshot of new project pop-up menu
    "New Project" pop-up menu.
  2. Next to start package creation select the R Package item.
    Screenshot of R package creation
    R package creation menu.
  3. In the final menu, choose Package w/ Rcpp in the upper left drop-down, specify a package name in the upper right text box (CppLibrary in my case) and make sure that the package location is correctly specified in the bottom text box.
    Screenshot of R package parameters
    R package creation details menu.
  4. Hit Create Project to finish. When being ask, allow installation of build tools, i.e. Rtools. The build tools come with all default compilers such as gcc/g++ in our case. Your result should look somewhat similar to the below screenshot.
    Screenshot of R package content
    R package content.
  5. To build the package – which is possible as a few default source were created – click Install and Restart in the Build tab.
  6. Remove all default files, i.e. R/hello.R and src/rcpp_hello.cpp as well as everything in the man subfolder. We will add our own sources soon.
  7. Finally we need to specify the C++ language standard explicitly. This is because we will make use of some more modern language features such as smart pointers. Therefore, add a file Makevars with the following content to the package’s src folder.
    
    PKG_CPPFLAGS = `$(R_HOME)/bin/Rscript -e "Rcpp:::CxxFlags()"`
    PKG_LIBS = `$(R_HOME)/bin/Rscript -e "Rcpp:::LdFlags()"`
    PKG_CXXFLAGS += -std=c++11
    
    							
    If you were to link an external C++ library, this is where your include and link compiler directives would go, e.g. when linking gsl add the following two lines to Makevars:
    
    PKG_LIBS += -I/usr/include
    PKG_LIBS += -L/usr/lib64 -lgsl -lgslcblas –lm
    
    							

The Object Factory

Let us introduce an object ObjectFactory by creating a source ObjectFactory.h with the below content in the package’s src subdirectory. The ObjectFactory will serve as both an instantiation helper and an object register to all classes which we want to be accessible multiple times from R. I will not go into all details but let me draw your attention to the following key aspects:

  1. Reference to instances by string handle: In order to be compatible with ObjectLibrary a class has to implement a constructor taking a string handle argument. This allows calling Object_Ptr get(const std::string& handle) which returns the respective instance of our target class if it exists and otherwise creates a new one.
  2. ObjectFactory as template class: We want to have an ObjectFactory for potentially arbitrary classes as long as they implement a handle constructor. Thus ObjectFactory is implemented as a template <class T>.
  3. Use of the singleton design pattern: ObjectFactory implements the so called singleton design pattern which ensures that at runtime all instances of class T will be handled by a single ObjectFactory. Therefore, the default constructor is made private and the copy constructor and assign operator are deleted. Instead, the ObjectFactory can be instantiated by calling getInstance(). For more details on the singleton design pattern specifically (Chapter 5) and design patterns in general I highly recommend checking "Head First: Design Patterns" by O’Reilly. Note that the book is Java based but an excellent read anyways.
  4. Map as object register: The member variable m_storage keeps track of all target class instances and makes them available through their respective string handle.
  5. getFactory as a simple wrapper: This is just for ease of notation.


#ifndef OBJECT_FACTORY_
#define OBJECT_FACTORY_

#include <map>
#include <memory>
#include <string>


namespace OBJ {


  template <class T>
  class ObjectFactory;


  template <class T>
  ObjectFactory<T>& getFactory() {
    return ObjectFactory<T>::getInstance();
  }


  template <class T>
  class ObjectFactory {
  public:
    typedef std::shared_ptr<T> Object_Ptr;

  private:
    std::map<std::string, Object_Ptr> m_storage;

    ObjectFactory() {}

  public:
    static ObjectFactory& getInstance() {
      static ObjectFactory instance;
      return instance;
    }

    ObjectFactory(ObjectFactory const&) = delete;
    void operator=(ObjectFactory const&) = delete;

    Object_Ptr get(const std::string& handle) {
      auto it = m_storage.find(handle);

      if (it == m_storage.end()) {
        Object_Ptr instance = std::make_shared<T>(handle);
        m_storage.emplace(handle, instance);
        return instance;
      } else {
        return it->second;
      }
    }

  };


}

#endif

				

A Simple Test

To test our simple library, create two files Test.h and testFunctions.cpp with the below code in the src subdirectory. Test.h introduces a class Incrementor which initialises a counter at 0 and allows to inc() that counter by one. The counter’s current value can be received by calling getCounter(). The second file testFunctions.cpp provides a simple function interface of Incrementor to R. Note that this is overly simplistic as all three R callable functions could be abstracted into a generic function interface.

Check the code of initIncrementor for instance to experience ObjectFactory in action. The string handle is passed from R and an instance of Incrementor is stored in ObjectFactory’s map m_storage. When later calling incIncrementor with the same handle the very same Incrementor instance is used to increment its counter. A short note on Rcpp: // [[Rcpp::export]] tells the C++ compiler to create an R callable version of the subsequent function, i.e. converting the return type (e.g. bool) and the function’s parameters (e.g. std::string) to something R can work with internally. In fact, that something is a so called SEXP which is short for S expression.


#ifndef TEST_
#define TEST_

#include <string>


namespace TST {


  class Incrementor {
  private:
    int m_counter;
    std::string m_handle;

  public:
    Incrementor(const std::string& handle) {
      m_counter = 0;
      m_handle = handle;
    }

    void inc() {
      ++m_counter;
    }

    int getCounter() {
      return m_counter;
    }
  };


}

#endif

				

#include "ObjectFactory.h"
#include "Test.h"

#include <Rcpp.h>

#include <memory>
#include <string>

using namespace Rcpp;


// [[Rcpp::export]]
bool initIncrementor(const std::string& handle) {
  auto i = OBJ::getFactory<TST::Incrementor>().get(handle);

  return (i ? true : false);
}


// [[Rcpp::export]]
bool incIncrementor(const std::string& handle) {
  auto i = OBJ::getFactory<TST::Incrementor>().get(handle);
  i->inc();

  return (i ? true : false);
}


// [[Rcpp::export]]
int getCounter(const std::string& handle) {
  return OBJ::getFactory<TST::Incrementor>().get(handle)->getCounter();
}

				

Finally, rebuild the package and execute the following R code line by line. This what the result should look like.


#
# test ObjectFactory
#

hdl <- "my_handle"

initIncrementor(hdl)

getCounter(hdl)

incIncrementor(hdl)

getCounter(hdl)

				
Screenshot of R package functionality test
R package functionality test successfull.