Skip to content

Commit c3686a2

Browse files
committed
Merge pull request #368 from RcppCore/feature/nullable_issue363
Nullable object (closes #363)
2 parents f1953ac + 2807d72 commit c3686a2

File tree

4 files changed

+221
-33
lines changed

4 files changed

+221
-33
lines changed

inst/include/Rcpp.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@
6464
#include <Rcpp/Module.h>
6565
#include <Rcpp/InternalFunction.h>
6666

67+
#include <Rcpp/Nullable.h>
68+
6769
#ifndef RCPP_NO_SUGAR
6870
#include <Rcpp/sugar/sugar.h>
6971
#include <Rcpp/stats/stats.h>

inst/include/Rcpp/Nullable.h

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// -*- mode: C++; c-indent-level: 4; c-basic-offset: 4; indent-tabs-mode: nil; -*-
2+
//
3+
// Nullable.h: Rcpp R/C++ interface class library -- SEXP container which can be NULL
4+
//
5+
// Copyright (C) 2015 Dirk Eddelbuettel and Daniel C. Dillon
6+
//
7+
// This file is part of Rcpp.
8+
//
9+
// Rcpp is free software: you can redistribute it and/or modify it
10+
// under the terms of the GNU General Public License as published by
11+
// the Free Software Foundation, either version 2 of the License, or
12+
// (at your option) any later version.
13+
//
14+
// Rcpp is distributed in the hope that it will be useful, but
15+
// WITHOUT ANY WARRANTY; without even the implied warranty of
16+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
// GNU General Public License for more details.
18+
//
19+
// You should have received a copy of the GNU General Public License
20+
// along with Rcpp. If not, see <http://www.gnu.org/licenses/>.
21+
22+
#ifndef Rcpp_Nullable_h
23+
#define Rcpp_Nullable_h
24+
25+
// This class could possibly be written in a templated manner too, and we looked
26+
// into this. However, as an exception is thrown as soon as an actual proxy
27+
// object is accessed _when it was initialized with NULL_ we found no
28+
// satisfactory solution.
29+
//
30+
// We looked into the safe_bool_idiom [1] but found that more trouble than is
31+
// warranted here. We first and foremost want an operator SEXP() which got in
32+
// the way of redefining operator bool.
33+
// [1] http://www.artima.com/cppsource/safebool.html)
34+
35+
namespace Rcpp {
36+
37+
template<class T>
38+
class Nullable {
39+
private:
40+
template<class U>
41+
friend class InputParameter;
42+
43+
template<class U>
44+
friend class traits::Exporter;
45+
46+
public:
47+
48+
/**
49+
* Empty no-argument constructor of a Nullable object
50+
*
51+
* Assigns (R's) NULL value, and sets validator to FALSE
52+
*/
53+
inline Nullable() : m_sexp(R_NilValue), m_set(false) {}
54+
55+
/**
56+
* Template constructor of a Nullable object
57+
*
58+
* Assigns object, and set validator to TRUE
59+
*/
60+
61+
inline Nullable(const T &t) : m_sexp(t), m_set(true) {}
62+
63+
protected:
64+
65+
/**
66+
* Standard constructor of a Nullable object
67+
*
68+
* @param SEXP is stored
69+
*/
70+
inline Nullable(SEXP t) {
71+
m_sexp = t;
72+
m_set = true;
73+
}
74+
75+
public:
76+
77+
/**
78+
* Copy constructor for Nullable object
79+
*
80+
* @param SEXP is used to update internal copy
81+
*/
82+
inline Nullable &operator=(SEXP sexp) {
83+
m_sexp = sexp;
84+
m_set = true;
85+
return *this;
86+
}
87+
88+
/**
89+
* operator SEXP() to return nullable object
90+
*
91+
* @throw 'not initialized' if object has not been set
92+
*/
93+
inline operator SEXP() {
94+
checkIfSet();
95+
return m_sexp;
96+
}
97+
98+
/**
99+
* get() accessor for object
100+
*
101+
* @throw 'not initialized' if object has not been set
102+
*/
103+
inline SEXP get() {
104+
checkIfSet();
105+
return m_sexp;
106+
}
107+
108+
/**
109+
* Boolean test for NULL
110+
*
111+
* @throw 'not initialized' if object has not been set
112+
*/
113+
inline bool isNull() const {
114+
checkIfSet();
115+
return Rf_isNull(m_sexp);
116+
}
117+
118+
/**
119+
* Boolean test for not NULL
120+
*
121+
* @throw 'not initialized' if object has not been set
122+
*/
123+
inline bool isNotNull() const {
124+
return ! isNull();
125+
}
126+
127+
/**
128+
* Test function to check if object has been initialized
129+
*
130+
*/
131+
inline bool isSet(void) const { return m_set; }
132+
133+
private:
134+
SEXP m_sexp;
135+
bool m_set;
136+
137+
inline void checkIfSet(void) const {
138+
if (!m_set) {
139+
throw ::Rcpp::exception("Not initialized");
140+
}
141+
}
142+
};
143+
}
144+
145+
#endif

inst/unitTests/cpp/misc.cpp

Lines changed: 52 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
// -*- mode: C++; c-indent-level: 4; c-basic-offset: 4; tab-width: 8 -*-
1+
// -*- mode: C++; c-indent-level: 4; c-basic-offset: 4; indent-tabs-mode: nil; -*-
22
//
33
// misc.cpp: Rcpp R/C++ interface class library -- misc unit tests
44
//
5-
// Copyright (C) 2013 Dirk Eddelbuettel and Romain Francois
5+
// Copyright (C) 2013 - 2015 Dirk Eddelbuettel and Romain Francois
66
//
77
// This file is part of Rcpp.
88
//
@@ -20,7 +20,7 @@
2020
// along with Rcpp. If not, see <http://www.gnu.org/licenses/>.
2121

2222
#include <Rcpp.h>
23-
using namespace Rcpp ;
23+
using namespace Rcpp;
2424
using namespace std;
2525
#include <iostream>
2626
#include <fstream>
@@ -34,47 +34,47 @@ class simple {
3434
};
3535

3636
// [[Rcpp::export]]
37-
SEXP symbol_(){
37+
SEXP symbol_() {
3838
return LogicalVector::create(
3939
Symbol( Rf_install("foobar") ) == Rf_install("foobar"),
4040
Symbol( Rf_mkChar("foobar") ) == Rf_install("foobar"),
4141
Symbol( Rf_mkString("foobar") ) == Rf_install("foobar"),
4242
Symbol( "foobar" ) == Rf_install("foobar")
43-
) ;
43+
);
4444
}
4545

4646
// [[Rcpp::export]]
47-
Symbol symbol_ctor(SEXP x){ return Symbol(x); }
47+
Symbol symbol_ctor(SEXP x) { return Symbol(x); }
4848

4949
// [[Rcpp::export]]
50-
List Argument_(){
50+
List Argument_() {
5151
Argument x("x"), y("y");
5252
return List::create( x = 2, y = 3 );
5353
}
5454

5555
// [[Rcpp::export]]
56-
int Dimension_const( SEXP ia ){
56+
int Dimension_const( SEXP ia ) {
5757
simple ss(ia);
58-
return ss.nrow();
58+
return ss.nrow();
5959
}
6060

6161
// [[Rcpp::export]]
62-
SEXP evaluator_error(){
63-
return Rcpp_eval( Rf_lang2( Rf_install("stop"), Rf_mkString( "boom" ) ) ) ;
62+
SEXP evaluator_error() {
63+
return Rcpp_eval( Rf_lang2( Rf_install("stop"), Rf_mkString( "boom" ) ) );
6464
}
6565

6666
// [[Rcpp::export]]
67-
SEXP evaluator_ok(SEXP x){
68-
return Rcpp_eval( Rf_lang2( Rf_install("sample"), x ) ) ;
67+
SEXP evaluator_ok(SEXP x) {
68+
return Rcpp_eval( Rf_lang2( Rf_install("sample"), x ) );
6969
}
7070

7171
// [[Rcpp::export]]
72-
void exceptions_(){
73-
throw std::range_error("boom") ;
72+
void exceptions_() {
73+
throw std::range_error("boom");
7474
}
7575

7676
// [[Rcpp::export]]
77-
LogicalVector has_iterator_( ){
77+
LogicalVector has_iterator_( ) {
7878
return LogicalVector::create(
7979
(bool)Rcpp::traits::has_iterator< std::vector<int> >::value,
8080
(bool)Rcpp::traits::has_iterator< std::list<int> >::value,
@@ -87,7 +87,7 @@ LogicalVector has_iterator_( ){
8787
}
8888

8989
// [[Rcpp::export]]
90-
void test_rcout(std::string tfile, std::string teststring){
90+
void test_rcout(std::string tfile, std::string teststring) {
9191
// define and open testfile
9292
std::ofstream testfile(tfile.c_str());
9393

@@ -108,8 +108,8 @@ void test_rcout(std::string tfile, std::string teststring){
108108
}
109109

110110
// [[Rcpp::export]]
111-
LogicalVector na_proxy(){
112-
CharacterVector s("foo") ;
111+
LogicalVector na_proxy() {
112+
CharacterVector s("foo");
113113
return LogicalVector::create(
114114
NA_REAL == NA,
115115
NA_INTEGER == NA,
@@ -130,29 +130,49 @@ LogicalVector na_proxy(){
130130
NA == 12 ,
131131
NA == "foo",
132132
NA == s[0]
133-
) ;
133+
);
134134
}
135135

136136
// [[Rcpp::export]]
137-
StretchyList stretchy_list(){
138-
StretchyList out ;
139-
out.push_back( 1 ) ;
140-
out.push_front( "foo" ) ;
141-
out.push_back( 3.2 ) ;
137+
StretchyList stretchy_list() {
138+
StretchyList out;
139+
out.push_back( 1 );
140+
out.push_front( "foo" );
141+
out.push_back( 3.2 );
142142
return out;
143143
}
144144

145145
// [[Rcpp::export]]
146-
StretchyList named_stretchy_list(){
147-
StretchyList out ;
148-
out.push_back( _["b"] = 1 ) ;
149-
out.push_front( _["a"] = "foo" ) ;
150-
out.push_back( _["c"] = 3.2 ) ;
146+
StretchyList named_stretchy_list() {
147+
StretchyList out;
148+
out.push_back( _["b"] = 1 );
149+
out.push_front( _["a"] = "foo" );
150+
out.push_back( _["c"] = 3.2 );
151151
return out;
152152
}
153153

154154
// [[Rcpp::export]]
155-
void test_stop_variadic(){
156-
stop( "%s %d", "foo", 3 ) ;
155+
void test_stop_variadic() {
156+
stop( "%s %d", "foo", 3 );
157+
}
158+
159+
// [[Rcpp::export]]
160+
bool testNullableForNull(Nullable<NumericMatrix> M) {
161+
return M.isNull();
162+
}
163+
164+
// [[Rcpp::export]]
165+
bool testNullableForNotNull(Nullable<NumericMatrix> M) {
166+
return M.isNotNull();
167+
}
168+
169+
// [[Rcpp::export]]
170+
SEXP testNullableOperator(Nullable<NumericMatrix> M) {
171+
return M;
172+
}
173+
174+
// [[Rcpp::export]]
175+
SEXP testNullableGet(Nullable<NumericMatrix> M) {
176+
return M.get();
157177
}
158178

inst/unitTests/runit.misc.R

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/r -t
22
#
3-
# Copyright (C) 2010 - 2014 Dirk Eddelbuettel and Romain Francois
3+
# Copyright (C) 2010 - 2015 Dirk Eddelbuettel and Romain Francois
44
#
55
# This file is part of Rcpp.
66
#
@@ -147,4 +147,25 @@ if (.runThisTest) {
147147
checkEquals( m, "foo 3" )
148148
}
149149

150+
test.NullableForNull <- function() {
151+
M <- matrix(1:4, 2, 2)
152+
checkTrue( testNullableForNull(NULL) )
153+
checkTrue( ! testNullableForNull(M) )
154+
}
155+
156+
test.NullableForNotNull <- function() {
157+
M <- matrix(1:4, 2, 2)
158+
checkTrue( ! testNullableForNotNull(NULL) )
159+
checkTrue( testNullableForNotNull(M) )
160+
}
161+
162+
test.NullableAccessOperator <- function() {
163+
M <- matrix(1:4, 2, 2)
164+
checkEquals( testNullableOperator(M), M )
165+
}
166+
167+
test.NullableAccessGet <- function() {
168+
M <- matrix(1:4, 2, 2)
169+
checkEquals( testNullableGet(M), M )
170+
}
150171
}

0 commit comments

Comments
 (0)