Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
fd99010
implement 25% of #297
pachadotdev Dec 17, 2024
f5d6054
clang format
pachadotdev Dec 27, 2024
713f147
bool -> int + using transform for bool -> double
pachadotdev Dec 27, 2024
bbee709
fix #406
pachadotdev Jan 1, 2025
496dd6b
borrow from @traversc's push_back_fast
pachadotdev Jan 1, 2025
008ab7c
add benchmarks
pachadotdev Jan 1, 2025
fb3583f
roxygen comments on cpp side (works ok with 1 roxygenised function pe…
pachadotdev Jan 3, 2025
14531db
almost there with rx/no rx in the same file
pachadotdev Jan 4, 2025
c750115
correctly handles roxygen in cpp files
pachadotdev Jan 4, 2025
fb848c6
revert some styler changes to make the changes more clear
pachadotdev Jan 4, 2025
17cac60
fix multi line roxygen examples
pachadotdev Jan 4, 2025
7a436d6
add roxygen example to documentation
pachadotdev Jan 4, 2025
a781c99
consider the case where a file does not exist
pachadotdev Jan 4, 2025
f717713
do not roxygenize chunk in vignette
pachadotdev Jan 4, 2025
fbb365f
workaround for roxygen comments in cpp chunks
pachadotdev Jan 5, 2025
c91f4ef
revert to eval = F
pachadotdev Jan 5, 2025
b1d8ecd
implement #312
pachadotdev Feb 1, 2025
7a72ec9
implement fix for #445
pachadotdev May 9, 2025
fcb7756
only one cstdlib in data_frame.hpp
pachadotdev May 9, 2025
012e4e0
use inline constexpr for doubles/integers/list/logicals/strings
pachadotdev May 9, 2025
2be6542
use noexcept in protect/sexp
pachadotdev May 9, 2025
91c3bd4
noexcept in operator SEXP (attribute proxy)
pachadotdev May 9, 2025
472f40f
+ noexcept
pachadotdev May 9, 2025
4209cf0
clang format
pachadotdev May 9, 2025
b0b3f47
rollback noexcept
pachadotdev May 9, 2025
8fdaaef
clang format
pachadotdev May 9, 2025
a80ae91
fix typos
pachadotdev May 9, 2025
ff8e314
fix r_Vector
pachadotdev May 9, 2025
f7fd0e0
fix strings
pachadotdev May 9, 2025
e0229d5
rollback strings
pachadotdev May 9, 2025
ddf5300
rollback strings
pachadotdev May 9, 2025
9851423
use a lamba to unwind protect around the loop
pachadotdev Aug 26, 2025
c016918
Use values added to a vector with push_back immediately
pachadotdev Aug 26, 2025
8ee112a
fix clang format
pachadotdev Aug 26, 2025
8093e12
llvm formatt
pachadotdev Sep 7, 2025
6783944
Global symbol visibility
pachadotdev Sep 15, 2025
5114411
clang ofrmat
pachadotdev Sep 15, 2025
acdcf61
clang format
pachadotdev Sep 15, 2025
e340f78
clang format 12
pachadotdev Sep 15, 2025
3c6e5c6
clang fmt
pachadotdev Sep 15, 2025
224b9af
check unix tests
pachadotdev Sep 15, 2025
72d2f37
Merge pull request #1 from pachadotdev/issue460
pachadotdev Sep 16, 2025
36ef9b7
Merge pull request #2 from pachadotdev/issue453
pachadotdev Sep 16, 2025
2c56e1a
Merge pull request #3 from pachadotdev/issue452
pachadotdev Sep 16, 2025
7256c37
Merge pull request #4 from pachadotdev/issue406
pachadotdev Sep 16, 2025
8e7a80d
Merge pull request #5 from pachadotdev/issue297
pachadotdev Sep 16, 2025
66d37b1
Merge pull request #6 from pachadotdev/compilationspeed
pachadotdev Sep 16, 2025
1487985
Merge pull request #7 from pachadotdev/nullable_extptr
pachadotdev Sep 16, 2025
5eff766
Merge branch 'main' into roxygen
pachadotdev Sep 16, 2025
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
65 changes: 52 additions & 13 deletions R/register.R
Original file line number Diff line number Diff line change
Expand Up @@ -77,17 +77,16 @@ cpp_register <- function(path = ".", quiet = !is_interactive(), extension = c(".
cli::cli_alert_success("generated file {.file {basename(r_path)}}")
}


call_entries <- get_call_entries(path, funs$name, package)

cpp_function_registration <- glue::glue_data(funs, ' {{
"_cpp11_{name}", (DL_FUNC) &_{package}_{name}, {n_args}}}, ',
n_args = viapply(funs$args, nrow)
)

