diff --git a/NAMESPACE b/NAMESPACE index d80abdf..a1a499a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -329,6 +329,8 @@ importFrom(graphics,par) importFrom(graphics,title) importFrom(methods,as) importFrom(methods,is) +importFrom(methods,isGeneric) +importFrom(methods,selectMethod) importFrom(stats,cor) importFrom(stats,median) importFrom(stats,quantile) diff --git a/R/bit64-package.R b/R/bit64-package.R index dbac467..e693715 100644 --- a/R/bit64-package.R +++ b/R/bit64-package.R @@ -704,7 +704,7 @@ #' ramsort ramsortorder repeat.time setattr shellorder shellsort #' shellsortorder still.identical #' @importFrom graphics barplot par title -#' @importFrom methods as is +#' @importFrom methods as is isGeneric selectMethod #' @importFrom stats cor median quantile #' @importFrom utils head packageDescription strOptions tail getS3method #' @export : %in% is.double match order print.cache rank diff --git a/R/setops64.R b/R/setops64.R index 78deb9a..786ad0c 100644 --- a/R/setops64.R +++ b/R/setops64.R @@ -31,9 +31,13 @@ #' @export #' @rdname sets union = function(x, y) { + if ((isS4(x) || isS4(y)) && isGeneric("union")) { + s4_intersect = selectMethod("union", list(x=class(x), y=class(y))) + return(s4_intersect(x, y)) + } if (!(is.integer64(x) || is.integer64(y))) return(base::union(x, y)) - + target_class = target_class(list(x, y)) # try using the benefit of integer64 caching, if possible. I.e. call unique() before as(). x = unique(x) @@ -60,9 +64,13 @@ union = function(x, y) { #' @rdname sets intersect = function(x, y) { if (is.null(x) || is.null(y)) return(NULL) + if ((isS4(x) || isS4(y)) && isGeneric("intersect")) { + s4_intersect = selectMethod("intersect", list(x=class(x), y=class(y))) + return(s4_intersect(x, y)) + } if (!(is.integer64(x) || is.integer64(y))) return(base::intersect(x, y)) - + target_class = target_class(list(x, y)) x = unique(x) class_x = class(x)[1L] @@ -109,6 +117,10 @@ setequal = function(x, y) { #' @export #' @rdname sets setdiff = function(x, y) { + if ((isS4(x) || isS4(y)) && isGeneric("setdiff")) { + s4_intersect = selectMethod("setdiff", list(x=class(x), y=class(y))) + return(s4_intersect(x, y)) + } if (!(is.integer64(x) || is.integer64(y))) return(base::setdiff(x, y)) diff --git a/tests/testthat/test-setops64.R b/tests/testthat/test-setops64.R index 91c6ccb..a3386d6 100644 --- a/tests/testthat/test-setops64.R +++ b/tests/testthat/test-setops64.R @@ -320,3 +320,55 @@ test_that("is.element works (additional cases)", { expect_true(is.element(NA_integer64_, NA)) }) + +test_that("S4 dispatch still happens for classes extending integer64 (#301)", { + methods::setClass("TestS4", representation(data="integer")) + + delete_intersect_generic = !methods::isGeneric("intersect") + suppressMessages(methods::setGeneric("intersect")) + delete_union_generic = !methods::isGeneric("union") + suppressMessages(methods::setGeneric("union")) + delete_setdiff_generic = !methods::isGeneric("setdiff") + suppressMessages(methods::setGeneric("setdiff")) + + methods::setMethod( + "intersect", + signature=c("TestS4", "integer64"), + function(x, y) "Successfully routed to S4 method!" + ) + methods::setMethod( + "union", + signature=c("TestS4", "integer64"), + function(x, y) "Successfully routed to S4 method!" + ) + methods::setMethod( + "setdiff", + signature=c("TestS4", "integer64"), + function(x, y) "Successfully routed to S4 method!" + ) + withr::defer({ + methods::removeMethod("setdiff", signature=c("TestS4", "integer64")) + methods::removeMethod("union", signature=c("TestS4", "integer64")) + methods::removeMethod("intersect", signature=c("TestS4", "integer64")) + methods::removeClass("TestS4") + if (delete_setdiff_generic) methods::removeGeneric("setdiff") + if (delete_union_generic) methods::removeGeneric("union") + if (delete_intersect_generic) methods::removeGeneric("intersect") + }) + + # Instantiate test objects + x = methods::new("TestS4", data = 1L) + y = as.integer64(2L) + + expect_identical(intersect(x, y), "Successfully routed to S4 method!") + expect_identical(union(x, y), "Successfully routed to S4 method!") + expect_identical(setdiff(x, y), "Successfully routed to S4 method!") + # NB: nanoival class is "complex64" -- it kludges complex to be a pair + # of integer64 vectors, but there is no complex64 class, so it just + # shows up on the inheritance chain as 'complex' --> need to ensure + # S4 gets invoked when possible even if the inputs don't directly test + # as being is("integer64"). + expect_identical(intersect(x, x), "Successfully routed to S4 method!") + expect_identical(union(x, x), "Successfully routed to S4 method!") + expect_identical(setdiff(x, x), "Successfully routed to S4 method!") +})