Last active
December 19, 2017 19:46
-
-
Save schalkdaniel/71c054c01408e0803c6960e23341c408 to your computer and use it in GitHub Desktop.
Rcpp Gallery: Define custom printer for exposed C++ classes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// -*- 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") | |
}) | |
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- | |
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