1 //
2 // XPtr.h: Rcpp R/C++ interface class library -- smart external pointers
3 //
4 // Copyright (C) 2009 - 2020 Dirk Eddelbuettel and Romain Francois
5 //
6 // This file is part of Rcpp.
7 //
8 // Rcpp is free software: you can redistribute it and/or modify it
9 // under the terms of the GNU General Public License as published by
10 // the Free Software Foundation, either version 2 of the License, or
11 // (at your option) any later version.
12 //
13 // Rcpp is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with Rcpp. If not, see <http://www.gnu.org/licenses/>.
20
21 #ifndef Rcpp_XPtr_h
22 #define Rcpp_XPtr_h
23
24 #include <RcppCommon.h>
25
26 namespace Rcpp {
27
28 template <typename T>
standard_delete_finalizer(T * obj)29 void standard_delete_finalizer(T* obj) { // #nocov start
30 delete obj;
31 }
32
33 template <typename T, void Finalizer(T*) >
finalizer_wrapper(SEXP p)34 void finalizer_wrapper(SEXP p) {
35 if (TYPEOF(p) != EXTPTRSXP)
36 return;
37
38 T* ptr = (T*) R_ExternalPtrAddr(p);
39 RCPP_DEBUG_3("finalizer_wrapper<%s>(SEXP p = <%p>). ptr = %p", DEMANGLE(T), p, ptr)
40
41 if (ptr == NULL)
42 return;
43
44 // Clear before finalizing to avoid behavior like access of freed memory
45 R_ClearExternalPtr(p);
46
47 Finalizer(ptr);
48 } // #nocov end
49
50 template <
51 typename T,
52 template <class> class StoragePolicy = PreserveStorage,
53 void Finalizer(T*) = standard_delete_finalizer<T>,
54 bool finalizeOnExit = false
55 >
56 class XPtr :
57 public StoragePolicy< XPtr<T,StoragePolicy, Finalizer, finalizeOnExit> >,
58 public SlotProxyPolicy< XPtr<T,StoragePolicy, Finalizer, finalizeOnExit> >,
59 public AttributeProxyPolicy< XPtr<T,StoragePolicy, Finalizer, finalizeOnExit> >,
60 public TagProxyPolicy< XPtr<T,StoragePolicy, Finalizer, finalizeOnExit> >,
61 public ProtectedProxyPolicy< XPtr<T,StoragePolicy, Finalizer, finalizeOnExit> >,
62 public RObjectMethods< XPtr<T,StoragePolicy, Finalizer, finalizeOnExit> >
63 {
64 public:
65
66 typedef StoragePolicy<XPtr> Storage;
67
68 /**
69 * constructs a XPtr wrapping the external pointer (EXTPTRSXP SEXP)
70 *
71 * @param xp external pointer to wrap
72 */
XPtr(SEXP x)73 explicit XPtr(SEXP x) { checked_set(x); };
74
75 /**
76 * constructs a XPtr wrapping the external pointer (EXTPTRSXP SEXP)
77 *
78 * @param xp external pointer to wrap
79 * @param tag tag to assign to external pointer
80 * @param prot protected data to assign to external pointer
81 */
XPtr(SEXP x,SEXP tag,SEXP prot)82 explicit XPtr(SEXP x, SEXP tag, SEXP prot) {
83 checked_set(x);
84 R_SetExternalPtrTag(x, tag);
85 R_SetExternalPtrProtected(x, prot);
86 };
87
88 /**
89 * creates a new external pointer wrapping the dumb pointer p.
90 *
91 * @param p dumb pointer to some object
92 * @param set_delete_finalizer if set to true, a finalizer will
93 * be registered for the external pointer. The finalizer
94 * is called when the xp is garbage collected. The finalizer
95 * is merely a call to the delete operator or the pointer
96 * so you need to make sure the pointer can be "delete" d
97 * this way (has to be a C++ object)
98 */
99 explicit XPtr(T* p, bool set_delete_finalizer = true,
100 SEXP tag = R_NilValue, SEXP prot = R_NilValue) {
101 RCPP_DEBUG_2("XPtr(T* p = <%p>, bool set_delete_finalizer = %s, SEXP tag = R_NilValue, SEXP prot = R_NilValue)", p, (set_delete_finalizer ? "true" : "false"))
102 Storage::set__(R_MakeExternalPtr((void*)p , tag, prot));
103 if (set_delete_finalizer) {
104 setDeleteFinalizer(); // #nocov
105 }
106 }
107
XPtr(const XPtr & other)108 XPtr(const XPtr& other) {
109 Storage::copy__(other);
110 }
111
112 XPtr& operator=(const XPtr& other) {
113 Storage::copy__(other);
114 return *this;
115 }
116
117 /**
118 * Typesafe accessor for underlying pointer (use checked_get
119 * if you want an exception thrown if the pointer is NULL)
120 */
get()121 inline T* get() const {
122 return (T*)(R_ExternalPtrAddr(Storage::get__()));
123 }
124
125 /**
126 * Boolean operator wrapper for get() using the "safe bool idiom", see:
127 * http://www.boost.org/doc/libs/1_57_0/boost/smart_ptr/detail/operator_bool.hpp
128 */
129 typedef void (*unspecified_bool_type)();
unspecified_bool_true()130 static void unspecified_bool_true() {}
unspecified_bool_type()131 operator unspecified_bool_type() const {
132 return get() == NULL ? 0 : unspecified_bool_true;
133 }
134 bool operator!() const {
135 return get() == NULL;
136 }
137
138 /**
139 * Access underlying pointer throwing an exception if the ptr is NULL
140 */
checked_get()141 inline T* checked_get() const {
142 T* ptr = get();
143 if (ptr == NULL)
144 throw ::Rcpp::exception("external pointer is not valid"); // #nocov
145 return ptr;
146 }
147
148 /**
149 * Returns a reference to the object wrapped. This allows this
150 * object to look and feel like a dumb pointer to T
151 */
152 T& operator*() const {
153 return *(checked_get());
154 }
155
156 /**
157 * Returns the dumb pointer. This allows to call the -> operator
158 * on this as if it was the dumb pointer
159 */
160 T* operator->() const {
161 return checked_get();
162 }
163
setDeleteFinalizer()164 void setDeleteFinalizer() { // #nocov start
165 R_RegisterCFinalizerEx(Storage::get__(), finalizer_wrapper<T,Finalizer>,
166 (Rboolean) finalizeOnExit);
167 } // #nocov end
168
169 /**
170 * Release the external pointer (if any) immediately. This will cause
171 * the pointer to be deleted and it's storage to be set to NULL.
172 * After this call the get() method returns NULL and the checked_get()
173 * method throws an exception.
174 *
175 * See the discussion here for the basic logic behind release:
176 * https://stat.ethz.ch/pipermail/r-help/2007-August/137871.html
177 */
release()178 void release() {
179
180 if (get() != NULL) {
181 // Call the finalizer -- note that this implies that finalizers
182 // need to be ready for a NULL external pointer value (our
183 // default C++ finalizer is since delete NULL is a no-op).
184 // This clears the external pointer just before calling the finalizer,
185 // to avoid interesting behavior with co-dependent finalizers.
186 finalizer_wrapper<T,Finalizer>(Storage::get__());
187 }
188 }
189
190 inline operator T*() {
191 return checked_get();
192 }
193
update(SEXP)194 void update(SEXP) {}
195
196 private:
checked_set(SEXP x)197 inline void checked_set(SEXP x) {
198 if (TYPEOF(x) != EXTPTRSXP) {
199 const char* fmt = "Expecting an external pointer: [type=%s]."; // #nocov
200 throw ::Rcpp::not_compatible(fmt, Rf_type2char(TYPEOF(x))); // #nocov
201 }
202 Storage::set__(x);
203 }
204
205 };
206
207 } // namespace Rcpp
208
209 #endif
210