Repository navigation
an exception while a module initializes aborts the R session #1525
Description
The boot function that RCPP_MODULE generates is an extern "C" entry point, called from R via .Call() when the module is loaded (Module(), loadModule(), sourceCpp()), but it has no BEGIN_RCPP/END_RCPP:
Rcpp/inst/include/Rcpp/Module.h
Lines 615 to 625 in 2cf7685
| #define RCPP_MODULE(name) \ | |
| void _rcpp_module_##name##_init() ; \ | |
| static Rcpp::Module _rcpp_module_##name( # name ) ; \ | |
| extern "C" SEXP _rcpp_module_boot_##name(){ \ | |
| ::setCurrentScope( & _rcpp_module_##name ) ; \ | |
| _rcpp_module_##name##_init( ) ; \ | |
| Rcpp::XPtr<Rcpp::Module> mod_xp(& _rcpp_module_##name , false); \ | |
| ::setCurrentScope( 0 ) ; \ | |
| return mod_xp ; \ | |
| } \ | |
| void _rcpp_module_##name##_init() |
#define RCPP_MODULE(name) \
void _rcpp_module_##name##_init() ; \
static Rcpp::Module _rcpp_module_##name( # name ) ; \
extern "C" SEXP _rcpp_module_boot_##name(){ \
::setCurrentScope( & _rcpp_module_##name ) ; \
_rcpp_module_##name##_init( ) ; \
Rcpp::XPtr<Rcpp::Module> mod_xp(& _rcpp_module_##name , false); \
::setCurrentScope( 0 ) ; \
return mod_xp ; \
} \
void _rcpp_module_##name##_init()So any C++ exception thrown while the module body runs escapes into R's C code and std::terminate() aborts the session. The module body is ordinary user code, and Rcpp itself throws from it, e.g. .derives<T>("Name") throws std::range_error("no such class") when Name is not (yet) exposed, and anything that allocates or calls Rcpp::stop() can throw too. The exception also skips ::setCurrentScope( 0 ), leaving the global module scope pointing at the half-initialized module.
This is the same class of problem as #1495 / #1496 (which fixed the .External method entry points), for the module boot function.
Reproducible example
repro.cpp:
#include <Rcpp.h>
using namespace Rcpp;
class Shape {
public:
virtual ~Shape() {}
virtual double area() const { return 0.0; }
};
class Square : public Shape {
public:
Square(double side_) : side(side_) {}
double area() const { return side * side; }
double side;
};
RCPP_MODULE(shape_module) {
class_<Shape>("Shape")
.method("area", &Shape::area);
class_<Square>("Square")
.derives<Shape>("Shpae") // typo in the parent class name
.constructor<double>();
}repro.R:
res <- tryCatch(
Rcpp::sourceCpp("repro.cpp"),
error = function(e) conditionMessage(e)
)
cat("result:", res, "\n")Output with R 4.6.1 (macOS 27, Apple clang 21, Rcpp 1.1.2.6 / current master):
libc++abi: terminating due to uncaught exception of type std::range_error: no such class
Abort trap: 6 Rscript repro.R
Rscript exits with code 134 (SIGABRT); the tryCatch() never gets a chance. In a package, the same typo takes down the session at library() time.
I'll follow up with a PR that wraps the boot function in BEGIN_RCPP/END_RCPP, so the exception becomes an R error, and resets the module scope with an RAII guard so it is cleared on the error path too. The change is confined to the macro, so it takes effect when a package is rebuilt.
Activity