diff --git a/ChangeLog b/ChangeLog index 402732946..9eda6d29a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2026-10-29 Kevin Ushey + + * inst/include/Rcpp/module/class.h (class_::AddMethod): Count special + '[' methods once per name rather than once per overload, as complete() + expects; overloading '[[' dropped methods from completion or ran past + the end of the method map (#1529) + * inst/tinytest/cpp/Module.cpp: Add classes overloading '[[' + * inst/tinytest/test_module.R: Test their completions + 2026-10-23 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Roll micro version and date diff --git a/inst/include/Rcpp/module/class.h b/inst/include/Rcpp/module/class.h index a8a1a9563..2ac279817 100644 --- a/inst/include/Rcpp/module/class.h +++ b/inst/include/Rcpp/module/class.h @@ -250,9 +250,10 @@ typename map_vec_signed_method::iterator it = ptr->vec_methods.find( name_ ) ; if( it == ptr->vec_methods.end() ){ it = ptr->vec_methods.insert( vec_signed_method_pair( name_, new vec_signed_method() ) ).first ; + // complete() relies on this counting names, not overloads + if( *name_ == '[' ) ptr->specials++ ; } (it->second)->push_back( new signed_method_class(m, valid, docstring ) ) ; - if( *name_ == '[' ) ptr->specials++ ; return *this ; } diff --git a/inst/tinytest/cpp/Module.cpp b/inst/tinytest/cpp/Module.cpp index 7f3132820..9bdd380a7 100644 --- a/inst/tinytest/cpp/Module.cpp +++ b/inst/tinytest/cpp/Module.cpp @@ -137,6 +137,27 @@ double Test_get_x_pointer(ModuleTest* x) { return x->value; } +class ModuleBrackets { +public: + ModuleBrackets() : value(1.0) {} + + double at(int i) { return value * i; } + double at2(int i, int j) { return value * i * j; } + double total() { return value; } + + double value; +}; + +class ModuleBracketsOnly { +public: + ModuleBracketsOnly() : value(1.0) {} + + double at(int i) { return value * i; } + double at2(int i, int j) { return value * i * j; } + + double value; +}; + class ModuleGadget { public: ModuleGadget() {} @@ -217,6 +238,22 @@ RCPP_MODULE(demoModule) { .method("get" , &ModuleRandomizer::get) ; + // overloaded [[, with and without an ordinary method + class_("ModuleBrackets") + .constructor() + .method("[[", &ModuleBrackets::at) + .method("[[", &ModuleBrackets::at2) + .method("total", &ModuleBrackets::total) + .field("value", &ModuleBrackets::value) + ; + + class_("ModuleBracketsOnly") + .constructor() + .method("[[", &ModuleBracketsOnly::at) + .method("[[", &ModuleBracketsOnly::at2) + .field("value", &ModuleBracketsOnly::value) + ; + class_("ModuleGadget") .constructor() .method("value", static_cast(&ModuleGadget::value)) diff --git a/inst/tinytest/test_module.R b/inst/tinytest/test_module.R index db0edfc60..7b000626e 100644 --- a/inst/tinytest/test_module.R +++ b/inst/tinytest/test_module.R @@ -108,6 +108,14 @@ expect_equal(r$get(10), x10) r <- new( ModuleRandomizer ) expect_error( r$get(10L), "not initialized" ) +## completion lists the ordinary methods and the fields; an overloaded [[ +## used to be counted once per overload, which dropped the last ordinary +## method, or ran past the end of the method map when there was none (#1529) +brackets <- new( ModuleBrackets ) +expect_equal( Rcpp:::complete( brackets ), c("total( ", "value") ) +expect_equal( brackets[[2L]], 2.0 ) +expect_equal( Rcpp:::complete( new( ModuleBracketsOnly ) ), "value" ) + # test.Module.flexible.semantics <- function( ){ expect_equal( test_reference( seq(0,10) ), 11L ) expect_equal( test_const_reference( seq(0,10) ), 11L )