@@ -36,16 +36,19 @@ make_c_bridge <- function(fsub, strict = TRUE, headers = TRUE) {
3636 scope = scope
3737 )
3838
39- # maybe define and allocate the output var
39+ # maybe define and allocate the output var(s)
4040 n_protected <- 0L
41- return_var <- get(closure_return_var_name(closure ), scope )
42- if (! return_var @ name %in% closure_arg_names ) {
43- return_var @ modified <- TRUE
44- assign(return_var @ name , return_var , scope )
45- append(c_body ) <- return_var_c_defs(return_var , fsub @ scope )
46- add(n_protected ) <- 1L # allocated return var
47- if (return_var @ rank > 1 ) {
48- add(n_protected ) <- 1L # allocated _dim_sexp
41+ return_var_names <- closure_return_var_names(closure )
42+ # Deduplicate by the underlying variable name to avoid duplicate C defs
43+ for (return_var in mget(unique(unname(return_var_names )), scope )) {
44+ if (! return_var @ name %in% closure_arg_names ) {
45+ return_var @ modified <- TRUE
46+ assign(return_var @ name , return_var , scope )
47+ append(c_body ) <- return_var_c_defs(return_var , fsub @ scope )
48+ add(n_protected ) <- 1L # allocated return var
49+ if (return_var @ rank > 1 ) {
50+ add(n_protected ) <- 1L # allocated _dim_sexp
51+ }
4952 }
5053 }
5154
@@ -64,10 +67,49 @@ make_c_bridge <- function(fsub, strict = TRUE, headers = TRUE) {
6467 if (uses_rng ) " PutRNGstate();" ,
6568 " "
6669 )
67- if (n_protected > 0 ) {
68- append(c_body ) <- glue(" UNPROTECT({n_protected});" )
70+ # Determine if the closure returns a list call or a single symbol
71+ is_list_return <- is_call(last(body(closure )), quote(list ))
72+
73+ if (length(return_var_names ) == 1L && ! is_list_return ) {
74+ if (n_protected > 0 ) {
75+ append(c_body ) <- glue(" UNPROTECT({n_protected});" )
76+ }
77+ append(c_body ) <- glue(" return {return_var_names};" )
78+ } else {
79+ return_var_values <- unname(return_var_names )
80+ provided_names <- names(return_var_names )
81+ if (is.null(provided_names )) {
82+ provided_names <- rep(" " , length(return_var_values ))
83+ }
84+ has_any_names <- any(nzchar(provided_names ))
85+
86+ append(c_body ) <- c(
87+ glue(
88+ " SEXP _ans = PROTECT(Rf_allocVector(VECSXP, {length(return_var_values)}));"
89+ ),
90+ imap(return_var_values , function (nm , i ) {
91+ glue(" SET_VECTOR_ELT(_ans, {i-1}, {nm});" )
92+ })
93+ )
94+
95+ if (has_any_names ) {
96+ names_to_use <- provided_names
97+ append(c_body ) <- c(
98+ glue(
99+ " SEXP _names = PROTECT(Rf_allocVector(STRSXP, {length(return_var_values)}));"
100+ ),
101+ imap(names_to_use , function (nm , i ) {
102+ glue(' SET_STRING_ELT(_names, {i-1}, Rf_mkChar("{nm}"));' )
103+ }),
104+ " Rf_setAttrib(_ans, R_NamesSymbol, _names);"
105+ )
106+ append(c_body ) <- glue(" UNPROTECT({n_protected + 2});" )
107+ } else {
108+ append(c_body ) <- glue(" UNPROTECT({n_protected + 1});" )
109+ }
110+
111+ append(c_body ) <- " return _ans;"
69112 }
70- append(c_body ) <- glue(" return {return_var@name};" )
71113
72114 c_args <- paste(" SEXP" , names(formals(closure )), collapse = " , " )
73115 c_body <- as_glue(str_flatten_lines(c_body ))
@@ -416,12 +458,39 @@ as_friendly_size_expression <- function(d) {
416458 deparse1(d )
417459}
418460
419- closure_return_var_name <- function (closure ) {
420- return_var_name <- last(body(closure ))
421- if (! is.symbol(return_var_name )) {
422- stop(" return value must be a symbol" )
461+ closure_return_var_names <- function (closure ) {
462+ return_var_expr <- last(body(closure ))
463+ if (is.symbol(return_var_expr )) {
464+ val <- as.character(return_var_expr )
465+ # Return named to keep interface consistent
466+ return (setNames(val , val ))
467+ }
468+ if (is_call(return_var_expr , quote(list ))) {
469+ args <- as.list(return_var_expr )[- 1L ]
470+ if (length(args ) == 0L ) {
471+ stop(" return list must contain at least one element" )
472+ }
473+ vals <- map_chr(args , as.character )
474+ nms <- names(args )
475+ if (is.null(nms )) {
476+ nms <- rep(" " , length(vals ))
477+ }
478+ # validate names are syntactic when provided
479+ if (any(nzchar(nms ))) {
480+ bad <- nzchar(nms ) & make.names(nms ) != nms
481+ if (any(bad )) {
482+ stop(
483+ " only syntactic names are valid, encountered: " ,
484+ paste0(nms [bad ], sep = " , " )
485+ )
486+ }
487+ }
488+ # Use provided names when present; fallback to symbol names
489+ # nms <- ifelse(nzchar(nms), nms, vals)
490+ return (setNames(vals , nms ))
423491 }
424- as.character(return_var_name )
492+ # # is it redundent ? new_fortran_subroutine also errors ?
493+ stop(" return value must be a symbol or list of symbols" )
425494}
426495
427496
0 commit comments