Skip to content

Commit 23ae34f

Browse files
authored
Feature remove redundant declarations (#199)
1 parent 5c6feec commit 23ae34f

File tree

4 files changed

+98
-14
lines changed

4 files changed

+98
-14
lines changed

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# cpp11 (development version)
2+
* Removed redundant `.Call calls` in cpp11.cpp file (@sbearrows, #170)
23
* Allow cpp11 decorators of the form `cpp11::linking_to` (@sbearrows, #193)
34

45
# cpp11 0.3.1
@@ -7,6 +8,7 @@
78

89
# cpp11 0.3.0
910

11+
1012
## New functions and features
1113
* New `x.empty()` method to check if a vector is empty (@sbearrows, #182)
1214
* New `x.named()` method to check if a vector is named (@sbearrows, #186)

R/register.R

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ cpp_register <- function(path = ".", quiet = FALSE) {
7070
cli::cli_alert_success("generated file {.file {basename(r_path)}}")
7171
}
7272

73-
call_entries <- get_call_entries(path)
73+
74+
call_entries <- get_call_entries(path, funs$name, package)
7475

7576
cpp_function_registration <- glue::glue_data(funs, ' {{
7677
"_cpp11_{name}", (DL_FUNC) &_{package}_{name}, {n_args}}}, ',
@@ -244,7 +245,7 @@ wrap_call <- function(name, return_type, args) {
244245
}
245246
}
246247

247-
get_call_entries <- function(path) {
248+
get_call_entries <- function(path, names, package) {
248249
con <- textConnection("res", local = TRUE, open = "w")
249250

250251
withr::with_collate("C",
@@ -258,12 +259,31 @@ get_call_entries <- function(path) {
258259
close(con)
259260

260261
start <- grep("/* .Call calls */", res, fixed = TRUE)
262+
261263
end <- grep("};", res, fixed = TRUE)
262264

263265
if (length(start) == 0) {
264266
return("")
265267
}
266-
res[seq(start, end)]
268+
269+
redundant <- glue::glue_collapse(glue::glue('extern SEXP _{package}_{names}'), sep = '|')
270+
271+
if (length(redundant) > 0) {
272+
redundant <- paste0("^", redundant)
273+
res <- res[!grepl(redundant, res)]
274+
}
275+
276+
end <- grep("};", res, fixed = TRUE)
277+
278+
call_calls <- startsWith(res, "extern SEXP")
279+
280+
if(any(call_calls)) {
281+
return(res[seq(start, end)])
282+
}
283+
284+
mid <- grep("static const R_CallMethodDef CallEntries[] = {", res, fixed = TRUE)
285+
286+
res[seq(mid, end)]
267287
}
268288

269289
pkg_links_to_rcpp <- function(path) {

tests/testthat/helper.R

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@ pkg_path <- function(pkg) {
1212
dirname(pkg$.__enclos_env__$private$path)
1313
}
1414

15+
get_funs <- function(path) {
16+
all_decorations <- decor::cpp_decorations(path, is_attribute = TRUE)
17+
get_registered_functions(all_decorations, "cpp11::register", quiet = FALSE)
18+
}
19+
20+
get_package_name <- function(path) {
21+
desc::desc_get("Package", file = file.path(path, "DESCRIPTION"))
22+
}
23+
1524
glue_str <- function(...) {
1625
glue::as_glue(unlist(list(...)))
1726
}

tests/testthat/test-register.R

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,16 @@ describe("pkg_links_to_rcpp", {
2525
describe("get_call_entries", {
2626
it("returns an empty string if there are no R files", {
2727
pkg <- local_package()
28-
expect_equal(get_call_entries(pkg_path(pkg)), "")
28+
path <- pkg_path(pkg)
29+
expect_equal(get_call_entries(path, get_funs(path)$name, get_package_name(path)), "")
2930
})
3031

3132
it("returns an empty string if there are no .Call calls", {
3233
pkg <- local_package()
33-
dir.create(file.path(pkg_path(pkg), "R"))
34-
writeLines("foo <- function() 1", file.path(pkg_path(pkg), "R", "foo.R"))
35-
expect_equal(get_call_entries(pkg_path(pkg)), "")
34+
path <- pkg_path(pkg)
35+
dir.create(file.path(path, "R"))
36+
writeLines("foo <- function() 1", file.path(path, "R", "foo.R"))
37+
expect_equal(get_call_entries(path, get_funs(path)$name, get_package_name(path)), "")
3638
})
3739

3840
it("Errors for invalid packages", {
@@ -44,7 +46,7 @@ describe("get_call_entries", {
4446
writeLines("Package: testPkg", file.path(pkg, "DESCRIPTION"))
4547
dir.create(file.path(pkg, "R"))
4648
writeLines('foo <- function() .Call("bar")', file.path(pkg, "R", "foo.R"))
47-
expect_error(get_call_entries(pkg), "has no 'NAMESPACE' file")
49+
expect_error(get_call_entries(pkg, get_funs(path)$name, get_package_name(pkg)), "has no 'NAMESPACE' file")
4850
})
4951

5052
it("returns an empty string for packages with .Call entries and NAMESPACE files", {
@@ -53,10 +55,11 @@ describe("get_call_entries", {
5355
skip_if(getRversion() < "3.4")
5456

5557
pkg <- local_package()
56-
dir.create(file.path(pkg_path(pkg), "R"))
57-
writeLines('foo <- function() .Call("bar")', file.path(pkg_path(pkg), "R", "foo.R"))
58+
path <- pkg_path(pkg)
59+
dir.create(file.path(path, "R"))
60+
writeLines('foo <- function() .Call("bar")', file.path(path, "R", "foo.R"))
5861
expect_equal(
59-
get_call_entries(pkg_path(pkg)),
62+
get_call_entries(path, get_funs(path)$name, get_package_name(path)),
6063
c("/* .Call calls */",
6164
"extern SEXP bar();",
6265
"",
@@ -67,6 +70,59 @@ describe("get_call_entries", {
6770
)
6871
)
6972
})
73+
it("works with multiple register functions.", {
74+
pkg <- local_package()
75+
p <- pkg_path(pkg)
76+
dir.create(file.path(p, "src"))
77+
file.copy(test_path("multiple.cpp"), file.path(p, "src", "multiple.cpp"))
78+
79+
cpp_register(p)
80+
cpp_bindings <- file.path(p, "src", "cpp11.cpp")
81+
expect_equal(read_file(cpp_bindings),
82+
"// Generated by cpp11: do not edit by hand
83+
// clang-format off
84+
85+
86+
#include \"cpp11/declarations.hpp\"
87+
88+
// multiple.cpp
89+
int foo();
90+
extern \"C\" SEXP _testPkg_foo() {
91+
BEGIN_CPP11
92+
return cpp11::as_sexp(foo());
93+
END_CPP11
94+
}
95+
// multiple.cpp
96+
double bar(bool run);
97+
extern \"C\" SEXP _testPkg_bar(SEXP run) {
98+
BEGIN_CPP11
99+
return cpp11::as_sexp(bar(cpp11::as_cpp<cpp11::decay_t<bool>>(run)));
100+
END_CPP11
101+
}
102+
// multiple.cpp
103+
bool baz(bool run, int value);
104+
extern \"C\" SEXP _testPkg_baz(SEXP run, SEXP value) {
105+
BEGIN_CPP11
106+
return cpp11::as_sexp(baz(cpp11::as_cpp<cpp11::decay_t<bool>>(run), cpp11::as_cpp<cpp11::decay_t<int>>(value)));
107+
END_CPP11
108+
}
109+
110+
extern \"C\" {
111+
static const R_CallMethodDef CallEntries[] = {
112+
{\"_testPkg_bar\", (DL_FUNC) &_testPkg_bar, 1},
113+
{\"_testPkg_baz\", (DL_FUNC) &_testPkg_baz, 2},
114+
{\"_testPkg_foo\", (DL_FUNC) &_testPkg_foo, 0},
115+
{NULL, NULL, 0}
116+
};
117+
}
118+
119+
extern \"C\" void R_init_testPkg(DllInfo* dll){
120+
R_registerRoutines(dll, NULL, CallEntries, NULL, NULL);
121+
R_useDynamicSymbols(dll, FALSE);
122+
R_forceSymbols(dll, TRUE);
123+
}
124+
")
125+
})
70126
})
71127

72128
describe("wrap_call", {
@@ -533,9 +589,6 @@ extern \"C\" SEXP _testPkg_foo() {
533589
}
534590
535591
extern \"C\" {
536-
/* .Call calls */
537-
extern SEXP _testPkg_foo();
538-
539592
static const R_CallMethodDef CallEntries[] = {
540593
{\"_testPkg_foo\", (DL_FUNC) &_testPkg_foo, 0},
541594
{NULL, NULL, 0}

0 commit comments

Comments
 (0)