Skip to content

Commit e02b44f

Browse files
authored
Use R C API compliant S4 utilities (#2084)
* Use `Rf_isS4()` * Use R C API compliant S4 utilities
1 parent 601219a commit e02b44f

File tree

8 files changed

+37
-23
lines changed

8 files changed

+37
-23
lines changed

R/proxy.R

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,9 @@ vec_data <- function(x) {
213213
x <- vec_set_attributes(x, list(names = names(x)))
214214
}
215215

216-
# Reset S4 bit in vector-like S4 objects
217-
unset_s4(x)
216+
# Unset S4 bit in vector-like S4 objects
217+
as_not_s4(x)
218218
}
219-
unset_s4 <- function(x) {
220-
.Call(ffi_unset_s4, x)
219+
as_not_s4 <- function(x) {
220+
.Call(ffi_as_not_s4, x)
221221
}

src/init.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ extern r_obj* ffi_outer_names(r_obj*, r_obj*, r_obj*);
8888
extern SEXP vctrs_df_size(SEXP);
8989
extern r_obj* ffi_as_df_col(r_obj*, r_obj*, r_obj*);
9090
extern r_obj* ffi_apply_name_spec(r_obj*, r_obj*, r_obj*, r_obj*);
91-
extern r_obj* ffi_unset_s4(r_obj*);
91+
extern r_obj* ffi_as_not_s4(r_obj*);
9292
extern SEXP vctrs_validate_name_repair_arg(SEXP);
9393
extern SEXP vctrs_validate_minimal_names(SEXP, SEXP);
9494
extern r_obj* ffi_vec_as_names(r_obj*, r_obj*, r_obj*, r_obj*);
@@ -285,7 +285,7 @@ static const R_CallMethodDef CallEntries[] = {
285285
{"vctrs_df_size", (DL_FUNC) &vctrs_df_size, 1},
286286
{"ffi_as_df_col", (DL_FUNC) &ffi_as_df_col, 3},
287287
{"ffi_apply_name_spec", (DL_FUNC) &ffi_apply_name_spec, 4},
288-
{"ffi_unset_s4", (DL_FUNC) &ffi_unset_s4, 1},
288+
{"ffi_as_not_s4", (DL_FUNC) &ffi_as_not_s4, 1},
289289
{"vctrs_altrep_rle_Make", (DL_FUNC) &altrep_rle_Make, 1},
290290
{"vctrs_altrep_rle_is_materialized", (DL_FUNC) &altrep_rle_is_materialized, 1},
291291
{"ffi_altrep_new_lazy_character", (DL_FUNC) &ffi_altrep_new_lazy_character, 1},

src/list-combine.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1573,7 +1573,7 @@ bool vec_implements_base_c(r_obj* x) {
15731573
return false;
15741574
}
15751575

1576-
if (IS_S4_OBJECT(x)) {
1576+
if (r_is_s4(x)) {
15771577
return s4_find_method(x, s4_c_method_table) != r_null;
15781578
} else {
15791579
return s3_find_method("c", x, base_method_table) != r_null;

src/proxy-restore.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ r_obj* vec_restore_dispatch(r_obj* x, r_obj* to) {
7171
r_obj* vec_restore_default(r_obj* x, r_obj* to, enum vctrs_ownership ownership) {
7272
r_obj* attrib = r_attrib(to);
7373

74-
const bool is_s4 = IS_S4_OBJECT(to);
74+
const bool is_s4 = r_is_s4(to);
7575

7676
if (attrib == r_null && !is_s4) {
7777
return x;
@@ -158,7 +158,7 @@ r_obj* vec_restore_default(r_obj* x, r_obj* to, enum vctrs_ownership ownership)
158158
}
159159

160160
if (is_s4) {
161-
r_mark_s4(x);
161+
x = r_as_s4(x);
162162
}
163163

164164
FREE(n_prot);

src/proxy.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -247,10 +247,8 @@ r_obj* vec_proxy_unwrap(r_obj* x) {
247247
}
248248

249249

250-
r_obj* ffi_unset_s4(r_obj* x) {
251-
x = r_clone_referenced(x);
252-
r_unmark_s4(x);
253-
return x;
250+
r_obj* ffi_as_not_s4(r_obj* x) {
251+
return r_as_not_s4(x);
254252
}
255253

256254

src/utils.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,7 @@ static SEXP s4_get_method(const char* cls, SEXP table) {
590590

591591
// For S4 objects, the `table` is specific to the generic
592592
SEXP s4_find_method(SEXP x, SEXP table) {
593-
if (!IS_S4_OBJECT(x)) {
593+
if (!r_is_s4(x)) {
594594
return R_NilValue;
595595
}
596596

src/utils.h

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -271,13 +271,29 @@ SEXP r_clone_referenced(SEXP x);
271271

272272
SEXP r_call_n(SEXP fn, SEXP* tags, SEXP* cars);
273273

274-
static inline SEXP r_mark_s4(SEXP x) {
275-
SET_S4_OBJECT(x);
276-
return(x);
274+
static inline bool r_is_s4(SEXP x) {
275+
return Rf_isS4(x);
277276
}
278-
static inline SEXP r_unmark_s4(SEXP x) {
279-
UNSET_S4_OBJECT(x);
280-
return(x);
277+
static inline SEXP r_as_s4(SEXP x) {
278+
// - Return value must be used, unlike `SET_S4_OBJECT()`
279+
// - `Rf_asS4()` calls `shallow_duplicate(x)` if `MAYBE_SHARED(x)`
280+
// - `flag = 1` goes through `SET_S4_OBJECT()`
281+
// - `complete` is never utilized when `flag = 1`
282+
const Rboolean flag = 1;
283+
const int complete = 0;
284+
return Rf_asS4(x, flag, complete);
285+
}
286+
static inline SEXP r_as_not_s4(SEXP x) {
287+
// - Return value must be used, unlike `UNSET_S4_OBJECT()`
288+
// - `Rf_asS4()` calls `shallow_duplicate(x)` if `MAYBE_SHARED(x)`
289+
// - `flag = 0` goes through `UNSET_S4_OBJECT()`
290+
// - `complete` is for S4 objects that wrap a "complete" S3 object by placing
291+
// it in the `.Data` slot. If you set `complete = 1`, it will unwrap and
292+
// return that, which we don't want. If `complete = 0`, no additional
293+
// behavior will happen beyond the `UNSET_S4_OBJECT()` call.
294+
const Rboolean flag = 0;
295+
const int complete = 0;
296+
return Rf_asS4(x, flag, complete);
281297
}
282298

283299
bool r_has_name_at(SEXP names, R_len_t i);

tests/testthat/test-s4.R

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,16 +60,16 @@ test_that("proxy and data", {
6060
expect_true(isS4(vec_restore(vec_data(x), x)))
6161
})
6262

63-
test_that("unset_s4() copies and works", {
63+
test_that("as_not_s4() copies and works", {
6464
# Initial condition
6565
x <- rando()
6666
expect_true(isS4(x))
6767

6868
# Unsetting has no side effect on x
69-
unset_s4(x)
69+
as_not_s4(x)
7070
expect_true(isS4(x))
7171

7272
# Unsetting actually works
73-
y <- unset_s4(x)
73+
y <- as_not_s4(x)
7474
expect_false(isS4(y))
7575
})

0 commit comments

Comments
 (0)