Skip to content

Commit 5439594

Browse files
committed
Merge pull request #246 from RcppCore/feature/xptr-improvements
Improvements to XPtr
2 parents 7ed45ef + b9ce780 commit 5439594

File tree

5 files changed

+101
-3
lines changed

5 files changed

+101
-3
lines changed

ChangeLog

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
2014-02-03 JJ Allaire <jj@rstudio.org>
2+
3+
* inst/include/Rcpp/XPtr.h: Improvements to XPtr including new
4+
checked_get and release functions and improved behavior (throw
5+
an exception rather than crash) when a NULL external pointer is
6+
dereferenced.
7+
* inst/unitTests/runit.XPTr.R: tests for XPtr improvements.
8+
* inst/unitTests/cpp/XPtr.cpp: tests for XPtr improvements.
9+
110
2014-02-03 JJ Allaire <jj@rstudio.org>
211

312
* R/Attributes.R: Include pkg_types.h file in RcppExports.cpp

inst/NEWS.Rd

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
\itemize{
99
\item Defining an error handler for tinyformat prevents \code{assert()}
1010
from spilling.
11+
\item Improvements to XPtr including new \code{checked_get} and
12+
\code{release} functions as well as improved behavior (throw an
13+
exception rather than crash) when a NULL external pointer is
14+
dereferenced.
1115
\item Evaluate R code within an \code{R_toplevelExec} block to prevent
1216
user interrupts from bypassing C++ destructors on the stack.
1317
}

inst/include/Rcpp/XPtr.h

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,28 +98,84 @@ class XPtr :
9898
return *this ;
9999
}
100100

101+
/**
102+
* Typesafe accessor for underlying pointer (use checked_get
103+
* if you want an exception thrown if the pointer is NULL)
104+
*/
105+
inline T* get() const {
106+
return (T*)(R_ExternalPtrAddr( Storage::get__() ));
107+
}
108+
109+
/**
110+
* Boolean operator wrapper for get() using the "safe bool idiom", see:
111+
* http://www.boost.org/doc/libs/1_57_0/boost/smart_ptr/detail/operator_bool.hpp
112+
*/
113+
typedef void (*unspecified_bool_type)();
114+
static void unspecified_bool_true() {}
115+
operator unspecified_bool_type() const
116+
{
117+
return get() == NULL ? 0 : unspecified_bool_true;
118+
}
119+
bool operator!() const
120+
{
121+
return get() == NULL;
122+
}
123+
124+
/**
125+
* Access underlying pointer throwing an exception if the ptr is NULL
126+
*/
127+
inline T* checked_get() const {
128+
T* ptr = get();
129+
if (ptr == NULL)
130+
throw ::Rcpp::exception("external pointer is not valid" ) ;
131+
return ptr;
132+
}
133+
101134
/**
102135
* Returns a reference to the object wrapped. This allows this
103136
* object to look and feel like a dumb pointer to T
104137
*/
105138
T& operator*() const {
106-
return *((T*)R_ExternalPtrAddr( Storage::get__() )) ;
139+
return *(checked_get()) ;
107140
}
108141

109142
/**
110143
* Returns the dumb pointer. This allows to call the -> operator
111144
* on this as if it was the dumb pointer
112145
*/
113146
T* operator->() const {
114-
return (T*)(R_ExternalPtrAddr( Storage::get__() ));
147+
return checked_get() ;
115148
}
116149

117150
void setDeleteFinalizer() {
118151
R_RegisterCFinalizerEx( Storage::get__(), finalizer_wrapper<T,Finalizer> , FALSE) ;
119152
}
120153

154+
/**
155+
* Release the external pointer (if any) immediately. This will cause
156+
* the pointer to be deleted and it's storage to be set to NULL.
157+
* After this call the get() method returns NULL and the checked_get()
158+
* method throws an exception.
159+
*
160+
* See the discussion here for the basic logic behind release:
161+
* https://stat.ethz.ch/pipermail/r-help/2007-August/137871.html
162+
*/
163+
void release() {
164+
165+
if (get() != NULL)
166+
{
167+
// Call the finalizer -- note that this implies that finalizers
168+
// need to be ready for a NULL external pointer value (our
169+
// default C++ finalizer is since delete NULL is a no-op).
170+
finalizer_wrapper<T,Finalizer>( Storage::get__() );
171+
172+
// Clear the external pointer
173+
R_ClearExternalPtr( Storage::get__() );
174+
}
175+
}
176+
121177
inline operator T*(){
122-
return (T*)( R_ExternalPtrAddr( Storage::get__() )) ;
178+
return checked_get() ;
123179
}
124180

125181
void update(SEXP){}

inst/unitTests/cpp/XPtr.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,24 @@ int xptr_2( XPtr< std::vector<int> > p){
4646
return p->front() ;
4747
}
4848

49+
// [[Rcpp::export]]
50+
bool xptr_release( XPtr< std::vector<int> > p) {
51+
p.release();
52+
return !p;
53+
}
54+
55+
// [[Rcpp::export]]
56+
bool xptr_access_released( XPtr< std::vector<int> > p) {
57+
58+
// double-release should be a no-op
59+
p.release();
60+
61+
// get should return NULL
62+
return p.get() == NULL;
63+
}
64+
65+
// [[Rcpp::export]]
66+
int xptr_use_released( XPtr< std::vector<int> > p ) {
67+
return p->front();
68+
}
69+

inst/unitTests/runit.XPTr.R

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ if (.runThisTest) {
3030

3131
front <- xptr_2(xp)
3232
checkEquals( front, 1L, msg = "check usage of external pointer" )
33+
34+
checkTrue(xptr_release(xp), msg = "check release of external pointer")
35+
36+
checkTrue(xptr_access_released(xp), msg = "check access of released external pointer")
37+
38+
checkException(xptr_use_released(xp),
39+
msg = "check exception on use of released external pointer",
40+
silent = TRUE)
3341
}
3442

3543
}

0 commit comments

Comments
 (0)