Skip to content

Instantly share code, notes, and snippets.

@schalkdaniel
Last active December 19, 2017 19:46
Show Gist options
  • Save schalkdaniel/71c054c01408e0803c6960e23341c408 to your computer and use it in GitHub Desktop.
Save schalkdaniel/71c054c01408e0803c6960e23341c408 to your computer and use it in GitHub Desktop.
Rcpp Gallery: Define custom printer for exposed C++ classes
// -*- mode: C++; c-indent-level: 4; c-basic-offset: 4; indent-tabs-mode: nil; -*-
/**
* @title Define a custom print method for exposed `C++` classes
* @author Daniel Schalk
* @license GPL (>= 2)
* @tags modules
* @summary How to define a custom print method for an exposed C++ class.
*
*
* While writing an `R` package which internally uses `C++` you might use
* [Rcpp Modules](http://dirk.eddelbuettel.com/code/rcpp/Rcpp-modules.pdf).
* After exposing a class to `R` it can be used to create
* new instances of that class.
*
* Therefore, lets take a look at the `Uniform` class of the Rcpp Modules vignette:
*/
// cf Rcpp Modules vignette
#include <Rcpp.h>
using namespace Rcpp;
class Uniform {
public:
Uniform(double min_, double max_) : min(min_), max(max_) {}
NumericVector draw(int n) const {
RNGScope scope;
return runif( n, min, max );
}
double min, max;
};
double uniformRange( Uniform* w) {
return w->max - w->min;
}
RCPP_MODULE(unif_module) {
class_<Uniform>( "Uniform" )
.constructor<double,double>()
.field( "min", &Uniform::min )
.field( "max", &Uniform::max )
.method( "draw", &Uniform::draw )
.method( "range", &uniformRange )
;
}
/**
*
* After sourcing the file the `Uniform` class can be used:
*
*/
/*** R
library(methods) ## needed for S4
## Create new instance myuniform:
myuniform <- new(Uniform, 0, 10)
# Print the new uniform isntance:
myuniform
*/
/**
*
* What happens now, is that the uniform instance calls its default printer.
* It would be nice if the printer of the instances can be customized
* to provide more information about the specific object.
*
* ## Customize the Printer
*
* Therefore, it is possible to make use of the underlying
* [S4 structure](http://adv-r.had.co.nz/OO-essentials.html#s4) of the exposed
* `C++` classes:
*/
/*** R
# Check exposed S4 structure:
isS4(myuniform)
# Get class name:
class(myuniform)
*/
/**
*
* For the `Uniform` class this is `Rcpp_Uniform`. To obtain a custom printer the
* last step now is to set the method `show` and define the function which should
* be used as printer:
*/
/*** R
# Define the printer:
ignoreMe <- setMethod("show", "Rcpp_Uniform", function (object) {
cat("\n Hi, I am an uniform object!\n")
cat("\n I was initialized with a minimum value of", object$min)
cat("\n and a maximum value of ", object$max, ".\n", sep = "")
cat("\n Therefore my range is ", object$range(), ".", sep = "")
cat("\n\n")
})
# Test the printer:
myuniform
*/
/**
*
* This works very nicely. Now it is possible to provide some more informations about
* a new `Uniform` instance. One thing to note is, that `setMethod` returns the
* method name as string. Anyway, this does not diminish the functionality.
*
* ## Use in Packages
*
* To get that printer as default printer after exposing the `C++` class to `R`
* within a package, it is sufficient to create a `R` file (e.g.
* `R/uniform_printer.R`) and put the following code in there:
*/
/*** R
ignoreMe <- setMethod("show", "Rcpp_Uniform", function (object) {
cat("\n Hi, I am an uniform object!\n")
cat("\n I was initialized with a minimum value of", object$min)
cat("\n and a maximum value of ", object$max, ".\n", sep = "")
cat("\n Therefore my range is ", object$range(), ".", sep = "")
cat("\n\n")
})
*/
---
title: Define custom printer for exposed `C++` classes
author: Daniel Schalk
license: GPL (>= 2)
tags: Rcpp Modules
summary: Motivation and demonstration of how to define a custom printer for exposed `C++` classes within `R`.
---
## The Problem
While writing a `R` package which internally uses `C++`, the probability of
using the
[Rcpp Modules](http://dirk.eddelbuettel.com/code/rcpp/Rcpp-modules.pdf) isn't
that low. After exposing a class and printing it to the console there appears
something like this:
```
C++ object <00000000137a2e90> of class 'CppClass' <0000000015256dc0>
```
To reproduce this, think of the following cpp file:
```{r engine='Rcpp'}
// ========================================================================== //
// Copy of the code from page 8 of:
// http://dirk.eddelbuettel.com/code/rcpp/Rcpp-modules.pdf
// ========================================================================== //
// BEGIN .cpp
#include <Rcpp.h>
using namespace Rcpp;
class Uniform {
public:
Uniform(double min_, double max_) : min(min_), max(max_) {}
NumericVector draw(int n) const {
RNGScope scope;
return runif( n, min, max );
}
double min, max;
};
double uniformRange( Uniform* w) {
return w->max - w->min;
}
RCPP_MODULE(unif_module) {
class_<Uniform>( "Uniform" )
.constructor<double,double>()
.field( "min", &Uniform::min )
.field( "max", &Uniform::max )
.method( "draw", &Uniform::draw )
.method( "range", &uniformRange )
;
}
// END .cpp
```
After sourcing the file the `Uniform` class can be used:
```{r}
# Create new instance uniform:
uniform = Uniform$new(0, 10)
# Print the new uniform isntance:
uniform
```
And there it is. It would be nice if the printer of the class can be customized
to provide more information about an specific instance.
## Customize the Printer
Therefore, it is possible to make use of the underlying
[S4 structure](http://adv-r.had.co.nz/OO-essentials.html#s4) of the exposed
`C++` classes:
```{r}
# Check exposed S4 structure:
isS4(uniform)
# Get class name:
class(uniform)
```
For the `Uniform` class this is `Rcpp_Uniform`. To obtain a custom printer the
last step now is to set the method `show` and define the function which should
be used as printer:
```{r}
# Define the printer:
setMethod("show", class(uniform), function (object) {
cat("\n Hi, I am an uniform object!\n")
cat("\n I was initialized with a minimum value of", object$min)
cat("\n and a maximum value of ", object$max, ".\n", sep = "")
cat("\n Therefore my range is ", object$range(), ".", sep = "")
cat("\n\n")
})
# Test the printer:
uniform
```
This works very nice. Now it is possible to provide some more informations about
a new `Uniform` instance. One thing to note is, that `setMethod` returns the
method name as string. Anyway, this does not diminish the functionality.
## Use in Packages
To get that printer as default printer after exposing the `C++` class to `R`
within a package, it is sufficient to create a `R` file (e.g.
`R/uniform_printer.R`) and put the following code in there:
```{r}
setMethod("show", "Rcpp_Uniform", function (object) {
cat("\n Hi, I am an uniform object!\n")
cat("\n I was initialized with a minimum value of", object$min)
cat("\n and a maximum value of ", object$max, ".\n", sep = "")
cat("\n Therefore my range is ", object$range(), ".", sep = "")
cat("\n\n")
})
```
Note that it is necessary to reference to the class explicitely by a string
(here `"Rcpp_Uniform"`).
One drawback is, that this will throw a warning while building the package
since there isn't a definition for that class while building the package. The
same warning occurs if one runs the `setMethod` function from above in front of
sourcing the cpp file.
Something to avoid this warning would be to set a class in front of `setMethod`,
which looks like this:
```{r}
setClass("Rcpp_Uniform")
setMethod("show", "Rcpp_Uniform", function (object) {
cat("\n Hi, I am an uniform object!\n")
cat("\n I was initialized with a minimum value of", object$min)
cat("\n and a maximum value of ", object$max, ".\n", sep = "")
cat("\n Therefore my range is ", object$range(), ".", sep = "")
cat("\n\n")
})
```
I honestly do not know how appropriate this is. But it works quite fine within a
package.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment