From b544fef76ab0afc9a73c941d22dda10008a8addf Mon Sep 17 00:00:00 2001 From: Michael Sumner Date: Fri, 2 Oct 2026 20:38:23 +0000 Subject: [PATCH 1/3] Module(): analyse reference class once and reuse C++Class objects --- R/Module.R | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/R/Module.R b/R/Module.R index 10c3cadce..03cd7a09a 100644 --- a/R/Module.R +++ b/R/Module.R @@ -222,15 +222,12 @@ Module <- function( module, PACKAGE = methods::getPackageName(where), where = to fields <- cpp_fields( CLASS, where ) methods <- cpp_refMethods(CLASS, where) - generator <- methods::setRefClass( clname, - fields = fields, - contains = "C++Object", - methods = methods, - where = where - ) # just to make codetools happy .self <- .refClassDef <- NULL - generator$methods(initialize = + # Supply 'initialize' together with the other methods so that the + # reference class is analysed once, instead of a second full pass + # through refClassInformation() in generator$methods(...) + methods[["initialize"]] <- if (cpp_hasDefaultConstructor(CLASS)) function(...) Rcpp::cpp_object_initializer(.self,.refClassDef, ...) else @@ -238,8 +235,13 @@ Module <- function( module, PACKAGE = methods::getPackageName(where), where = to if (nargs()) Rcpp::cpp_object_initializer(.self,.refClassDef, ...) else Rcpp::cpp_object_dummy(.self, .refClassDef) # #nocov } - ) rm( .self, .refClassDef ) + generator <- methods::setRefClass( clname, + fields = fields, + contains = "C++Object", + methods = methods, + where = where + ) classDef <- methods::getClass(clname) ## non-public (static) fields in class representation @@ -281,7 +283,10 @@ Module <- function( module, PACKAGE = methods::getPackageName(where), where = to CLASS <- classes[[i]] clname <- CLASS@.Data demangled_name <- sub( "^Rcpp_", "", clname ) - .classes_map[[ CLASS@typeid ]] <- storage[[ demangled_name ]] <- .get_Module_Class( module, demangled_name, xp ) + # reuse the C++Class object already built by Module__classes_info + # rather than rebuilding it (and all of its method/field objects) + CLASS@generator <- generators[[ clname ]] + .classes_map[[ CLASS@typeid ]] <- storage[[ demangled_name ]] <- CLASS # exposing enums values as CLASS.VALUE # (should really be CLASS$value but I don't know how to do it) From a965846d2845a91920dc0b02110c0dd84f86fb34 Mon Sep 17 00:00:00 2001 From: Michael Sumner Date: Tue, 20 Oct 2026 13:24:46 +0000 Subject: [PATCH 2/3] Module(): take classDef from the generator, hoist converter registration, match show exactly --- R/Module.R | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/R/Module.R b/R/Module.R index 03cd7a09a..72a00e2f9 100644 --- a/R/Module.R +++ b/R/Module.R @@ -243,7 +243,7 @@ Module <- function( module, PACKAGE = methods::getPackageName(where), where = to where = where ) - classDef <- methods::getClass(clname) + classDef <- generator$def ## non-public (static) fields in class representation ## Should these become real fields? fields <- classDef@fieldPrototypes @@ -270,7 +270,7 @@ Module <- function( module, PACKAGE = methods::getPackageName(where), where = to } # #nocov end # promoting show to S4 - if( any( grepl( "show", names(CLASS@methods) ) ) ){ + if( "show" %in% names(CLASS@methods) ){ setMethod( "show", clname, function(object) object$show(), where = where ) # #nocov } @@ -304,24 +304,21 @@ Module <- function( module, PACKAGE = methods::getPackageName(where), where = to functions <- .Call( Module__functions_names, xp ) for( fun in functions ){ storage[[ fun ]] <- .get_Module_function( module, fun, xp ) - - # register as(FROM, TO) methods - converter_rx <- "^[.]___converter___(.*)___(.*)$" - if( length( matches <- grep( converter_rx, functions ) ) ){ # #nocov start - for( i in matches ){ - fun <- functions[i] - from <- sub( converter_rx, "\\1", fun ) - to <- sub( converter_rx, "\\2", fun ) - converter <- function( from ){} - body( converter ) <- substitute( { CONVERT(from) }, - list( CONVERT = storage[[fun]] ) - ) - setAs( from, to, converter, where = where ) - } - } # #nocov end - } + # register as(FROM, TO) methods once storage holds every function: the + # converter body captures storage[[fun]] by value via substitute() + converter_rx <- "^[.]___converter___(.*)___(.*)$" + for( fun in grep( converter_rx, functions, value = TRUE ) ){ # #nocov start + from <- sub( converter_rx, "\\1", fun ) + to <- sub( converter_rx, "\\2", fun ) + converter <- function( from ){} + body( converter ) <- substitute( { CONVERT(from) }, + list( CONVERT = storage[[fun]] ) + ) + setAs( from, to, converter, where = where ) + } # #nocov end + assign( "storage", storage, envir = as.environment(module) ) module } From 5e77869ba34640dc1814f72a1c5a719527a1ae1a Mon Sep 17 00:00:00 2001 From: Michael Sumner Date: Tue, 20 Oct 2026 13:24:46 +0000 Subject: [PATCH 3/3] Add ChangeLog entry, roll micro version and date --- ChangeLog | 21 +++++++++++++++++++++ DESCRIPTION | 4 ++-- inst/include/Rcpp/config.h | 4 ++-- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index e37fa9ed9..dbb4d7ca6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +2026-10-21 Michael Sumner + + * R/Module.R (Module): Supply the 'initialize' method to setRefClass() with + the other methods instead of adding it in a second generator$methods() + call, which repeated the reference class analysis of every exposed + method, and reuse the C++Class objects returned by + Module__classes_info() rather than rebuilding each of them via + .get_Module_Class(); both reduce the time spent loading a module (#1515) + * R/Module.R (Module): Take the class representation from the generator + rather than looking it up by name with methods::getClass(), which + ignores 'where' and can return another loaded package's class of the same + name + * R/Module.R (Module): Register as(FROM, TO) converters after the loop + that populates 'storage' rather than once per function inside it + * R/Module.R (Module): Match the 'show' method name exactly rather than as + a substring, so a class exposing e.g. 'showInfo' keeps the C++Object show + method + + * DESCRIPTION (Version, Date): Roll micro version and date + * inst/include/Rcpp/config.h: Idem + 2026-10-13 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Roll micro version and date diff --git a/DESCRIPTION b/DESCRIPTION index 703326006..534fb4874 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: Rcpp Title: Seamless R and C++ Integration -Version: 1.1.2.5 -Date: 2026-10-13 +Version: 1.1.2.6 +Date: 2026-10-21 Authors@R: c(person("Dirk", "Eddelbuettel", role = c("aut", "cre"), email = "edd@debian.org", comment = c(ORCID = "0000-0001-6419-907X")), person("Romain", "Francois", role = "aut", diff --git a/inst/include/Rcpp/config.h b/inst/include/Rcpp/config.h index e08ae0b08..59be1fc9a 100644 --- a/inst/include/Rcpp/config.h +++ b/inst/include/Rcpp/config.h @@ -30,8 +30,8 @@ #define RCPP_VERSION_STRING "1.1.2" // the current source snapshot (using four components, if a fifth is used in DESCRIPTION we ignore it) -#define RCPP_DEV_VERSION RcppDevVersion(1,1,2,5) -#define RCPP_DEV_VERSION_STRING "1.1.2.5" +#define RCPP_DEV_VERSION RcppDevVersion(1,1,2,6) +#define RCPP_DEV_VERSION_STRING "1.1.2.6" // Whether sugar checks that the operands of an expression, and a vector // assigned into a range, row or column, have matching lengths. On by default