Rcpp Version 1.1.2
Loading...
Searching...
No Matches
exceptions.h
Go to the documentation of this file.
1
2// exceptions.h: Rcpp R/C++ interface class library -- exceptions
3//
4// Copyright (C) 2010 - 2020 Dirk Eddelbuettel and Romain Francois
5// Copyright (C) 2021 - 2024 Dirk Eddelbuettel, Romain Francois and Iñaki Ucar
6// Copyright (C) 2025 - 2026 Dirk Eddelbuettel, Romain Francois, Iñaki Ucar and James J Balamuta
7//
8// This file is part of Rcpp.
9//
10// Rcpp is free software: you can redistribute it and/or modify it
11// under the terms of the GNU General Public License as published by
12// the Free Software Foundation, either version 2 of the License, or
13// (at your option) any later version.
14//
15// Rcpp is distributed in the hope that it will be useful, but
16// WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18// GNU General Public License for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with Rcpp. If not, see <http://www.gnu.org/licenses/>.
22
23#ifndef Rcpp__exceptions__h
24#define Rcpp__exceptions__h
25
26#include <Rversion.h>
27#include <cstdio>
28
29#ifndef RCPP_DEFAULT_INCLUDE_CALL
30#define RCPP_DEFAULT_INCLUDE_CALL true
31#endif
32
33#define GET_STACKTRACE() R_NilValue
34
35namespace Rcpp {
36
37 // Throwing an exception must be thread-safe to avoid surprises w/ OpenMP.
38 class exception : public std::exception {
39 public:
40 explicit exception(const char* message_, bool include_call = RCPP_DEFAULT_INCLUDE_CALL) : // #nocov start
41 message(message_),
44 }
45 exception(const char* message_, const char*, int, bool include_call = RCPP_DEFAULT_INCLUDE_CALL) :
46 message(message_),
49 }
50 bool include_call() const {
51 return include_call_;
52 }
53 virtual ~exception() throw() {}
54 virtual const char* what() const throw() {
55 return message.c_str(); // #nocov end
56 }
57 inline void copy_stack_trace_to_r() const;
58 private:
59 std::string message;
61 std::vector<std::string> stack;
62 inline void record_stack_trace();
63 };
64
65 // simple helper
66 static std::string toString(const int i) { // #nocov start
67 std::ostringstream ostr;
68 ostr << i;
69 return ostr.str(); // #nocov end
70 }
71
72 class no_such_env : public std::exception {
73 public:
74 no_such_env(const std::string& name) throw() :
75 message(std::string("no such environment: '") + name + "'") {};
76 no_such_env(int pos) throw() :
77 message("no environment in given position '" + toString(pos) + "'") {};
78 virtual ~no_such_env() throw() {};
79 virtual const char* what() const throw() { return message.c_str(); };
80 private:
81 std::string message;
82 };
83
84 class file_io_error : public std::exception {
85 public:
86 file_io_error(const std::string& file) throw() : // #nocov start
87 message(std::string("file io error: '") + file + "'"), file(file) {};
88 file_io_error(int code, const std::string& file) throw() :
89 message("file io error " + toString(code) + ": '" + file + "'"), file(file) {};
90 file_io_error(const std::string& msg, const std::string& file) throw() :
91 message(msg + ": '" + file + "'"), file(file) {};
92 virtual ~file_io_error() throw() {};
93 virtual const char* what() const throw() { return message.c_str(); };
94 std::string filePath() const throw() { return file; }; // #nocov end
95 private:
96 std::string message;
97 std::string file;
98 } ;
99
100 class file_not_found : public file_io_error { // #nocov start
101 public:
102 file_not_found(const std::string& file) throw() :
103 file_io_error("file not found", file) {} // #nocov end
104 };
105
106 class file_exists : public file_io_error { // #nocov start
107 public:
108 file_exists(const std::string& file) throw() :
109 file_io_error("file already exists", file) {} // #nocov end
110 };
111
112 // Variadic / code generated version of the warning and stop functions
113 // can be found within the respective C++11 or C++98 exceptions.h
114 // included below.
115 inline void warning(const std::string& message) { // #nocov start
116 ::Rf_warning("%s", message.c_str());
117 } // #nocov end
118
119 inline void NORET stop(const std::string& message) { // #nocov start
120 throw Rcpp::exception(message.c_str());
121 } // #nocov end
122
123} // namespace Rcpp
124
125
126namespace Rcpp { namespace internal {
127
128inline SEXP longjumpSentinel(SEXP token) {
129 SEXP sentinel = PROTECT(Rf_allocVector(VECSXP, 1));
130 SET_VECTOR_ELT(sentinel, 0, token);
131
132 SEXP sentinelClass = PROTECT(Rf_mkString("Rcpp:longjumpSentinel"));
133 Rf_setAttrib(sentinel, R_ClassSymbol, sentinelClass) ;
134
135 UNPROTECT(2);
136 return sentinel;
137}
138
139inline bool isLongjumpSentinel(SEXP x) { // #nocov start
140 return
141 Rf_inherits(x, "Rcpp:longjumpSentinel") &&
142 TYPEOF(x) == VECSXP &&
143 Rf_length(x) == 1;
144}
145
146inline SEXP getLongjumpToken(SEXP sentinel) {
147 return VECTOR_ELT(sentinel, 0);
148}
149
150inline void resumeJump(SEXP token) {
151 if (isLongjumpSentinel(token)) {
152 token = getLongjumpToken(token);
153 }
154 ::R_ReleaseObject(token);
155 ::R_ContinueUnwind(token);
156 Rf_error("Internal error: Rcpp longjump failed to resume");
157}
158
159}} // namespace Rcpp::internal
160
161
162namespace Rcpp {
163
165 SEXP token;
166 LongjumpException(SEXP token_) : token(token_) {
169 }
170 }
171};
172
173 #define RCPP_ADVANCED_EXCEPTION_CLASS(__CLASS__, __WHAT__) \
174 class __CLASS__ : public std::exception { \
175 public: \
176 __CLASS__( ) throw() : message( std::string(__WHAT__) + "." ){} \
177 __CLASS__( const std::string& message ) throw() : \
178 message( std::string(__WHAT__) + ": " + message + "."){} \
179 template <typename... Args> \
180 __CLASS__( const char* fmt, Args&&... args ) throw() : \
181 message( tfm::format(fmt, std::forward<Args>(args)... ) ){} \
182 virtual ~__CLASS__() throw(){} \
183 virtual const char* what() const throw() { return message.c_str(); } \
184 private: \
185 std::string message; \
186 };
187
188 template <typename... Args>
189 inline void warning(const char* fmt, Args&&... args ) {
190 // Rf_warning() may longjmp out of this frame (e.g. when the caller
191 // installs a warning handler via tryCatch(warning=...)). A longjmp
192 // skips C++ destructors, so the std::string returned by tfm::format()
193 // would leak its heap buffer. Copy into a stack buffer and let the
194 // std::string be destroyed before Rf_warning() is invoked.
195 char buf[8192];
196 {
197 const std::string msg = tfm::format(fmt, std::forward<Args>(args)...);
198 std::snprintf(buf, sizeof(buf), "%s", msg.c_str());
199 }
200 Rf_warning("%s", buf);
201 } // #nocov end
202
203 template <typename... Args>
204 inline void NORET stop(const char* fmt, Args&&... args) {
205 throw Rcpp::exception( tfm::format(fmt, std::forward<Args>(args)... ).c_str() );
206 }
207
208 #define RCPP_EXCEPTION_CLASS(__CLASS__,__WHAT__) \
209 class __CLASS__ : public std::exception{ \
210 public: \
211 __CLASS__( ) throw() : message( std::string(__WHAT__) + "." ){} ; \
212 __CLASS__( const std::string& message ) throw() : \
213 message( std::string(__WHAT__) + ": " + message + "." ){} ; \
214 virtual ~__CLASS__() throw(){} ; \
215 virtual const char* what() const throw() { return message.c_str() ; } \
216 private: \
217 std::string message ; \
218 } ;
219
220 #define RCPP_SIMPLE_EXCEPTION_CLASS(__CLASS__,__MESSAGE__) \
221 class __CLASS__ : public std::exception{ \
222 public: \
223 __CLASS__() throw() {} ; \
224 virtual ~__CLASS__() throw(){} ; \
225 virtual const char* what() const throw() { return __MESSAGE__ ; } \
226 } ;
227
228 RCPP_SIMPLE_EXCEPTION_CLASS(not_a_matrix, "Not a matrix.") // #nocov start
229 RCPP_SIMPLE_EXCEPTION_CLASS(parse_error, "Parse error.")
230 RCPP_SIMPLE_EXCEPTION_CLASS(not_s4, "Not an S4 object.")
231 RCPP_SIMPLE_EXCEPTION_CLASS(not_reference, "Not an S4 object of a reference class.")
232 RCPP_SIMPLE_EXCEPTION_CLASS(not_initialized, "C++ object not initialized. (Missing default constructor?)")
233 RCPP_SIMPLE_EXCEPTION_CLASS(no_such_field, "No such field.") // not used internally
234 RCPP_SIMPLE_EXCEPTION_CLASS(no_such_function, "No such function.")
235 RCPP_SIMPLE_EXCEPTION_CLASS(unevaluated_promise, "Promise not yet evaluated.")
236 RCPP_SIMPLE_EXCEPTION_CLASS(embedded_nul_in_string, "Embedded NUL in string.")
237
238 // Promoted
239 RCPP_EXCEPTION_CLASS(no_such_slot, "No such slot")
240 RCPP_EXCEPTION_CLASS(not_a_closure, "Not a closure")
241
242 RCPP_EXCEPTION_CLASS(S4_creation_error, "Error creating object of S4 class")
243 RCPP_EXCEPTION_CLASS(reference_creation_error, "Error creating object of reference class") // not used internally
244 RCPP_EXCEPTION_CLASS(no_such_binding, "No such binding")
245 RCPP_EXCEPTION_CLASS(binding_not_found, "Binding not found")
246 RCPP_EXCEPTION_CLASS(binding_is_locked, "Binding is locked")
247 RCPP_EXCEPTION_CLASS(no_such_namespace, "No such namespace")
248 RCPP_EXCEPTION_CLASS(function_not_exported, "Function not exported")
249 RCPP_EXCEPTION_CLASS(eval_error, "Evaluation error")
250
251 // Promoted
252 RCPP_ADVANCED_EXCEPTION_CLASS(not_compatible, "Not compatible" ) // #nocov end
253 RCPP_ADVANCED_EXCEPTION_CLASS(index_out_of_bounds, "Index is out of bounds")
254
255 #undef RCPP_SIMPLE_EXCEPTION_CLASS
256 #undef RCPP_EXCEPTION_CLASS
257 #undef RCPP_ADVANCED_EXCEPTION_CLASS
258
259
260namespace internal {
261
262 inline SEXP nth(SEXP s, int n) { // #nocov start
263 return Rf_length(s) > n ? (n == 0 ? CAR(s) : CAR(Rf_nthcdr(s, n))) : R_NilValue;
264 }
265
266 // We want the call just prior to the call from Rcpp_eval
267 // This conditional matches
268 // tryCatch(evalq(sys.calls(), .GlobalEnv), error = identity, interrupt = identity)
269 inline bool is_Rcpp_eval_call(SEXP expr) {
270 SEXP sys_calls_symbol = Rf_install("sys.calls");
271 SEXP identity_symbol = Rf_install("identity");
272 Shield<SEXP> identity_fun(Rf_findFun(identity_symbol, R_BaseEnv));
273 SEXP tryCatch_symbol = Rf_install("tryCatch");
274 SEXP evalq_symbol = Rf_install("evalq");
275
276 return TYPEOF(expr) == LANGSXP &&
277 Rf_length(expr) == 4 &&
278 nth(expr, 0) == tryCatch_symbol &&
279 CAR(nth(expr, 1)) == evalq_symbol &&
280 CAR(nth(nth(expr, 1), 1)) == sys_calls_symbol &&
281 nth(nth(expr, 1), 2) == R_GlobalEnv &&
282 nth(expr, 2) == identity_fun &&
283 nth(expr, 3) == identity_fun;
284 }
285} // namespace internal
286
287} // namespace Rcpp
288
289inline SEXP get_last_call(){
290 SEXP sys_calls_symbol = Rf_install("sys.calls");
291
292 Rcpp::Shield<SEXP> sys_calls_expr(Rf_lang1(sys_calls_symbol));
293 Rcpp::Shield<SEXP> calls(Rcpp_fast_eval(sys_calls_expr, R_GlobalEnv));
294
295 SEXP cur, prev;
296 prev = cur = calls;
297 while(CDR(cur) != R_NilValue) {
298 SEXP expr = CAR(cur);
299
301 break;
302 }
303 prev = cur;
304 cur = CDR(cur);
305 }
306 return CAR(prev);
307}
308
309inline SEXP get_exception_classes( const std::string& ex_class) {
310 Rcpp::Shield<SEXP> res( Rf_allocVector( STRSXP, 4 ) );
311
312 #ifndef RCPP_USING_UTF8_ERROR_STRING
313 SET_STRING_ELT( res, 0, Rf_mkChar( ex_class.c_str() ) ) ;
314 #else
315 SET_STRING_ELT( res, 0, Rf_mkCharLenCE( ex_class.c_str(), ex_class.size(), CE_UTF8 ) );
316 #endif
317 SET_STRING_ELT( res, 1, Rf_mkChar( "C++Error" ) ) ;
318 SET_STRING_ELT( res, 2, Rf_mkChar( "error" ) ) ;
319 SET_STRING_ELT( res, 3, Rf_mkChar( "condition" ) ) ;
320 return res;
321}
322
323inline SEXP make_condition(const std::string& ex_msg, SEXP call, SEXP cppstack, SEXP classes){
324 Rcpp::Shield<SEXP> res( Rf_allocVector( VECSXP, 3 ) ) ;
325 #ifndef RCPP_USING_UTF8_ERROR_STRING
326 SET_VECTOR_ELT( res, 0, Rf_mkString( ex_msg.c_str() ) ) ;
327 #else
328 Rcpp::Shield<SEXP> ex_msg_rstring( Rf_allocVector( STRSXP, 1 ) ) ;
329 SET_STRING_ELT( ex_msg_rstring, 0, Rf_mkCharLenCE( ex_msg.c_str(), ex_msg.size(), CE_UTF8 ) );
330 SET_VECTOR_ELT( res, 0, ex_msg_rstring ) ;
331 #endif
332 SET_VECTOR_ELT( res, 1, call ) ;
333 SET_VECTOR_ELT( res, 2, cppstack ) ;
334
335 Rcpp::Shield<SEXP> names( Rf_allocVector( STRSXP, 3 ) );
336 SET_STRING_ELT( names, 0, Rf_mkChar( "message" ) ) ;
337 SET_STRING_ELT( names, 1, Rf_mkChar( "call" ) ) ;
338 SET_STRING_ELT( names, 2, Rf_mkChar( "cppstack" ) ) ;
339 Rf_setAttrib( res, R_NamesSymbol, names ) ;
340 Rf_setAttrib( res, R_ClassSymbol, classes ) ;
341 return res ;
342}
343
344template <typename Exception>
345inline SEXP exception_to_condition_template( const Exception& ex, bool include_call) {
346#ifndef RCPP_NO_RTTI
347 std::string ex_class = demangle( typeid(ex).name() ) ;
348#else
349 std::string ex_class = "<not available>";
350#endif
351 std::string ex_msg = ex.what() ;
352
353 Rcpp::Shelter<SEXP> shelter;
354 SEXP call, cppstack;
355 if (include_call) {
356 call = shelter(get_last_call());
357 cppstack = shelter(rcpp_get_stack_trace());
358 } else {
359 call = R_NilValue;
360 cppstack = R_NilValue;
361 }
362 SEXP classes = shelter( get_exception_classes(ex_class) );
363 SEXP condition = shelter( make_condition( ex_msg, call, cppstack, classes) );
364 rcpp_set_stack_trace( R_NilValue ) ;
365 return condition ;
366}
367
372
373inline SEXP exception_to_r_condition( const std::exception& ex){
375}
376
377inline SEXP string_to_try_error( const std::string& str){
378 using namespace Rcpp;
379
380 #ifndef RCPP_USING_UTF8_ERROR_STRING
381 Rcpp::Shield<SEXP> txt(Rf_mkString(str.c_str()));
382 Rcpp::Shield<SEXP> simpleErrorExpr(Rf_lang2(::Rf_install("simpleError"), txt));
383 Rcpp::Shield<SEXP> tryError( Rf_mkString( str.c_str() ) );
384 #else
385 Rcpp::Shield<SEXP> tryError( Rf_allocVector( STRSXP, 1 ) ) ;
386 SET_STRING_ELT( tryError, 0, Rf_mkCharLenCE( str.c_str(), str.size(), CE_UTF8 ) );
387 Rcpp::Shield<SEXP> simpleErrorExpr( Rf_lang2(::Rf_install("simpleError"), tryError ));
388 #endif
389
390 Rcpp::Shield<SEXP> simpleError( Rf_eval(simpleErrorExpr, R_GlobalEnv) );
391 Rf_setAttrib( tryError, R_ClassSymbol, Rf_mkString("try-error") ) ;
392 Rf_setAttrib( tryError, Rf_install( "condition") , simpleError ) ;
393
394 return tryError; // #nocov end
395}
396
397inline SEXP exception_to_try_error( const std::exception& ex){
398 return string_to_try_error(ex.what());
399}
400
401std::string demangle( const std::string& name) ;
402#ifndef RCPP_NO_RTTI
403#define DEMANGLE(__TYPE__) demangle( typeid(__TYPE__).name() ).c_str()
404#endif
405
406
407inline void forward_exception_to_r(const std::exception& ex){
408 SEXP stop_sym = Rf_install( "stop" ) ;
410 Rcpp::Shield<SEXP> expr( Rf_lang2( stop_sym , condition ) ) ;
411 Rf_eval( expr, R_GlobalEnv ) ;
412}
413
415 SEXP stop_sym = Rf_install( "stop" ) ;
417 Rcpp::Shield<SEXP> expr( Rf_lang2( stop_sym , condition ) ) ;
418 Rf_eval( expr, R_GlobalEnv ) ;
419}
420
421
422#endif
std::vector< std::string > stack
Definition exceptions.h:61
void copy_stack_trace_to_r() const
exception(const char *message_, bool include_call=RCPP_DEFAULT_INCLUDE_CALL)
Definition exceptions.h:40
bool include_call() const
Definition exceptions.h:50
std::string message
Definition exceptions.h:59
exception(const char *message_, const char *, int, bool include_call=RCPP_DEFAULT_INCLUDE_CALL)
Definition exceptions.h:45
virtual const char * what() const
Definition exceptions.h:54
virtual ~exception()
Definition exceptions.h:53
file_exists(const std::string &file)
Definition exceptions.h:108
virtual ~file_io_error()
Definition exceptions.h:92
virtual const char * what() const
Definition exceptions.h:93
std::string file
Definition exceptions.h:97
std::string filePath() const
Definition exceptions.h:94
std::string message
Definition exceptions.h:96
file_io_error(const std::string &msg, const std::string &file)
Definition exceptions.h:90
file_io_error(int code, const std::string &file)
Definition exceptions.h:88
file_io_error(const std::string &file)
Definition exceptions.h:86
file_not_found(const std::string &file)
Definition exceptions.h:102
std::string message
Definition exceptions.h:81
no_such_env(const std::string &name)
Definition exceptions.h:74
virtual ~no_such_env()
Definition exceptions.h:78
virtual const char * what() const
Definition exceptions.h:79
no_such_env(int pos)
Definition exceptions.h:76
void forward_rcpp_exception_to_r(const Rcpp::exception &ex)
Definition exceptions.h:414
void forward_exception_to_r(const std::exception &ex)
Definition exceptions.h:407
SEXP exception_to_try_error(const std::exception &ex)
Definition exceptions.h:397
#define RCPP_EXCEPTION_CLASS(__CLASS__, __WHAT__)
Definition exceptions.h:208
std::string demangle(const std::string &name)
Definition routines.h:192
SEXP string_to_try_error(const std::string &str)
Definition exceptions.h:377
SEXP get_exception_classes(const std::string &ex_class)
Definition exceptions.h:309
SEXP rcpp_exception_to_r_condition(const Rcpp::exception &ex)
Definition exceptions.h:368
SEXP make_condition(const std::string &ex_msg, SEXP call, SEXP cppstack, SEXP classes)
Definition exceptions.h:323
SEXP get_last_call()
Definition exceptions.h:289
SEXP exception_to_r_condition(const std::exception &ex)
Definition exceptions.h:373
#define RCPP_ADVANCED_EXCEPTION_CLASS(__CLASS__, __WHAT__)
Definition exceptions.h:173
#define RCPP_SIMPLE_EXCEPTION_CLASS(__CLASS__, __MESSAGE__)
Definition exceptions.h:220
SEXP exception_to_condition_template(const Exception &ex, bool include_call)
Definition exceptions.h:345
#define RCPP_DEFAULT_INCLUDE_CALL
Definition exceptions.h:30
#define NORET
Definition headers.h:84
internal implementation details
Definition Date.h:43
bool isLongjumpSentinel(SEXP x)
Definition exceptions.h:139
SEXP getLongjumpToken(SEXP sentinel)
Definition exceptions.h:146
void resumeJump(SEXP token)
Definition exceptions.h:150
bool is_Rcpp_eval_call(SEXP expr)
Definition exceptions.h:269
SEXP nth(SEXP s, int n)
Definition exceptions.h:262
SEXP longjumpSentinel(SEXP token)
Definition exceptions.h:128
Rcpp API.
Definition algo.h:28
void NORET stop(const std::string &message)
Definition exceptions.h:119
void message(SEXP s)
Definition message.h:26
static std::string toString(const int i)
Definition exceptions.h:66
void warning(const std::string &message)
Definition exceptions.h:115
void format(std::ostream &out, const char *fmt)
Definition tinyformat.h:996
attribute_hidden SEXP rcpp_set_stack_trace(SEXP e)
Definition routines.h:186
attribute_hidden SEXP rcpp_get_stack_trace()
Definition routines.h:180
LongjumpException(SEXP token_)
Definition exceptions.h:166