From 42b9631ce15660c5c428d50cb4ec77b16eaab155 Mon Sep 17 00:00:00 2001 From: Kevin Ushey Date: Mon, 3 Aug 2026 13:35:13 -0700 Subject: [PATCH] convert c++ exceptions to r errors in module .External entry points --- ChangeLog | 10 ++++++++++ inst/tinytest/test_module.R | 6 ++++++ src/module.cpp | 10 +++++++++- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 15cefd8f8..a45c14530 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2026-08-03 Kevin Ushey + + * src/module.cpp (class__newInstance, CppMethod__invoke, + CppMethod__invoke_void, CppMethod__invoke_notvoid): Convert C++ + exceptions to R errors at these .External entry points, so that + e.g. calling a method on an uninitialized module object raises + the intended R error rather than terminating the R session + (#1495) + * inst/tinytest/test_module.R: Add regression test + 2026-07-24 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Roll micro version and date diff --git a/inst/tinytest/test_module.R b/inst/tinytest/test_module.R index 960a33e96..f9e975bbe 100644 --- a/inst/tinytest/test_module.R +++ b/inst/tinytest/test_module.R @@ -102,6 +102,12 @@ x10 <- runif(10, 10.0, 20.0) set.seed(123) expect_equal(r$get(10), x10) +## calling a method on an uninitialized module object (created via the +## dummy-object path for classes without a default constructor) should +## raise an R error rather than terminating the R session (#1495) +r <- new( ModuleRandomizer ) +expect_error( r$get(10L), "not initialized" ) + # test.Module.flexible.semantics <- function( ){ expect_equal( test_reference( seq(0,10) ), 11L ) expect_equal( test_const_reference( seq(0,10) ), 11L ) diff --git a/src/module.cpp b/src/module.cpp index 3ffb5639b..d90d81ffb 100644 --- a/src/module.cpp +++ b/src/module.cpp @@ -135,12 +135,14 @@ END_RCPP } // #nocov end SEXP class__newInstance(SEXP args) { +BEGIN_RCPP SEXP p = CDR(args); XP_Module module(CAR(p)); p = CDR(p); XP_Class clazz(CAR(p)); p = CDR(p); UNPACK_EXTERNAL_ARGS(cargs,p) return clazz->newInstance(cargs, nargs); +END_RCPP } // relies on being set in .onLoad() @@ -164,6 +166,7 @@ SEXP class__dummyInstance(SEXP args) { } SEXP CppMethod__invoke(SEXP args) { // #nocov start +BEGIN_RCPP SEXP p = CDR(args); // the external pointer to the class @@ -180,9 +183,11 @@ SEXP CppMethod__invoke(SEXP args) { // #nocov start UNPACK_EXTERNAL_ARGS(cargs,p) return clazz->invoke(met, obj, cargs, nargs); -} // #nocov end +END_RCPP +} // #nocov end SEXP CppMethod__invoke_void(SEXP args) { +BEGIN_RCPP SEXP p = CDR(args); // the external pointer to the class @@ -199,9 +204,11 @@ SEXP CppMethod__invoke_void(SEXP args) { UNPACK_EXTERNAL_ARGS(cargs,p) clazz->invoke_void(met, obj, cargs, nargs); return R_NilValue; +END_RCPP } SEXP CppMethod__invoke_notvoid(SEXP args) { +BEGIN_RCPP SEXP p = CDR(args); // the external pointer to the class @@ -218,6 +225,7 @@ SEXP CppMethod__invoke_notvoid(SEXP args) { UNPACK_EXTERNAL_ARGS(cargs,p) return clazz->invoke_notvoid(met, obj, cargs, nargs); +END_RCPP } namespace Rcpp{