Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Simplify attribute accessor using two lambda visitors
  • Loading branch information
eddelbuettel committed Dec 22, 2025
commit f6591c3a107ef5c0204808a3b84b443bca875998
10 changes: 3 additions & 7 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
2025-12-22 Dirk Eddelbuettel <edd@debian.org>

* inst/include/Rcpp/DataFrame.h (nrow): Simplified per #1430 discussion
relying on compact sequence also removing use of ATTRIB
relying on compact sequence representation removing use of ATTRIB
* inst/include/Rcpp/proxy/AttributeProxy.h (hasAttribute): For R
4.6.0, rewritten using R_mapAttrib() avoiding to-be-removed ATTRIB()

2025-12-21 Dirk Eddelbuettel <edd@debian.org>

* inst/include/Rcpp/proxy/AttributeProxy.h (attributeNames):
Rewritten using R_mapAttrib() avoiding ATTRIB() to be removed in R 4.6.0
* src/utilities.cpp: Add helper function used by R_mapAttrib()
* inst/include/Rcpp/routines.h: Register new helper function
* src/rcpp_init.cpp (registerFunctions): Idem

* .github/workflows/docker.yaml: Add workflow_dispatch, update
checkout action

Expand Down
10 changes: 3 additions & 7 deletions inst/include/Rcpp/DataFrame.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,13 @@ namespace Rcpp{

// By definition, the number of rows in a data.frame is contained
// in its row.names attribute. Since R 3.5.0 this is returned as a
// compact sequence from which we can just take the length
// Shield<SEXP> rn = Rf_getAttrib(Parent::get__(), R_RowNamesSymbol);
// return Rf_xlength(rn);
// compact sequence from which we can just take the (x)length
// But as this makes an allocation an even simpler check on length as
// discussed in #1430 is also possible and preferable. We also switch
// to returning R_xlen_t which as upcast from int is safe
inline R_xlen_t nrow() const {
SEXP x = Parent::get__();
// We can simplify: zero row DFs have no names, else length of all vector same
// by design constraint so can use vector length for desired row count
return (XLENGTH(x) == 0) ? 0 : XLENGTH(VECTOR_ELT(x, 0));
Shield<SEXP> rn = Rf_getAttrib(Parent::get__(), R_RowNamesSymbol);
return Rf_xlength(rn);
}

template <typename T>
Expand Down
32 changes: 26 additions & 6 deletions inst/include/Rcpp/proxy/AttributeProxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,22 +80,42 @@ class AttributeProxyPolicy {
std::vector<std::string> attributeNames() const {
std::vector<std::string> v;
#if R_VERSION >= R_Version(4, 6, 0)
R_mapAttrib(static_cast<const CLASS&>(*this).get__(), get_attr_names, (void *) &v);
auto visitor = [](SEXP name, SEXP attr, void* data) -> SEXP {
std::vector<std::string>* ptr = static_cast<std::vector<std::string>*>(data);
std::string s{CHAR(Rf_asChar(name))};
ptr->push_back(s);
return NULL;
};
R_mapAttrib(static_cast<const CLASS&>(*this).get__(), visitor, static_cast<void*>(&v));
#else
SEXP attrs = ATTRIB( static_cast<const CLASS&>(*this).get__());
while( attrs != R_NilValue ){
v.push_back( std::string(CHAR(PRINTNAME(TAG(attrs)))) ) ;
attrs = CDR( attrs ) ;
}
#endif
return v ;
return v;
}

bool hasAttribute( const std::string& attr) const {
struct AttributeProxyStruct {
const std::string* match;
bool found;
};

bool hasAttribute(const std::string& attr) const {
#if R_VERSION >= R_Version(4, 6, 0)
std::vector<std::string> v = attributeNames();
auto it = std::find(v.begin(), v.end(), attr);
return it != v.end(); // if found 'it' points to element equal to 'attr'
auto visitor = [](SEXP name, SEXP attr, void* data) -> SEXP {
AttributeProxyStruct *ptr = static_cast<struct AttributeProxyStruct*>(data);
if (ptr->found) return Rf_ScalarInteger(1); // already found
if (*(ptr->match) == CHAR(Rf_asChar(name))) {
ptr->found = true; // signal we have match
return Rf_ScalarInteger(1);
}
return NULL; // continue
};
AttributeProxyStruct str{&attr, false};
R_mapAttrib(static_cast<const CLASS&>(*this).get__(), visitor, static_cast<void*>(&str));
return str.found;
#else
SEXP attrs = ATTRIB(static_cast<const CLASS&>(*this).get__());
while( attrs != R_NilValue ){
Expand Down
8 changes: 0 additions & 8 deletions inst/include/Rcpp/routines.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,6 @@ SEXP reset_current_error();
int error_occured();
SEXP rcpp_get_current_error();
// void print(SEXP s);
SEXP get_attr_names(SEXP t, SEXP a, void* d);
SEXP get_row_count(SEXP t, SEXP a, void* d);

#else

Expand Down Expand Up @@ -311,12 +309,6 @@ inline attribute_hidden SEXP rcpp_get_current_error(){
// fun(s);
// }

inline attribute_hidden SEXP get_attr_names(SEXP tag, SEXP attr, void* data){
typedef SEXP (*Fun)(SEXP, SEXP, void*);
static Fun fun = GET_CALLABLE("get_attr_names");
return fun(tag, attr, data);
}

#endif


Expand Down
2 changes: 0 additions & 2 deletions src/rcpp_init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,6 @@ void registerFunctions(){
RCPP_REGISTER(Rcpp_precious_remove)
RCPP_REGISTER(Rcpp_cout_get)
RCPP_REGISTER(Rcpp_cerr_get)

RCPP_REGISTER(get_attr_names)
#undef RCPP_REGISTER
}

Expand Down
36 changes: 0 additions & 36 deletions src/utilities.cpp

This file was deleted.

Loading