Skip to content

Commit 43b1937

Browse files
committed
finalising 2020-05-18 post
1 parent 2a462a4 commit 43b1937

File tree

2 files changed

+271
-0
lines changed

2 files changed

+271
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
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+
layout: post
8+
src: 2020-05-18-optional-null-function-arguments.Rmd
9+
---
10+
11+
12+
13+
### Introduction
14+
15+
Often we need to have optional arguments in `R` of `Rcpp` functions with default values. Sometimes,
16+
the default value for the optional parameters is set to be `NULL`. `Rcpp` provides the `Nullable <>`
17+
to set default value as to be `R_NilValue` (equivalent of `NULL` in `Rcpp`). There have been several
18+
StackOverflow [posts](https://stackoverflow.com/search?tab=relevance&q=rcpp%20Nullable) on using the
19+
`Nullable` behavior. As seen from quite a few posts, the _key step_ in using `Rcpp::Nullable<>` is
20+
to cast it to the underlying type first (i.e., instantiation) after checking that the input is not
21+
`NULL`.
22+
23+
### Nullability of Vector, Matrix or Logical Vector
24+
25+
26+
{% highlight cpp %}
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+
{% endhighlight %}
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+
59+
{% highlight r %}
60+
nullable1(c(1,2), NULL, NULL)
61+
{% endhighlight %}
62+
63+
64+
65+
<pre class="output">
66+
Numeric Vector is set to not NULL.
67+
1 2
68+
</pre>
69+
70+
71+
72+
{% highlight r %}
73+
m <- matrix(-0.5, 3, 3)
74+
nullable1(NULL, m, NULL)
75+
{% endhighlight %}
76+
77+
78+
79+
<pre class="output">
80+
Numeric Matrix is set to not NULL.
81+
-0.500000 -0.500000 -0.500000
82+
-0.500000 -0.500000 -0.500000
83+
-0.500000 -0.500000 -0.500000
84+
</pre>
85+
86+
87+
88+
{% highlight r %}
89+
nullable1(NULL, NULL, FALSE)
90+
{% endhighlight %}
91+
92+
93+
94+
<pre class="output">
95+
Logical Vector is set to not NULL.
96+
0
97+
</pre>
98+
99+
100+
101+
{% highlight r %}
102+
nullable1(NULL, NULL, NULL)
103+
{% endhighlight %}
104+
105+
106+
107+
<pre class="output">
108+
Warning in nullable1(NULL, NULL, NULL): All arguments are set to NULL.
109+
</pre>
110+
111+
112+
113+
{% highlight r %}
114+
nullable1(c(), NULL, NULL)
115+
{% endhighlight %}
116+
117+
118+
119+
<pre class="output">
120+
Warning in nullable1(c(), NULL, NULL): All arguments are set to NULL.
121+
</pre>
122+
123+
We get the same result when the input to the `NumericVector` argument is not `NULL` but an empty
124+
vector, i.e., `c()`, which is also expected since `is.null(c())` is `TRUE` in `R`.
125+
126+
A stricter test whether the input is usable can be (aptly named) `isUsable()`.
127+
128+
129+
{% highlight cpp %}
130+
// Testing another check, isUsable for a Nullable Vector
131+
#include <Rcpp.h>
132+
using namespace Rcpp;
133+
134+
// [[Rcpp::export]]
135+
void nullable2(Nullable<NumericVector> NV_ = R_NilValue) {
136+
137+
if (NV_.isUsable()) {
138+
NumericVector NV(NV_); // casting to underlying type NumericVector
139+
Rcout << "Input is usable." << std::endl;
140+
Rcout << NV << std::endl;
141+
} else {
142+
Rcout << "Input is either NULL or not usable." << std::endl;
143+
}
144+
}
145+
{% endhighlight %}
146+
147+
### Nullability of DataFrame and List
148+
149+
`Rcpp::Nullable<>` works for `SEXP` based `Rcpp` types, so `Rcpp::DataFrame` and `Rcpp::List` can
150+
also be set to `Nullable` and instantiated if not `NULL`.
151+
152+
153+
{% highlight cpp %}
154+
// Checking setting List and DataFrame to NULL by default and
155+
// using the input if not set to NULL
156+
#include <Rcpp.h>
157+
using namespace Rcpp;
158+
159+
// [[Rcpp::export]]
160+
void nullable3(Nullable<List> ls_ = R_NilValue, Nullable<DataFrame> df_ = R_NilValue){
161+
162+
if (ls_.isNotNull()){
163+
Rcpp::List ls(ls_); // casting to underlying type List
164+
Rcout << "List is not NULL." << std::endl;
165+
Rcout << "List length of " << ls.length() << " elements." << std::endl;
166+
} else if(df_.isNotNull()) {
167+
Rcpp::DataFrame df(df_); // casting to underlying type DataFrame
168+
Rcout << "DataFrame is not NULL." << std::endl;
169+
Rcout << "DataFrame of " << df.nrows() << " rows and " << df.length() << " columns." << std::endl;
170+
} else {
171+
warning("Both inputs are NULL.\n");
172+
}
173+
}
174+
{% endhighlight %}
175+
176+
Testing with `Rcpp::List` and `Rcpp::DataFrame` gives expected results, i.e.,
177+
178+
179+
{% highlight r %}
180+
mylist <- list(A = 1:10, B = letters[1:10])
181+
nullable3(mylist, NULL)
182+
{% endhighlight %}
183+
184+
185+
186+
<pre class="output">
187+
List is not NULL.
188+
List length of 2 elements.
189+
</pre>
190+
191+
192+
193+
{% highlight r %}
194+
df <- data.frame(A = 1:20, B = letters[1:20])
195+
nullable3(NULL, df)
196+
{% endhighlight %}
197+
198+
199+
200+
<pre class="output">
201+
DataFrame is not NULL.
202+
DataFrame of 20 rows and 2 columns.
203+
</pre>
204+
205+
### Nullability of `RcppGSL::Matrix`
206+
207+
In addition to `Rcpp` types, `RcppGSL::Matrix` can also be set with `Nullable` type (e.g, in the
208+
`mvlabund`
209+
[package](https://github.com/aliceyiwang/mvabund/blob/99cb1ea8420b9d0f97ba68ec818c4751f20fb9a5/src/Rinterface.cpp#L22)):
210+
e.g.,
211+
212+
213+
{% highlight cpp %}
214+
// Checking setting RcppGSL Matrix to NULL by default and
215+
// using the input if not set to NULL
216+
// [[Rcpp::depends(RcppGSL)]]
217+
#include <RcppGSL.h>
218+
219+
using namespace Rcpp;
220+
221+
// [[Rcpp::export]]
222+
void nullable4(Rcpp::Nullable<RcppGSL::Matrix> M_ = R_NilValue) {
223+
224+
if (M_.isNotNull()){
225+
RcppGSL::Matrix M(M_); // casting to underlying type RcppGSL::Matrix
226+
Rcout << "Input is not NULL." << std::endl;
227+
Rcout << "Input GSL matrix has " << M.nrow() << " and " << M.ncol() << " columns.\n";
228+
} else {
229+
warning("Input GSL Matrix is NULL.\n");
230+
}
231+
}
232+
{% endhighlight %}
233+
234+
Finally, testing with `RcppGSL::Matrix` which can also be set to `Nullable<>`, i.e.,
235+
236+
237+
{% highlight r %}
238+
nullable4(NULL) # testing with NULL
239+
{% endhighlight %}
240+
241+
242+
243+
<pre class="output">
244+
Warning in nullable4(NULL): Input GSL Matrix is NULL.
245+
</pre>
246+
247+
248+
249+
{% highlight r %}
250+
m <- matrix(-0.5, 3, 3) # testing with a non-NULL matrix
251+
nullable4(m)
252+
{% endhighlight %}
253+
254+
255+
256+
<pre class="output">
257+
Input is not NULL.
258+
Input GSL matrix has 3 and 3 columns.
259+
</pre>
260+
261+
### Summary
262+
263+
`Rcpp` provides a convenient construct to set datatypes to `NULL` using `R_NilValue` and application
264+
of the datatype if not set to `NULL` using the `.isNotNull()` check. This construct to applied to
265+
set datatypes to `NULL` as default values and possible simple simplification.

tags/nullable/index.html

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
layout: tag
3+
title: nullable
4+
---
5+
6+
{% include tag_page.html %}

0 commit comments

Comments
 (0)