Skip to content

Commit 2a462a4

Browse files
authored
Merge pull request #127 from sn248/patch-1
Commit for the rcpp gallery article on Nullable
2 parents bf82704 + 8a4723a commit 2a462a4

File tree

1 file changed

+167
-0
lines changed

1 file changed

+167
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
---
2+
title: Nullable Optional Arguments in Rcpp functions
3+
author: Satyaprakash Nayak
4+
license: GPL (>= 2)
5+
tags: nullable
6+
summary: This post shows how to set datatypes as NULL and use them if not NULL
7+
---
8+
9+
```{r, echo=FALSE}
10+
knitr::opts_chunk$set(collapse=TRUE)
11+
options(width=80)
12+
```
13+
14+
### Introduction
15+
16+
Often we need to have optional arguments in `R` of `Rcpp` functions with default values. Sometimes,
17+
the default value for the optional parameters is set to be `NULL`. `Rcpp` provides the `Nullable <>`
18+
to set default value as to be `R_NilValue` (equivalent of `NULL` in `Rcpp`). There have been several
19+
StackOverflow [posts](https://stackoverflow.com/search?tab=relevance&q=rcpp%20Nullable) on using the
20+
`Nullable` behavior. As seen from quite a few posts, the _key step_ in using `Rcpp::Nullable<>` is
21+
to cast it to the underlying type first (i.e., instantiation) after checking that the input is not
22+
`NULL`.
23+
24+
### Nullability of Vector, Matrix or Logical Vector
25+
26+
```{Rcpp rcppchunk1}
27+
// Checking setting Vector, Matrix and LogicalVector to NULL by default and
28+
// using the input if not set to NULL
29+
#include <Rcpp.h>
30+
using namespace Rcpp;
31+
32+
// [[Rcpp::export]]
33+
void nullable1(Nullable<NumericVector> NV_ = R_NilValue,
34+
Nullable<NumericMatrix> NM_ = R_NilValue,
35+
Nullable<LogicalVector> LG_ = R_NilValue){
36+
37+
if (NV_.isNotNull()) {
38+
NumericVector NV(NV_); // casting to underlying type NumericVector
39+
Rcout << "Numeric Vector is set to not NULL." << std::endl;
40+
Rcout << NV << std::endl;
41+
} else if (NM_.isNotNull()){
42+
NumericMatrix NM(NM_); // casting to underlying type NumericMatrix
43+
Rcout << "Numeric Matrix is set to not NULL." << std::endl;
44+
Rcout << NM << std::endl;
45+
} else if (LG_.isNotNull()){
46+
LogicalVector LG(LG_); // casting to underlying type Boolean
47+
Rcout << "Logical Vector is set to not NULL." << std::endl;
48+
Rcout << LG << std::endl;
49+
} else {
50+
warning("All arguments are set to NULL.\n");
51+
}
52+
}
53+
```
54+
55+
Running a few examples with setting `NULL` for a vector, matrix or a boolean value gives us the
56+
expected results.
57+
58+
```{r}
59+
nullable1(c(1,2), NULL, NULL)
60+
m <- matrix(-0.5, 3, 3)
61+
nullable1(NULL, m, NULL)
62+
nullable1(NULL, NULL, FALSE)
63+
nullable1(NULL, NULL, NULL)
64+
nullable1(c(), NULL, NULL)
65+
```
66+
67+
We get the same result when the input to the `NumericVector` argument is not `NULL` but an empty
68+
vector, i.e., `c()`, which is also expected since `is.null(c())` is `TRUE` in `R`.
69+
70+
A stricter test whether the input is usable can be (aptly named) `isUsable()`.
71+
72+
```{Rcpp rcppchunk2}
73+
// Testing another check, isUsable for a Nullable Vector
74+
#include <Rcpp.h>
75+
using namespace Rcpp;
76+
77+
// [[Rcpp::export]]
78+
void nullable2(Nullable<NumericVector> NV_ = R_NilValue) {
79+
80+
if (NV_.isUsable()) {
81+
NumericVector NV(NV_); // casting to underlying type NumericVector
82+
Rcout << "Input is usable." << std::endl;
83+
Rcout << NV << std::endl;
84+
} else {
85+
Rcout << "Input is either NULL or not usable." << std::endl;
86+
}
87+
}
88+
```
89+
90+
### Nullability of DataFrame and List
91+
92+
`Rcpp::Nullable<>` works for `SEXP` based `Rcpp` types, so `Rcpp::DataFrame` and `Rcpp::List` can
93+
also be set to `Nullable` and instantiated if not `NULL`.
94+
95+
```{Rcpp rcppchunk3}
96+
// Checking setting List and DataFrame to NULL by default and
97+
// using the input if not set to NULL
98+
#include <Rcpp.h>
99+
using namespace Rcpp;
100+
101+
// [[Rcpp::export]]
102+
void nullable3(Nullable<List> ls_ = R_NilValue, Nullable<DataFrame> df_ = R_NilValue){
103+
104+
if (ls_.isNotNull()){
105+
Rcpp::List ls(ls_); // casting to underlying type List
106+
Rcout << "List is not NULL." << std::endl;
107+
Rcout << "List length of " << ls.length() << " elements." << std::endl;
108+
} else if(df_.isNotNull()) {
109+
Rcpp::DataFrame df(df_); // casting to underlying type DataFrame
110+
Rcout << "DataFrame is not NULL." << std::endl;
111+
Rcout << "DataFrame of " << df.nrows() << " rows and " << df.length() << " columns." << std::endl;
112+
} else {
113+
warning("Both inputs are NULL.\n");
114+
}
115+
}
116+
```
117+
118+
Testing with `Rcpp::List` and `Rcpp::DataFrame` gives expected results, i.e.,
119+
120+
```{r}
121+
mylist <- list(A = 1:10, B = letters[1:10])
122+
nullable3(mylist, NULL)
123+
df <- data.frame(A = 1:20, B = letters[1:20])
124+
nullable3(NULL, df)
125+
```
126+
127+
### Nullability of `RcppGSL::Matrix`
128+
129+
In addition to `Rcpp` types, `RcppGSL::Matrix` can also be set with `Nullable` type (e.g, in the
130+
`mvlabund`
131+
[package](https://github.com/aliceyiwang/mvabund/blob/99cb1ea8420b9d0f97ba68ec818c4751f20fb9a5/src/Rinterface.cpp#L22)):
132+
e.g.,
133+
134+
```{Rcpp rcppchunk4}
135+
// Checking setting RcppGSL Matrix to NULL by default and
136+
// using the input if not set to NULL
137+
// [[Rcpp::depends(RcppGSL)]]
138+
#include <RcppGSL.h>
139+
140+
using namespace Rcpp;
141+
142+
// [[Rcpp::export]]
143+
void nullable4(Rcpp::Nullable<RcppGSL::Matrix> M_ = R_NilValue) {
144+
145+
if (M_.isNotNull()){
146+
RcppGSL::Matrix M(M_); // casting to underlying type RcppGSL::Matrix
147+
Rcout << "Input is not NULL." << std::endl;
148+
Rcout << "Input GSL matrix has " << M.nrow() << " and " << M.ncol() << " columns.\n";
149+
} else {
150+
warning("Input GSL Matrix is NULL.\n");
151+
}
152+
}
153+
```
154+
155+
Finally, testing with `RcppGSL::Matrix` which can also be set to `Nullable<>`, i.e.,
156+
157+
```{r}
158+
nullable4(NULL) # testing with NULL
159+
m <- matrix(-0.5, 3, 3) # testing with a non-NULL matrix
160+
nullable4(m)
161+
```
162+
163+
### Summary
164+
165+
`Rcpp` provides a convenient construct to set datatypes to `NULL` using `R_NilValue` and application
166+
of the datatype if not set to `NULL` using the `.isNotNull()` check. This construct to applied to
167+
set datatypes to `NULL` as default values and possible simple simplification.

0 commit comments

Comments
 (0)