Consider the getrusage example in the examples directory. Specifically, let's look at the gettimeofday routine.

int gettimeofday(struct timeval *tv, struct timezone *tz);


When we look at this routine, we can "recognize" that the two arguments are out arguments. We might want to call this in several different ways from R.

  • gettimeofday() no arguments and we just want two values back representing the contents of the timeval and timezone structures. In this case, the C code could use local variables and explicitly copy them back to R objects.
    
    SEXP
    R_gettimeofday()
    {
      SEXP ans;
    
      struct timeval tv;
      struct timezone tz;
    
      gettimeofday(&tv, &tz);
      PROTECT(ans = NEW_LIST(2));
      SET_VECTOR_ELT(ans, 0, R_copyStruct_timeval(&tv));
      SET_VECTOR_ELT(ans, 1, R_copyStruct_timezone(&tz));
       /* Put names on the elements of the list ... */
      UNPROTECT(1);
      return(ans);
    }
    
    

  • An alternative approach is that again the R users calls the function with no arguments, but the R function allocates C-level structures for these two arguments and then passes the references to the C routine that calls gettimeofday.
    gettimeofday =
    function()
    {
       tv = alloc("struct timeval")  
       tz = alloc("struct timezone")
       .Call(R_gettimeofday, tv, tz)
    }
    

    The C routine R_gettimeofday is then defined as
    SEXP
    R_gettimeofday(SEXP r_tv, SEXP r_tz)
    {
      SEXP ans;
    
      struct timeval *tv;
      struct timezone *tz;
    
      tv = DEREF_PTR(r_tv, struct timeval);
      tz = DEREF_PTR(r_tz, struct timezone);
    
      gettimeofday(tv, tz);
      PROTECT(ans = NEW_LIST(2));
      SET_VECTOR_ELT(ans, 0, r_tv);
      SET_VECTOR_ELT(ans, 1, r_tz);
       /* Put names on the elements of the list ... */
      UNPROTECT(1);
      return(ans);
    }
    

    This just gets access to the allocated data structures and passes them to gettimeofday and then, with the structures filled in, passes the two back as a list. We could do the last step in R. Alternatively, we could explicitly copy the contents as before rather than returning references. But again, we can do this in R, either in the R function or leaving it to the user
    as(gettimeofday()[[1]], "timeval")
    

So we have several combinations.

  • Create the local variables in C and copy the results back to R objects.
  • Create the local variables from R as references and pass them to C to be filled in and then either copy them back as R objects or return as references to the C data structures and fetch the invdividual elements as needed.


gettimeofday =
function(tv = NULL, tz = NULL, copy = TRUE)
{
  if(!copy) {
       # not copying results so need to have references we can hold onto.
     if(is.null(tv))
        tv = alloc(R_alloc_struct_timeval)
     if(is.null(tz))
        tz = alloc(R_alloc_struct_timezone)
  }

  if(!is.null(tv) && !is(tv, "timevalRef"))
   stop("need a NULL or reference to a timevalRef")
  if(!is.null(tz) && !is(tz, "timezoneRef"))
   stop("need a NULL or reference to a timezoneRef")

  .Call(R_gettimeofday, tv, tz, as.logical(copy))
}


And the C code would look like


SEXP
R_gettimeofday(SEXP r_tz, SEXP r_tv, SEXP r_copy)
{
 bool copy = LOGICAL(r_copy)[0];

  struct timeval dummy_tv, *tv = &dummy_tv;
  struct timezone dummy_tz, *tz = &dummy_tz;

      /* Will actually be an object that has a slot containing the externalptr */
  if(TYPEOF(r_tv) == EXTPTRSXP)
     tv = R_ExternalPtrAddr(r_tv);

  if(TYPEOF(r_tz) == EXTPTRSXP)
     tz = R_ExternalPtrAddr(r_tz);

  gettimeofday(tv, tz);

  PROTECT(ans = NEW_LIST(2));

  if(copy) {
     SET_VECTOR_ELT(ans, 0, R_copyStruct_timeval(tv));
     SET_VECTOR_ELT(ans, 1, R_copyStruct_timezone(tz));
  } else {
     SET_VECTOR_ELT(ans, 0, TYPEOF(r_tv) == EXTPTRSXP ? r_tv : R_createNativeReference(tv, "timevalRef", "timeval"));
     SET_VECTOR_ELT(ans, 1, TYPEOF(r_tz) == EXTPTRSXP ? r_tz : R_createNativeReference(tv, "timezoneRef", "timezone"));
  }
     /* Put names on the elements of the list ... */
  UNPROTECT(1);
}