@@ -89,9 +89,6 @@ register <- function(path = NULL, quiet = !is_interactive(), extension = c(".cpp
8989 cpp_function_registration <- glue :: glue_collapse(cpp_function_registration , sep = " \n " )
9090
9191 extra_includes <- character ()
92- if (pkg_links_to_rcpp(path )) {
93- extra_includes <- c(extra_includes , " #include <cpp4r/R.hpp>" , " #include <Rcpp.h>" , " using namespace Rcpp;" )
94- }
9592
9693 pkg_types <- c(
9794 file.path(path , " src" , paste0(package , " _types.h" )),
@@ -230,8 +227,60 @@ generate_r_functions <- function(funs, package = "cpp4r", use_package = FALSE) {
230227 }
231228
232229 funs $ package_call <- package_call
233- funs $ list_params <- vcapply(funs $ args , glue_collapse_data , " {name}" )
234- funs $ params <- vcapply(funs $ list_params , function (x ) if (nzchar(x )) paste0(" , " , x ) else x )
230+
231+ # Extract default values and create parameter lists
232+ funs $ param_info <- lapply(funs $ args , function (args_df ) {
233+ if (nrow(args_df ) == 0 ) {
234+ return (list (params = " " , args = " " , checks = " " ))
235+ }
236+
237+ # Parse default values from the type column (they appear after '=')
238+ param_names <- args_df $ name
239+ param_types <- args_df $ type
240+
241+ # Extract defaults (format: "type name = value" becomes "value")
242+ defaults <- vapply(param_types , function (t ) {
243+ if (grepl(" =" , t )) {
244+ sub(" .*=\\ s*" , " " , t )
245+ } else {
246+ " "
247+ }
248+ }, character (1 ))
249+
250+ # Clean up types (remove default value parts)
251+ clean_types <- vapply(param_types , function (t ) {
252+ trimws(sub(" \\ s*=.*$" , " " , t ))
253+ }, character (1 ))
254+
255+ # Generate R function parameters with defaults
256+ params_with_defaults <- vapply(seq_along(param_names ), function (i ) {
257+ if (nzchar(defaults [i ])) {
258+ # Convert C++ defaults to R defaults
259+ r_default <- convert_cpp_default_to_r(defaults [i ])
260+ paste0(param_names [i ], " = " , r_default )
261+ } else {
262+ param_names [i ]
263+ }
264+ }, character (1 ))
265+
266+ # Generate type checking/coercion code
267+ checks <- vapply(seq_along(param_names ), function (i ) {
268+ generate_type_check(param_names [i ], clean_types [i ])
269+ }, character (1 ))
270+ checks <- checks [nzchar(checks )]
271+
272+ list (
273+ params = paste(params_with_defaults , collapse = " , " ),
274+ args = paste(param_names , collapse = " , " ),
275+ checks = if (length(checks ) > 0 ) paste0(" \t " , checks , collapse = " \n " ) else " "
276+ )
277+ })
278+
279+ funs $ list_params <- vapply(funs $ param_info , function (x ) x $ params , character (1 ))
280+ funs $ call_args <- vapply(funs $ param_info , function (x ) x $ args , character (1 ))
281+ funs $ type_checks <- vapply(funs $ param_info , function (x ) x $ checks , character (1 ))
282+
283+ funs $ params <- vcapply(funs $ call_args , function (x ) if (nzchar(x )) paste0(" , " , x ) else x )
235284 is_void <- funs $ return_type == " void"
236285 funs $ calls <- ifelse(is_void ,
237286 glue :: glue_data(funs , " invisible(.Call({package_names}{params}{package_call}))" ),
@@ -256,20 +305,89 @@ generate_r_functions <- function(funs, package = "cpp4r", use_package = FALSE) {
256305 }
257306 }, funs $ file , funs $ line , SIMPLIFY = TRUE )
258307
259- # Generate R functions with or without Roxygen comments
260- out <- mapply(function (name , list_params , calls , roxygen_comment ) {
308+ # Generate R functions with type checks and defaults
309+ out <- mapply(function (name , list_params , calls , roxygen_comment , type_checks ) {
310+ body <- if (nzchar(type_checks )) {
311+ paste0(" \n " , type_checks , " \n\t " , calls , " \n " )
312+ } else {
313+ paste0(" \n\t " , calls , " \n " )
314+ }
315+
261316 if (nzchar(roxygen_comment )) {
262- glue :: glue(" {roxygen_comment}\n {name} <- function({list_params}) {{\n\t {calls} \n }}" )
317+ glue :: glue(" {roxygen_comment}\n {name} <- function({list_params}) {{{body} }}" )
263318 } else {
264- glue :: glue(" {name} <- function({list_params}) {{\n {calls} \n }}" )
319+ glue :: glue(" {name} <- function({list_params}) {{{body} }}" )
265320 }
266- }, funs $ name , funs $ list_params , funs $ calls , funs $ roxygen_comment , SIMPLIFY = TRUE )
321+ }, funs $ name , funs $ list_params , funs $ calls , funs $ roxygen_comment , funs $ type_checks , SIMPLIFY = TRUE )
267322
268323 out <- glue :: trim(out )
269324 out <- glue :: glue_collapse(out , sep = " \n\n " )
270325 unclass(out )
271326}
272327
328+ # Helper function to convert C++ default values to R
329+ convert_cpp_default_to_r <- function (cpp_default ) {
330+ cpp_default <- trimws(cpp_default )
331+
332+ # Handle common cases
333+ if (cpp_default == " true" || cpp_default == " TRUE" ) {
334+ return (" TRUE" )
335+ } else if (cpp_default == " false" || cpp_default == " FALSE" ) {
336+ return (" FALSE" )
337+ } else if (grepl(" ^[0-9]+L?$" , cpp_default )) {
338+ # Integer literal
339+ return (paste0(sub(" L$" , " " , cpp_default ), " L" ))
340+ } else if (grepl(" ^[0-9.]+[fF]?$" , cpp_default )) {
341+ # Float/double literal
342+ return (sub(" [fF]$" , " " , cpp_default ))
343+ } else if (grepl(' ^".*"$' , cpp_default ) || grepl(" ^'.*'$" , cpp_default )) {
344+ # String literal - keep as is
345+ return (cpp_default )
346+ } else if (cpp_default == " NULL" || cpp_default == " nullptr" ) {
347+ return (" NULL" )
348+ }
349+
350+ # Default: keep as-is and hope for the best
351+ cpp_default
352+ }
353+
354+ # Helper function to generate type checking/coercion code
355+ generate_type_check <- function (param_name , param_type ) {
356+ # Map C++ types to R coercion functions
357+ if (param_type == " int" || grepl(" ^int[[:space:]]*$" , param_type )) {
358+ return (glue :: glue(" {param_name} <- as.integer({param_name})" ))
359+ } else if (param_type == " double" || grepl(" ^double[[:space:]]*$" , param_type )) {
360+ return (glue :: glue(" {param_name} <- as.numeric({param_name})" ))
361+ } else if (param_type == " bool" || grepl(" ^bool[[:space:]]*$" , param_type )) {
362+ return (glue :: glue(" {param_name} <- as.logical({param_name})" ))
363+ } else if (grepl(" string" , param_type , ignore.case = TRUE )) {
364+ return (glue :: glue(" {param_name} <- as.character({param_name})" ))
365+ }
366+
367+ # Handle cpp4r matrix types - set proper storage mode
368+ if (grepl(" integers_matrix" , param_type )) {
369+ return (glue :: glue(" storage.mode({param_name}) <- \" integer\" " ))
370+ } else if (grepl(" doubles_matrix" , param_type )) {
371+ return (glue :: glue(" storage.mode({param_name}) <- \" double\" " ))
372+ } else if (grepl(" logicals_matrix" , param_type )) {
373+ return (glue :: glue(" storage.mode({param_name}) <- \" logical\" " ))
374+ }
375+
376+ # Handle cpp4r vector types - set proper storage mode as well
377+ if (grepl(" ^integers[^_]" , param_type ) || param_type == " integers" ) {
378+ return (glue :: glue(" storage.mode({param_name}) <- \" integer\" " ))
379+ } else if (grepl(" ^doubles[^_]" , param_type ) || param_type == " doubles" ) {
380+ return (glue :: glue(" storage.mode({param_name}) <- \" double\" " ))
381+ } else if (grepl(" ^logicals[^_]" , param_type ) || param_type == " logicals" ) {
382+ return (glue :: glue(" storage.mode({param_name}) <- \" logical\" " ))
383+ } else if (grepl(" ^strings[^_]" , param_type ) || param_type == " strings" ) {
384+ return (glue :: glue(" storage.mode({param_name}) <- \" character\" " ))
385+ }
386+
387+ # For other cpp4r types, don't add checks (they handle conversion internally)
388+ return (" " )
389+ }
390+
273391extract_roxygen_comments <- function (file ) {
274392 lines <- readLines(file )
275393
@@ -356,12 +474,6 @@ get_call_entries <- function(path, names, package) {
356474 res [seq(mid , end )]
357475}
358476
359- pkg_links_to_rcpp <- function (path ) {
360- deps <- desc :: desc_get_deps(file.path(path , " DESCRIPTION" ))
361-
362- any(deps $ type == " LinkingTo" & deps $ package == " Rcpp" )
363- }
364-
365477get_register_needs <- function () {
366478 res <- read.dcf(system.file(" DESCRIPTION" , package = " cpp4r" ))[, " Config/Needs/cpp4r/register" ]
367479 strsplit(res , " [[:space:]]*,[[:space:]]*" )[[1 ]]
0 commit comments