Skip to content

Commit 1b33aad

Browse files
romainfrancoiseddelbuettel
authored andcommitted
Control whether exceptions include the call and C++ stack with a macros (#868)
* Support for configuring default value for include_call parameter of Rcpp exceptions. * Also use RCPP_DEFAULT_INCLUDE_CALL for exception classes other than Rcpp::exception use Shelter instead of Shield because e.g. the result of get_last_call() would not be protected long enough. rework exception_to_r_condition and rcpp_exception_to_r_condition so that they share the same code via a template * Additional tests for Rcpp::exception and eval_error. Trsting that they don't include call and cppstack when RCPP_DEFAULT_INCLUDE_CALL is defined to false * using namespace Rcpp; * s/eval_error/eval_error_no_call/ for disambiguation
1 parent 5d5cdb8 commit 1b33aad

File tree

3 files changed

+80
-26
lines changed

3 files changed

+80
-26
lines changed

inst/include/Rcpp/exceptions.h

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,22 @@
2424

2525
#include <Rversion.h>
2626

27+
#ifndef RCPP_DEFAULT_INCLUDE_CALL
28+
#define RCPP_DEFAULT_INCLUDE_CALL true
29+
#endif
2730

2831
#define GET_STACKTRACE() stack_trace( __FILE__, __LINE__ )
2932

3033
namespace Rcpp {
3134

3235
class exception : public std::exception {
3336
public:
34-
explicit exception(const char* message_, bool include_call = true) : // #nocov start
37+
explicit exception(const char* message_, bool include_call = RCPP_DEFAULT_INCLUDE_CALL) : // #nocov start
3538
message(message_),
3639
include_call_(include_call){
3740
rcpp_set_stack_trace(Shield<SEXP>(stack_trace()));
3841
}
39-
exception(const char* message_, const char*, int, bool include_call = true) :
42+
exception(const char* message_, const char*, int, bool include_call = RCPP_DEFAULT_INCLUDE_CALL) :
4043
message(message_),
4144
include_call_(include_call){
4245
rcpp_set_stack_trace(Shield<SEXP>(stack_trace()));
@@ -306,34 +309,32 @@ inline SEXP make_condition(const std::string& ex_msg, SEXP call, SEXP cppstack,
306309
return res ;
307310
}
308311

312+
template <typename Exception>
313+
inline SEXP exception_to_condition_template( const Exception& ex, bool include_call) {
314+
std::string ex_class = demangle( typeid(ex).name() ) ;
315+
std::string ex_msg = ex.what() ;
316+
317+
Rcpp::Shelter<SEXP> shelter;
318+
SEXP call, cppstack;
319+
if (include_call) {
320+
call = shelter(get_last_call());
321+
cppstack = shelter(rcpp_get_stack_trace());
322+
} else {
323+
call = R_NilValue;
324+
cppstack = R_NilValue;
325+
}
326+
SEXP classes = shelter( get_exception_classes(ex_class) );
327+
SEXP condition = shelter( make_condition( ex_msg, call, cppstack, classes) );
328+
rcpp_set_stack_trace( R_NilValue ) ;
329+
return condition ;
330+
}
331+
309332
inline SEXP rcpp_exception_to_r_condition(const Rcpp::exception& ex) {
310-
std::string ex_class = demangle( typeid(ex).name() ) ;
311-
std::string ex_msg = ex.what() ;
312-
313-
SEXP call, cppstack;
314-
if (ex.include_call()) {
315-
call = Rcpp::Shield<SEXP>(get_last_call());
316-
cppstack = Rcpp::Shield<SEXP>( rcpp_get_stack_trace());
317-
} else {
318-
call = R_NilValue;
319-
cppstack = R_NilValue;
320-
}
321-
Rcpp::Shield<SEXP> classes( get_exception_classes(ex_class) );
322-
Rcpp::Shield<SEXP> condition( make_condition( ex_msg, call, cppstack, classes) );
323-
rcpp_set_stack_trace( R_NilValue ) ;
324-
return condition ;
333+
return exception_to_condition_template(ex, ex.include_call());
325334
}
326335

327336
inline SEXP exception_to_r_condition( const std::exception& ex){
328-
std::string ex_class = demangle( typeid(ex).name() ) ;
329-
std::string ex_msg = ex.what() ;
330-
331-
Rcpp::Shield<SEXP> cppstack( rcpp_get_stack_trace() );
332-
Rcpp::Shield<SEXP> call( get_last_call() );
333-
Rcpp::Shield<SEXP> classes( get_exception_classes(ex_class) );
334-
Rcpp::Shield<SEXP> condition( make_condition( ex_msg, call, cppstack, classes) );
335-
rcpp_set_stack_trace( R_NilValue ) ;
336-
return condition ;
337+
return exception_to_condition_template(ex, RCPP_DEFAULT_INCLUDE_CALL);
337338
}
338339

339340
inline SEXP string_to_try_error( const std::string& str){
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#define RCPP_DEFAULT_INCLUDE_CALL false
2+
#include <Rcpp.h>
3+
using namespace Rcpp;
4+
5+
// [[Rcpp::export]]
6+
void Rcpp_exception(){
7+
throw Rcpp::exception("ouch");
8+
}
9+
10+
// [[Rcpp::export]]
11+
void eval_error_no_call(){
12+
Rcpp_eval(Rf_lang1(Rf_install("ouch")), R_EmptyEnv);
13+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/usr/bin/r -t
2+
#
3+
# Copyright (C) 2018 Dirk Eddelbuettel and Romain Francois
4+
#
5+
# This file is part of Rcpp.
6+
#
7+
# Rcpp is free software: you can redistribute it and/or modify it
8+
# under the terms of the GNU General Public License as published by
9+
# the Free Software Foundation, either version 2 of the License, or
10+
# (at your option) any later version.
11+
#
12+
# Rcpp is distributed in the hope that it will be useful, but
13+
# WITHOUT ANY WARRANTY; without even the implied warranty of
14+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
# GNU General Public License for more details.
16+
#
17+
# You should have received a copy of the GNU General Public License
18+
# along with Rcpp. If not, see <http://www.gnu.org/licenses/>.
19+
20+
.runThisTest <- Sys.getenv("RunAllRcppTests") == "yes"
21+
22+
if (.runThisTest) {
23+
24+
.setUp <- Rcpp:::unitTestSetup("Exceptions_nocall.cpp")
25+
26+
test.Rcpp_exception <- function() {
27+
tryCatch(Rcpp_exception(), error = function(e){
28+
checkTrue(is.null(e$call))
29+
checkTrue(is.null(e$cppstack))
30+
})
31+
}
32+
33+
test.eval_error <- function() {
34+
tryCatch(eval_error_no_call(), error = function(e){
35+
checkTrue(is.null(e$call))
36+
checkTrue(is.null(e$cppstack))
37+
})
38+
}
39+
40+
}

0 commit comments

Comments
 (0)