[R] C++: Appending Values onto an R-Vector.

Jim Java
Sun, 21 Dec 2003 00:10:49 -0000

[Jim Java]
>Hi folks. I posted this question a few days ago, but maybe it got
>lost because of the code I included with it. I'm having a problem
>using the SET_LENGTH() macro in an R extension I'm writing in C++.
>In a function within the extension I use SET_LENGTH() to resize R
>vectors so as to allow the concatenation of single values onto the
>vectors -- it's a "push back" function to append values onto
the
>end of a vector. However, when I use this function to push back a
>large number of values one at a time, Rgui.exe (I'm working with R
>1.8.1 in Windows XP) crashes from an Access Violation; if, however,
>I pre-allocate space (is the space actually pre-allocated?) for the
>vector (say with NEW_INTEGER(n) rather than NEW_INTEGER(0)) and
>insert values into the allocated slots, the code works fine. If
>you'd like to see some test code, I've already posted it here:
>
>https://www.stat.math.ethz.ch/pipermail/r-help/2003-December/041871.html
>
>Here's my question, then: Is SET_LENGTH() the appropriate way to
>create space for tacking values onto the end of an R-vector in C++,
>or should I be trying to tack them on in some other way?

[Brian Ripley]
>You UNPROTECT before calling lengthgets, and that is I think part
>of your problem.  You need to use REPROTECT...

[Jim Java]
This problem is solved, so I want to post a follow-up for reference
in the archives. My thanks to Prof. Ripley for helping me out: the
problem was indeed in using UNPROTECT before lengthgets() rather than
using REPROTECT afterwards. My revised test code is listed below, also
for reference.

<CPP Code>
  SEXP R_SimplePushBackTest(SEXP args)
  {
    SEXP arg1, arg2, int_vect;

    PROTECT(arg1 = AS_INTEGER(CADR(args)));
    int n_reps = INTEGER_POINTER(arg1)[0];
    PROTECT(arg2 = AS_LOGICAL(CADDR(args)));
    bool full_alloc = (LOGICAL_POINTER(arg2)[0] ? true : false);
    PROTECT_INDEX int_vect_pindex;
    if (full_alloc)
      PROTECT_WITH_INDEX(int_vect = NEW_INTEGER(n_reps), &int_vect_pindex);
    else
      PROTECT_WITH_INDEX(int_vect = NEW_INTEGER(0), &int_vect_pindex);

    for (int i = 0; i < n_reps; ++i) {
      Rprintf("  ** Iteration %d:\n", i + 1);
      if (full_alloc)
        INTEGER_POINTER(int_vect)[i] = i;
      else {
        // This works now! --
        SET_LENGTH(int_vect, GET_LENGTH(int_vect) + 1);
        REPROTECT(int_vect, int_vect_pindex);
        INTEGER_POINTER(int_vect)[GET_LENGTH(int_vect) - 1] = i;
      }
    }

    SEXP out, names, cls;

    PROTECT(out = NEW_LIST(1));
    SET_VECTOR_ELT(out, 0, int_vect);

    PROTECT(names = NEW_CHARACTER(1));
    SET_STRING_ELT(names, 0, COPY_TO_USER_STRING("integer.vector"));
    SET_NAMES(out, names);

    PROTECT(cls = NEW_CHARACTER(1));
    SET_STRING_ELT(cls, 0, COPY_TO_USER_STRING("pushback"));
    classgets(out, cls);

    UNPROTECT(6);
    return out;
  }
</CPP Code>

<R Code>
  nreps=50000
  allocate=FALSE
  sink("pushback_test.txt")
  test.pushback=.External("R_SimplePushBackTest", as.integer(nreps),
as.logical(allocate))
  print(test.pushback)
  sink()
</R Code>