cpp_function_registration <- glue::glue_collapse(cpp_function_registration, sep = "\n")
cpp_function_registration <- glue::glue_collapse(cpp_function_registration, sep = "\n")

extra_includes <- character()
extra_includes <- character()
if (pkg_links_to_rcpp(path)) {
extra_includes <- c(extra_includes, "#include <cpp11/R.hpp>", "#include <Rcpp.h>", "using namespace Rcpp;")
}
Expand Down Expand Up @@ -215,35 +214,75 @@ generate_init_functions <- function(funs) {
}

generate_r_functions <- function(funs, package = "cpp11", use_package = FALSE) {
funs <- funs[c("name", "return_type", "args")]
funs <- funs[c("name", "return_type", "args", "file", "line", "decoration")]

if (use_package) {
package_call <- glue::glue(', PACKAGE = "{package}"')
package_names <- glue::glue_data(funs, '"_{package}_{name}"')
} else {
package_names <- glue::glue_data(funs, '`_{package}_{name}`')
package_names <- glue::glue_data(funs, "`_{package}_{name}`")
package_call <- ""
}

funs$package <- package
funs$package_call <- package_call
funs$list_params <- vcapply(funs$args, glue_collapse_data, "{name}")
funs$params <- vcapply(funs$list_params, function(x) if (nzchar(x)) paste0(", ", x) else x)
is_void <- funs$return_type == "void"
funs$calls <- ifelse(is_void,
glue::glue_data(funs, 'invisible(.Call({package_names}{params}{package_call}))'),
glue::glue_data(funs, '.Call({package_names}{params}{package_call})')
glue::glue_data(funs, "invisible(.Call({package_names}{params}{package_call}))"),
glue::glue_data(funs, ".Call({package_names}{params}{package_call})")
)

out <- glue::glue_data(funs, '
{name} <- function({list_params}) {{
{calls}
}}
')
# Parse and associate Roxygen comments
funs$roxygen_comment <- mapply(function(file, line) {
if (file.exists(file)) {
comments <- extract_roxygen_comments(file)
matched_comment <- ""
for (comment in comments) {
# Check if the comment directly precedes the function without gaps
if (line == comment$line + 1) {
matched_comment <- comment$text
break
}
}
matched_comment
} else {
""
}
}, funs$file, funs$line, SIMPLIFY = TRUE)

# Generate R functions with or without Roxygen comments
out <- mapply(function(name, list_params, calls, roxygen_comment) {
if (nzchar(roxygen_comment)) {
glue::glue("{roxygen_comment}\n{name} <- function({list_params}) {{\n\t{calls}\n}}")
} else {
glue::glue("{name} <- function({list_params}) {{\n {calls}\n}}")
}
}, funs$name, funs$list_params, funs$calls, funs$roxygen_comment, SIMPLIFY = TRUE)

out <- glue::trim(out)
out <- glue::glue_collapse(out, sep = "\n\n")
unclass(out)
}

extract_roxygen_comments <- function(file) {
lines <- readLines(file)
roxygen_start <- grep("^/\\* roxygen start", lines)
roxygen_end <- grep("roxygen end \\*/$", lines)

if (length(roxygen_start) == 0 || length(roxygen_end) == 0) {
return(list())
}

roxygen_comments <- mapply(function(start, end) {
roxygen_lines <- lines[(start + 1):(end - 1)]
roxygen_lines <- sub("^", "#' ", roxygen_lines)
list(line = end, text = paste(roxygen_lines, collapse = "\n"))
}, roxygen_start, roxygen_end, SIMPLIFY = FALSE)

roxygen_comments
}

wrap_call <- function(name, return_type, args) {
call <- glue::glue('{name}({list_params})', list_params = glue_collapse_data(args, "cpp11::as_cpp<cpp11::decay_t<{type}>>({name})"))
if (return_type == "void") {
Expand Down
21 changes: 15 additions & 6 deletions R/source.R
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@
#' uses 'CXX11' if unset.
#' @param dir The directory to store the generated source files. `tempfile()` is
#' used by default. The directory will be removed if `clean` is `TRUE`.
#' @param local Passed to [dyn.load()]. If `TRUE` (the default) the shared
#' library is loaded with local symbols; if `FALSE` symbols are made global
#' (equivalent to `dyn.load(..., local = FALSE)`), which can be required when
#' other shared objects need to see RTTI/vtable symbols from this library.
#' @note See the unit test that demonstrates this usage at
#' \code{tests/testthat/test-source-local.R} (shows how `local = FALSE` exports
#' the necessary symbols so separate shared objects can link against them).
#' @return For [cpp_source()] and `[cpp_function()]` the results of
#' [dyn.load()] (invisibly). For `[cpp_eval()]` the results of the evaluated
#' expression.
Expand Down Expand Up @@ -65,7 +72,7 @@
#' }
#'
#' @export
cpp_source <- function(file, code = NULL, env = parent.frame(), clean = TRUE, quiet = TRUE, cxx_std = Sys.getenv("CXX_STD", "CXX11"), dir = tempfile()) {
cpp_source <- function(file, code = NULL, env = parent.frame(), clean = TRUE, quiet = TRUE, cxx_std = Sys.getenv("CXX_STD", "CXX11"), dir = tempfile(), local = TRUE) {
stop_unless_installed(c("brio", "callr", "cli", "decor", "desc", "glue", "tibble", "vctrs"))
if (!missing(file) && !file.exists(file)) {
stop("Can't find `file` at this path:\n", file, "\n", call. = FALSE)
Expand Down Expand Up @@ -145,7 +152,7 @@ cpp_source <- function(file, code = NULL, env = parent.frame(), clean = TRUE, qu
brio::write_lines(r_functions, r_path)
source(r_path, local = env)

dyn.load(shared_lib, local = TRUE, now = TRUE)
dyn.load(shared_lib, local = local, now = TRUE)
}

the <- new.env(parent = emptyenv())
Expand Down Expand Up @@ -183,7 +190,7 @@ generate_makevars <- function(includes, cxx_std) {

#' @rdname cpp_source
#' @export
cpp_function <- function(code, env = parent.frame(), clean = TRUE, quiet = TRUE, cxx_std = Sys.getenv("CXX_STD", "CXX11")) {
cpp_function <- function(code, env = parent.frame(), clean = TRUE, quiet = TRUE, cxx_std = Sys.getenv("CXX_STD", "CXX11"), local = TRUE) {
cpp_source(code = paste(c('#include "cpp11.hpp"',
"using namespace ::cpp11;",
"namespace writable = ::cpp11::writable;",
Expand All @@ -193,15 +200,16 @@ cpp_function <- function(code, env = parent.frame(), clean = TRUE, quiet = TRUE,
env = env,
clean = clean,
quiet = quiet,
cxx_std = cxx_std
cxx_std = cxx_std,
local = local
)
}

utils::globalVariables("f")

#' @rdname cpp_source
#' @export
cpp_eval <- function(code, env = parent.frame(), clean = TRUE, quiet = TRUE, cxx_std = Sys.getenv("CXX_STD", "CXX11")) {
cpp_eval <- function(code, env = parent.frame(), clean = TRUE, quiet = TRUE, cxx_std = Sys.getenv("CXX_STD", "CXX11"), local = TRUE) {
cpp_source(code = paste(c('#include "cpp11.hpp"',
"using namespace ::cpp11;",
"namespace writable = ::cpp11::writable;",
Expand All @@ -214,7 +222,8 @@ cpp_eval <- function(code, env = parent.frame(), clean = TRUE, quiet = TRUE, cxx
env = env,
clean = clean,
quiet = quiet,
cxx_std = cxx_std
cxx_std = cxx_std,
local = local
)
f()
}
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,13 @@ Please note that the cpp11 project is released with a [Contributor Code of Condu

cpp11 would not exist without Rcpp.
Thanks to the Rcpp authors, Dirk Eddelbuettel, Romain Francois, JJ Allaire, Kevin Ushey, Qiang Kou, Nathan Russell, Douglas Bates and John Chambers for their work writing and maintaining Rcpp.

## Clang format

To match GHA, use clang-format-12 to format C++ code. With systems that provide clang-format-14 or newer, you can use Docker:

```bash
docker run --rm -v "$PWD":/work -w /work ubuntu:22.04 bash -lc "\
apt-get update && apt-get install -y clang-format-12 && \
find . -name '*.cpp' -o -name '*.hpp' -o -name '*.h' | xargs -r clang-format-12 -i"
```
2 changes: 1 addition & 1 deletion cpp11test/DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ Suggests:
xml2
LazyData: true
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.1.1
RoxygenNote: 7.3.2
5 changes: 5 additions & 0 deletions cpp11test/NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Generated by roxygen2: do not edit by hand

export(roxcpp2_)
export(roxcpp3_)
export(roxcpp4_)
export(roxcpp5_)
export(roxcpp7_)
export(run_tests)
exportPattern("_$")
importFrom(Rcpp,sourceCpp)
Expand Down
85 changes: 85 additions & 0 deletions cpp11test/R/cpp11.R
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,63 @@ rcpp_release_ <- function(n) {
invisible(.Call(`_cpp11test_rcpp_release_`, n))
}

notroxcpp1_ <- function(x) {
.Call(`_cpp11test_notroxcpp1_`, x)
}

#' @title Roxygenise C++ function II
#' @param x numeric value
#' @description Dummy function to test roxygen2. It adds 2.0 to a double.
#' @export
#' @examples roxcpp2_(1.0)
roxcpp2_ <- function(x) {
.Call(`_cpp11test_roxcpp2_`, x)
}

#' @title Roxygenise C++ function III
#' @param x numeric value
#' @description Dummy function to test roxygen2. It adds 3.0 to a double.
#' @export
#' @examples roxcpp3_(1.0)
roxcpp3_ <- function(x) {
.Call(`_cpp11test_roxcpp3_`, x)
}

#' @title Roxygenise C++ function IV
#' @param x numeric value
#' @description Dummy function to test roxygen2. It adds 4.0 to a double.
#' @export
#' @examples roxcpp4_(1.0)
roxcpp4_ <- function(x) {
.Call(`_cpp11test_roxcpp4_`, x)
}

#' @title Roxygenise C++ function V
#' @param x numeric value
#' @description Dummy function to test roxygen2. It adds 5.0 to a double.
#' @export
#' @examples roxcpp5_(1.0)
roxcpp5_ <- function(x) {
.Call(`_cpp11test_roxcpp5_`, x)
}

notroxcpp6_ <- function(x) {
.Call(`_cpp11test_notroxcpp6_`, x)
}

#' @title Roxygenise C++ function VII
#' @param x numeric value
#' @description Dummy function to test roxygen2. It adds 7.0 to a double.
#' @export
#' @examples
#' my_fun <- function(x) {
#' roxcpp7_(x)
#' }
#' @seealso \code{\link{roxcpp1_}}
roxcpp7_ <- function(x) {
.Call(`_cpp11test_roxcpp7_`, x)
}

cpp11_safe_ <- function(x_sxp) {
.Call(`_cpp11test_cpp11_safe_`, x_sxp)
}
Expand All @@ -168,6 +225,26 @@ string_push_back_ <- function() {
.Call(`_cpp11test_string_push_back_`)
}

grow_strings_cpp11_ <- function(n, seed) {
.Call(`_cpp11test_grow_strings_cpp11_`, n, seed)
}

grow_strings_rcpp_ <- function(n, seed) {
.Call(`_cpp11test_grow_strings_rcpp_`, n, seed)
}

grow_strings_manual_ <- function(n, seed) {
.Call(`_cpp11test_grow_strings_manual_`, n, seed)
}

assign_cpp11_ <- function(n, seed) {
.Call(`_cpp11test_assign_cpp11_`, n, seed)
}

assign_rcpp_ <- function(n, seed) {
.Call(`_cpp11test_assign_rcpp_`, n, seed)
}

sum_dbl_for_ <- function(x) {
.Call(`_cpp11test_sum_dbl_for_`, x)
}
Expand Down Expand Up @@ -236,6 +313,14 @@ rcpp_push_and_truncate_ <- function(size_sxp) {
.Call(`_cpp11test_rcpp_push_and_truncate_`, size_sxp)
}

nullable_extptr_1 <- function() {
.Call(`_cpp11test_nullable_extptr_1`)
}

nullable_extptr_2 <- function() {
.Call(`_cpp11test_nullable_extptr_2`)
}

test_destruction_inner <- function() {
invisible(.Call(`_cpp11test_test_destruction_inner`))
}
Expand Down
35 changes: 35 additions & 0 deletions cpp11test/bench/strings.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
pkgload::load_all("cpp11test")

bench::press(len = as.integer(10^(0:6)), {
bench::mark(
assign_cpp11_(n = len, 123L),
assign_rcpp_(n = len, 123L),
iterations = 20
)
})[c("expression", "len", "min", "mem_alloc", "n_itr", "n_gc")]

# Longer benchmark, lots of gc
len <- as.integer(10^7)
bench::mark(
cpp11 = cpp11_push_and_truncate_(len),
rcpp = rcpp_push_and_truncate_(len),
min_iterations = 200
)[c("expression", "min", "mem_alloc", "n_itr", "n_gc")]

bench::press(len = as.integer(10^(0:6)), {
bench::mark(
grow_strings_cpp11_(len, 123L),
grow_strings_rcpp_(len, 123L),
grow_strings_manual_(len, 123L),
iterations = 20
)
})[c("expression", "len", "min", "mem_alloc", "n_itr", "n_gc")]

# Longer benchmark, lots of gc
len <- as.integer(10^7)
bench::mark(
cpp11 = cpp11_grow_strings_(len),
rcpp = rcpp_grow_strings_(len),
manual = manual_grow_strings_(len),
min_iterations = 200
)[c("expression", "min", "mem_alloc", "n_itr", "n_gc")]
17 changes: 17 additions & 0 deletions cpp11test/man/roxcpp2_.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions cpp11test/man/roxcpp3_.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading