Skip to content

Commit bce9f92

Browse files
committed
squashing in commit of #663
commit 43e53b0 Merge: 886f5df a15ba45 Author: Dirk Eddelbuettel <edd@debian.org> Date: Tue Apr 11 13:20:10 2017 -0500 Merge pull request #663 from jimhester/exceptions_without_callstacks Added support for throwing exceptions without call stacks. commit a15ba45 Author: Jim Hester <james.f.hester@gmail.com> Date: Mon Apr 3 16:11:24 2017 -0400 Added support for throwing exceptions without call stacks.
1 parent a8dbfa1 commit bce9f92

File tree

6 files changed

+71
-8
lines changed

6 files changed

+71
-8
lines changed

ChangeLog

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@
3636
renamed modules
3737
* inst/unitTests/testRcppModule/tests/modules.R: Call renamed module
3838

39+
2017-04-03 Jim Hester <james.f.hester@gmail.com>
40+
41+
* inst/include/Rcpp/exceptions.h: Added support for throwing
42+
exceptions without call stacks.
43+
* inst/include/Rcpp/macros/macros.h: Idem
44+
* inst/unitTests/cpp/exceptions.cpp: Idem
45+
* inst/unitTests/runit.exceptions.R: Idem
46+
3947
2017-03-28 James J Balamuta <balamut2@illinois.edu>
4048

4149
* inst/vignettes/Rcpp-FAQ.Rnw: Added "Known Issues" section to FAQ

inst/NEWS.Rd

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
\item Added a Known Issues section to the Rcpp FAQ vignette
1515
(James Balamuta in \ghpr{661} addressing \ghit{628}, \ghit{563},
1616
\ghit{552}, \ghit{460}, \ghit{419}, and \ghit{251}).
17+
\item Rcpp::exceptions can now be constructed without a call stack (Jim
18+
Hester in \ghpr{663}).
1719
}
1820
}
1921
}

inst/include/Rcpp/exceptions.h

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,25 +28,33 @@ namespace Rcpp {
2828

2929
class exception : public std::exception {
3030
public:
31-
explicit exception(const char* message_) : message(message_) { // #nocov start
31+
explicit exception(const char* message_, bool include_call = true) : // #nocov start
32+
message(message_),
33+
include_call_(include_call){
3234
rcpp_set_stack_trace(stack_trace());
3335
}
34-
exception(const char* message_, const char* file, int line) : message(message_) {
36+
exception(const char* message_, const char* file, int line, bool include_call = true) :
37+
message(message_),
38+
include_call_(include_call){
3539
rcpp_set_stack_trace(stack_trace(file,line));
3640
}
41+
bool include_call() const {
42+
return include_call_;
43+
}
3744
virtual ~exception() throw() {}
3845
virtual const char* what() const throw() {
3946
return message.c_str(); // #nocov end
4047
}
4148
private:
4249
std::string message;
50+
bool include_call_;
4351
};
4452

4553
// simple helper
4654
static std::string toString(const int i) { // #nocov start
4755
std::ostringstream ostr;
4856
ostr << i;
49-
return ostr.str(); // #nocov end
57+
return ostr.str(); // #nocov end
5058
}
5159

5260
class no_such_env : public std::exception {
@@ -127,7 +135,7 @@ namespace Rcpp {
127135
RCPP_EXCEPTION_CLASS(binding_is_locked, std::string("binding is locked: '") + message + "'" )
128136
RCPP_EXCEPTION_CLASS(no_such_namespace, std::string("no such namespace: '") + message + "'" )
129137
RCPP_EXCEPTION_CLASS(function_not_exported, std::string("function not exported: ") + message)
130-
RCPP_EXCEPTION_CLASS(eval_error, message ) // #nocov end
138+
RCPP_EXCEPTION_CLASS(eval_error, message ) // #nocov end
131139

132140
#undef RCPP_EXCEPTION_CLASS
133141
#undef RCPP_SIMPLE_EXCEPTION_CLASS
@@ -217,6 +225,24 @@ inline SEXP make_condition(const std::string& ex_msg, SEXP call, SEXP cppstack,
217225
return res ;
218226
}
219227

228+
inline SEXP rcpp_exception_to_r_condition(const Rcpp::exception& ex) {
229+
std::string ex_class = demangle( typeid(ex).name() ) ;
230+
std::string ex_msg = ex.what() ;
231+
232+
SEXP call, cppstack;
233+
if (ex.include_call()) {
234+
call = Rcpp::Shield<SEXP>(get_last_call());
235+
cppstack = Rcpp::Shield<SEXP>( rcpp_get_stack_trace());
236+
} else {
237+
call = R_NilValue;
238+
cppstack = R_NilValue;
239+
}
240+
Rcpp::Shield<SEXP> classes( get_exception_classes(ex_class) );
241+
Rcpp::Shield<SEXP> condition( make_condition( ex_msg, call, cppstack, classes) );
242+
rcpp_set_stack_trace( R_NilValue ) ;
243+
return condition ;
244+
}
245+
220246
inline SEXP exception_to_r_condition( const std::exception& ex){
221247
std::string ex_class = demangle( typeid(ex).name() ) ;
222248
std::string ex_msg = ex.what() ;
@@ -245,7 +271,7 @@ inline SEXP string_to_try_error( const std::string& str){
245271
Rf_setAttrib( tryError, R_ClassSymbol, Rf_mkString("try-error") ) ;
246272
Rf_setAttrib( tryError, Rf_install( "condition") , simpleError ) ;
247273

248-
return tryError; // #nocov end
274+
return tryError; // #nocov end
249275
}
250276

251277
inline SEXP exception_to_try_error( const std::exception& ex){
@@ -313,7 +339,7 @@ namespace Rcpp{
313339

314340
inline void NORET stop(const std::string& message) { // #nocov start
315341
throw Rcpp::exception(message.c_str());
316-
} // #nocov end
342+
} // #nocov end
317343

318344
template <typename T1>
319345
inline void NORET stop(const char* fmt, const T1& arg1) {
@@ -366,7 +392,14 @@ namespace Rcpp{
366392
}
367393
}
368394

369-
inline void forward_exception_to_r( const std::exception& ex){
395+
inline void forward_exception_to_r(const std::exception& ex){
396+
SEXP stop_sym = Rf_install( "stop" ) ;
397+
Rcpp::Shield<SEXP> condition( exception_to_r_condition(ex) );
398+
Rcpp::Shield<SEXP> expr( Rf_lang2( stop_sym , condition ) ) ;
399+
Rf_eval( expr, R_GlobalEnv ) ;
400+
}
401+
402+
inline void forward_rcpp_exception_to_r(const Rcpp::exception& ex) {
370403
SEXP stop_sym = Rf_install( "stop" ) ;
371404
Rcpp::Shield<SEXP> condition( exception_to_r_condition(ex) );
372405
Rcpp::Shield<SEXP> expr( Rf_lang2( stop_sym , condition ) ) ;

inst/include/Rcpp/macros/macros.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,15 @@
3939
catch( Rcpp::internal::InterruptedException &__ex__) { \
4040
rcpp_output_type = 1 ; \
4141
} \
42+
catch(Rcpp::exception& __ex__) { \
43+
rcpp_output_type = 2 ; \
44+
rcpp_output_condition = PROTECT(rcpp_exception_to_r_condition(__ex__)) ; \
45+
} \
4246
catch( std::exception& __ex__ ){ \
4347
rcpp_output_type = 2 ; \
4448
rcpp_output_condition = PROTECT(exception_to_r_condition(__ex__)) ; \
45-
} catch( ... ){ \
49+
} \
50+
catch( ... ){ \
4651
rcpp_output_type = 2 ; \
4752
rcpp_output_condition = PROTECT(string_to_try_error("c++ exception (unknown reason)")) ; \
4853
} \

inst/unitTests/cpp/exceptions.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,8 @@ double f1(double val) {
6262
double takeLogNested(double val) {
6363
return f1(val);
6464
}
65+
66+
// [[Rcpp::export]]
67+
void noCall() {
68+
throw Rcpp::exception("Testing", false);
69+
}

inst/unitTests/runit.exceptions.R

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,4 +110,14 @@ test.rcppExceptionLocation <- function() {
110110
checkEquals(nested$call, quote(takeLogNested(x)))
111111
}
112112

113+
test.rcppExceptionNoCall <- function() {
114+
115+
# Can throw exceptions that don't include a call stack
116+
e <- tryCatch(noCall(), error = identity)
117+
118+
checkIdentical(e$message, "Testing")
119+
checkIdentical(e$call, NULL)
120+
checkIdentical(e$cppstack, NULL)
121+
checkIdentical(class(e), c("Rcpp::exception", "C++Error", "error", "condition"))
122+
}
113123
}

0 commit comments

Comments
 (0)