For using R's .Call interface to C functions, all the examples I've seen have the C function return type SEXP. Why? What does R actually do with this return type? What happens if I *don't* return a SEXP? Reason I ask, is I've written some R code which allocates two long lists, and then calls a C function with .Call. My C code writes to those two pre-allocated lists, thus, I thought I should NOT need to return any SEXP from my C function. However, if I do that, it segfaults somewhere in "src/main/memory.c". If I instead return the SEXP for one of my two result lists it appears to work fine... But of course I don't understand what's going on here and so don't trust it. The list SEXP I am returning was allocated by R, not by my C code, so why does it make any difference whether my C function returns it or not?>From "src/main/names.c" I see that .Call is implemented by do_dotcallin "src/main/dotcode.c". I don't necessarily understand what that really does, but AFAICT if my C function returns nothing, the do_dotcall's retval should simply remain set to R_NilValue, which should be fine. I must be misunderstanding something here, but I don't know what, and would definitely appreciate any help. Thanks! My R code looks like this: result.1 <- vector("list" ,1e6) result.2 <- vector("list" ,1e6) .Call("my_C_function", result.1, result.2, other.input) My C code looks like this: SEXP result_v; result_v = Rf_allocVector(REALSXP, 5); SET_VECTOR_ELT(result_list_1, k1, result_v); REAL(result_v)[0] = some_number; REAL(result_v)[1] = another_number; /* Also do the same sort of thing for result_list_2. */ return(result_list_1); /* Appears to work ok. */ /* return; */ /* Segfaults. */ -- Andrew Piskorski <atp at piskorski.com> http://www.piskorski.com/
On 27 October 2010 at 19:43, Andrew Piskorski wrote: | For using R's .Call interface to C functions, all the examples I've | seen have the C function return type SEXP. Why? What does R actually | do with this return type? What happens if I *don't* return a SEXP? | | Reason I ask, is I've written some R code which allocates two long | lists, and then calls a C function with .Call. My C code writes to | those two pre-allocated lists, thus, I thought I should NOT need to | return any SEXP from my C function. However, if I do that, it | segfaults somewhere in "src/main/memory.c". | | If I instead return the SEXP for one of my two result lists it appears | to work fine... But of course I don't understand what's going on here | and so don't trust it. The list SEXP I am returning was allocated by | R, not by my C code, so why does it make any difference whether my C | function returns it or not? | | >From "src/main/names.c" I see that .Call is implemented by do_dotcall | in "src/main/dotcode.c". I don't necessarily understand what that | really does, but AFAICT if my C function returns nothing, the | do_dotcall's retval should simply remain set to R_NilValue, which | should be fine. | | I must be misunderstanding something here, but I don't know what, and | would definitely appreciate any help. Thanks! | | | My R code looks like this: | | result.1 <- vector("list" ,1e6) | result.2 <- vector("list" ,1e6) | .Call("my_C_function", result.1, result.2, other.input) | | My C code looks like this: | | SEXP result_v; | result_v = Rf_allocVector(REALSXP, 5); | SET_VECTOR_ELT(result_list_1, k1, result_v); | REAL(result_v)[0] = some_number; | REAL(result_v)[1] = another_number; | /* Also do the same sort of thing for result_list_2. */ | return(result_list_1); /* Appears to work ok. */ | /* return; */ /* Segfaults. */ I don't deal with the C API when I can avoid it. Here is your setup in a simple, self-contanined Rcpp example using inline: ----------------------------------------------------------------------------- library(inline) # body of C++ function src <- 'Rcpp::NumericVector vc = Rcpp::NumericVector(vr); Rcpp::IntegerVector wc = Rcpp::IntegerVector(wr); vc[1] = 2*vc[1]; wc[2] = 3*wc[2]; return Rcpp::wrap(0L); ' # create it fun <- cxxfunction(signature(vr="numeric", wr="integer"), src, plugin="Rcpp") vr <- seq(1.5, 3.5) wr <- seq(1L, 3L) # call it print(fun(vr, wr)) print(vr) print(wr) ----------------------------------------------------------------------------- We create 'vr' and 'wr' at the R level, pass them to C++ where they are taken as numeric and int vectors (with type checking and casts). Your hunch is correct -- SEXP pointer changes continue back to the R level. We do return something (R_NilValue is an option too) as that is how .Call() declared, pure and simple. If I run this in my emacs shell buffer, I get this: ----------------------------------------------------------------------------- edd at max:~$ r /tmp/andrew.R Loading required package: methods [1] 0 [1] 1.5 5.0 3.5 [1] 1 2 9 edd at max:~$ ----------------------------------------------------------------------------- First the zero, then the two (changed) vectors even though I didn't explicitly return them. So this does work -- but I don't like hidden side effects. Much better, in my book, to explicitly return as you'd do with return Rcpp::List::create(Rcpp::Named("newv"=vc, Rcpp::Named("neww"=wc))); which will get you a named list at the R level. Feel free to come over to rcpp-devel is you have questions about the Rcpp and inline parts. Dirk -- Dirk Eddelbuettel | edd at debian.org | http://dirk.eddelbuettel.com
On Oct 27, 2010, at 7:43 PM, Andrew Piskorski wrote:> For using R's .Call interface to C functions, all the examples I've > seen have the C function return type SEXP. Why? What does R actually > do with this return type? What happens if I *don't* return a SEXP? >You are breaking the API = bad things happen. .Call behaves like any function in R which means it takes arguments are returns a value. The result of .Call will be exactly the SEXP that you return from your C code hence you have to return something. It's quite common to R_NilValue which is NULL in R if you have nothing to do.> Reason I ask, is I've written some R code which allocates two long > lists, and then calls a C function with .Call. My C code writes to > those two pre-allocated lists,That's bad! All arguments are essentially read-only so you should never write into them! R has pass-by-value(!) semantics, so semantically you code has nothing to do with the result.1 and result.2 variables since only their *values* are guaranteed to be passed (possibly a copy). The fact that internally R attempts to avoid copying for performance reasons is the only reason why your code may have appeared to work, but it's invalid! So what you really want to do is to have just .Call("my_C_function", other.input) and in your C code you have SEXP result_1 = PROTECT(allocVector(VECSXP, 1e6)); SEXP result_2 = PROTECT(allocVector(VECSXP, 1e6)); /* do something ... */ and in order to pass it out you just create a list and return it: SEXP result = PROTECT(allocVector(VECSXP, 2)); SET_VECTOR_ELT(result, 0, result_1); SET_VECTOR_ELT(result, 1, result_2); UNPROTECT(3); return result; Accordingly you get: l = .Call("my_C_function", other.input) result_1 = l[[1]] result_2 = l[[2]] Cheers, Simon> thus, I thought I should NOT need to > return any SEXP from my C function. However, if I do that, it > segfaults somewhere in "src/main/memory.c". > > If I instead return the SEXP for one of my two result lists it appears > to work fine... But of course I don't understand what's going on here > and so don't trust it. The list SEXP I am returning was allocated by > R, not by my C code, so why does it make any difference whether my C > function returns it or not? > >> From "src/main/names.c" I see that .Call is implemented by do_dotcall > in "src/main/dotcode.c". I don't necessarily understand what that > really does, but AFAICT if my C function returns nothing, the > do_dotcall's retval should simply remain set to R_NilValue, which > should be fine. > > I must be misunderstanding something here, but I don't know what, and > would definitely appreciate any help. Thanks! > > > My R code looks like this: > > result.1 <- vector("list" ,1e6) > result.2 <- vector("list" ,1e6) > .Call("my_C_function", result.1, result.2, other.input) > > My C code looks like this: > > SEXP result_v; > result_v = Rf_allocVector(REALSXP, 5); > SET_VECTOR_ELT(result_list_1, k1, result_v); > REAL(result_v)[0] = some_number; > REAL(result_v)[1] = another_number; > /* Also do the same sort of thing for result_list_2. */ > return(result_list_1); /* Appears to work ok. */ > /* return; */ /* Segfaults. */ > > -- > Andrew Piskorski <atp at piskorski.com> > http://www.piskorski.com/ > > ______________________________________________ > R-devel at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel > >