Skip to content

Commit 8b22574

Browse files
committed
Avoid ATTRIB under R 4.6.0 and use R_mapAttrib
1 parent 3557be1 commit 8b22574

File tree

7 files changed

+102
-4
lines changed

7 files changed

+102
-4
lines changed

ChangeLog

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
* .github/workflows/docker.yaml: Add workflow_dispatch, update
44
checkout action
5+
* inst/include/Rcpp/DataFrame.h (nrow): Rewritten using R_mapAttrib()
6+
avoiding ATTRIB() likely to be removed by R 4.6.0
7+
* inst/include/Rcpp/proxy/AttributeProxy.h (attributeNames): Idem
8+
* src/utilities.cpp: Add two helper functions used by R_mapAttrib()
9+
* inst/include/Rcpp/routines.h: Register two new helper functions
10+
* src/rcpp_init.cpp (registerFunctions): Idem
511

612
2025-12-19 Dirk Eddelbuettel <[email protected]>
713

DESCRIPTION

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Package: Rcpp
22
Title: Seamless R and C++ Integration
3-
Version: 1.1.0.11
4-
Date: 2025-12-17
3+
Version: 1.1.0.11.2
4+
Date: 2025-12-21
55
Authors@R: c(person("Dirk", "Eddelbuettel", role = c("aut", "cre"), email = "[email protected]",
66
comment = c(ORCID = "0000-0001-6419-907X")),
77
person("Romain", "Francois", role = "aut",

inst/include/Rcpp/DataFrame.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,15 @@ namespace Rcpp{
6767
// compact form thereby allocating a huge vector when we just want
6868
// the row.names. Hence this workaround.
6969
inline int nrow() const {
70+
#if R_VERSION >= R_Version(4, 6, 0)
71+
SEXP v = R_mapAttrib(Parent::get__(), get_row_count, R_NilValue);
72+
if (v != NULL && TYPEOF(v) == INTSXP) {
73+
return INTEGER(v)[0];
74+
} else {
75+
// TODO: error?
76+
return NA_INTEGER;
77+
}
78+
#else
7079
SEXP rn = R_NilValue ;
7180
SEXP att = ATTRIB( Parent::get__() ) ;
7281
while( att != R_NilValue ){
@@ -81,9 +90,10 @@ namespace Rcpp{
8190
if (TYPEOF(rn) == INTSXP && LENGTH(rn) == 2 && INTEGER(rn)[0] == NA_INTEGER)
8291
return std::abs(INTEGER(rn)[1]);
8392
return LENGTH(rn);
93+
#endif
8494
}
8595

86-
template <typename T>
96+
template <typename T>
8797
void push_back( const T& object){
8898
Parent::push_back(object);
8999
set_type_after_push();

inst/include/Rcpp/proxy/AttributeProxy.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,16 +78,25 @@ class AttributeProxyPolicy {
7878
}
7979

8080
std::vector<std::string> attributeNames() const {
81-
std::vector<std::string> v ;
81+
std::vector<std::string> v;
82+
#if R_VERSION >= R_Version(4, 6, 0)
83+
R_mapAttrib(static_cast<const CLASS&>(*this).get__(), get_attr_names, (void *) &v);
84+
#else
8285
SEXP attrs = ATTRIB( static_cast<const CLASS&>(*this).get__());
8386
while( attrs != R_NilValue ){
8487
v.push_back( std::string(CHAR(PRINTNAME(TAG(attrs)))) ) ;
8588
attrs = CDR( attrs ) ;
8689
}
90+
#endif
8791
return v ;
8892
}
8993

9094
bool hasAttribute( const std::string& attr) const {
95+
#if R_VERSION >= R_Version(4, 6, 0)
96+
std::vector<std::string> v = attributeNames();
97+
auto it = std::find(v.begin(), v.end(), attr);
98+
return it != v.end(); // if found 'it' points to element equal to 'attr'
99+
#else
91100
SEXP attrs = ATTRIB(static_cast<const CLASS&>(*this).get__());
92101
while( attrs != R_NilValue ){
93102
if( attr == CHAR(PRINTNAME(TAG(attrs))) ){
@@ -96,6 +105,7 @@ class AttributeProxyPolicy {
96105
attrs = CDR( attrs ) ;
97106
}
98107
return false; /* give up */
108+
#endif
99109
}
100110

101111

inst/include/Rcpp/routines.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ SEXP reset_current_error();
7474
int error_occured();
7575
SEXP rcpp_get_current_error();
7676
// void print(SEXP s);
77+
SEXP get_attr_names(SEXP t, SEXP a, void* d);
78+
SEXP get_row_count(SEXP t, SEXP a, void* d);
7779

7880
#else
7981

@@ -309,6 +311,19 @@ inline attribute_hidden SEXP rcpp_get_current_error(){
309311
// fun(s);
310312
// }
311313

314+
inline attribute_hidden SEXP get_attr_names(SEXP tag, SEXP attr, void* data){
315+
typedef SEXP (*Fun)(SEXP, SEXP, void*);
316+
static Fun fun = GET_CALLABLE("get_attr_names");
317+
return fun(tag, attr, data);
318+
}
319+
320+
inline attribute_hidden SEXP get_row_count(SEXP tag, SEXP attr, void* data){
321+
typedef SEXP (*Fun)(SEXP, SEXP, void*);
322+
static Fun fun = GET_CALLABLE("get_row_count");
323+
return fun(tag, attr, data);
324+
}
325+
326+
312327
#endif
313328

314329

src/rcpp_init.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ void registerFunctions(){
127127
RCPP_REGISTER(Rcpp_precious_remove)
128128
RCPP_REGISTER(Rcpp_cout_get)
129129
RCPP_REGISTER(Rcpp_cerr_get)
130+
131+
RCPP_REGISTER(get_attr_names)
132+
RCPP_REGISTER(get_row_count)
130133
#undef RCPP_REGISTER
131134
}
132135

src/utilities.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// utilities.cpp: Rcpp R/C++ interface class library -- attribute utilities
2+
//
3+
// Copyright (C) 2025 - current Dirk Eddelbuettel
4+
//
5+
// This file is part of Rcpp.
6+
//
7+
// Rcpp is free software: you can redistribute it and/or modify it
8+
// under the terms of the GNU General Public License as published by
9+
// the Free Software Foundation, either version 2 of the License, or
10+
// (at your option) any later version.
11+
//
12+
// Rcpp is distributed in the hope that it will be useful, but
13+
// WITHOUT ANY WARRANTY; without even the implied warranty of
14+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
// GNU General Public License for more details.
16+
//
17+
// You should have received a copy of the GNU General Public License
18+
// along with Rcpp. If not, see <http://www.gnu.org/licenses/>.
19+
20+
#define COMPILING_RCPP
21+
22+
#include <vector>
23+
#include <string>
24+
#include <Rcpp/Lighter>
25+
26+
// See WRE Section 6.22.6 'Working with attributes' under R 4.6.0 or later
27+
// This function collects the names from 'tag' in a vector passed via *data
28+
// It is used by AttributeProxyPolicy::attributeNames() in a call to R_mapAttrib
29+
// [[Rcpp::register]]
30+
SEXP get_attr_names(SEXP tag, SEXP attr, void* data) {
31+
std::vector<std::string>* vecptr = static_cast<std::vector<std::string>*>(data);
32+
std::string s{CHAR(Rf_asChar(tag))};
33+
vecptr->push_back(s);
34+
return NULL;
35+
}
36+
37+
// See WRE Section 6.22.6 'Working with attributes' under R 4.6.0 or later
38+
// This function extract the number of rows in a data frame
39+
// It is used DataFrame_impl::nrow() in a call to R_mapAttrib()
40+
// [[Rcpp::register]]
41+
SEXP get_row_count(SEXP tag, SEXP attr, void* data) {
42+
if (tag == R_RowNamesSymbol) {
43+
if (TYPEOF(attr) == INTSXP && LENGTH(attr) == 2 && INTEGER(attr)[0] == NA_INTEGER) {
44+
int n = std::abs(INTEGER(attr)[1]);
45+
//Rcpp::Rcout << "Seeing " << n << std::endl;
46+
return Rf_ScalarInteger(n);
47+
}
48+
if (Rf_isNull(attr)) {
49+
return Rf_ScalarInteger(0);
50+
}
51+
return Rf_ScalarInteger(LENGTH(attr));
52+
}
53+
return NULL;
54+
}

0 commit comments

Comments
 (0)