3

This question is related to my other question: Process list from R to C and access it. I want to process a list of strings: <-list(c("ab", "ac"), c("ab", "zd", "fd"), c("de", "re", "te", "zz")).

I struggle with processing strings instead of integers. I know how to declare a string:

char string[] = "example";

But i obviously still have to learn a lot to process the data - my attempt:

char stor[] = CHAR(VECTOR_ELT(lst, i));
// and then store in in a list --> target[i] = stor;

The c Code - d.c:

/* Including some headers to show the results*/
#include <Rinternals.h>
#include <Rdefines.h>
#include <R.h>
#include <stdlib.h>
#include <stdio.h>
SEXP processlist(SEXP lst){
   int i;
   int len = length(lst);

   char **target = malloc(sizeof(char *)*len);
   for (i = 0;i < l; i++) {
     // use char stor[] to store string instead of Array!?
     char stor[] = CHAR(VECTOR_ELT(lst, i));
     // would i have to modify target[] too?
     target[i] = stor;
   }
   printf("target[0]: %s\n",target[0]);
   printf("target[1]: %s\n",target[1]);
   printf("target[2]: %s\n",target[2]);
   free(target);
   return R_NilValue;
}

Equivilant to other post: d.R (after d.c has been compiled):

dyn.load("d.so")
mylist<-list(c("ab", "ac"), c("ab", "zd", "fd"), c("de", "re", "te", "zz"))
# should be character already? to be sure,...
mylist<-lapply(mylist,as.character)
.Call("processlist", mylist)
Tlatwork
  • 1,445
  • 12
  • 35
  • 1
    just a few references to maybe help: http://adv-r.had.co.nz/C-interface.html, https://github.com/hadley/r-internals, https://stackoverflow.com/questions/38177777/what-exactly-is-the-sexp-data-type-in-rs-c-api-and-why-is-it-used, http://users.stat.umn.edu/~geyer/rc/ – MichaelChirico May 08 '18 at 10:39
  • In general it looks like you still have to allocate memry and copy the value of `stor` to `target[i]`. – Paul Ogilvie May 08 '18 at 10:46
  • much appreciated for the hints. I will go on! – Tlatwork May 08 '18 at 10:48
  • 1
    Keep in mind this: a string -> `char *`; a vector of strings -> `char **`; a `list` of vectors of strings -> `char ***`. – nicola May 08 '18 at 12:40

2 Answers2

3

Expanding my comment: a C variable whose content points to the content of the R list must be of type char *** since a string is a char * and a vector of strings is char **. Say target is that variable. target[i] points to the i-th vector of the list; target[i][j] is the j-th string of the i-th vector. target[i][j][k] is the k-th character of the j-th string of the i-th vector. You need to allocate the outer target to the length of the R list; then you need to allocate every target[i] to be as long as the corresponding vector they point to. Optionally, you allocate every target[i][j] to the length (in number of characters) of the corresponding string if you want to copy the content of the R list. Otherwise, you just assign to target[i][j] the corresponding CHAR(STRING_ELT(VECTOR_ELT(lst,i),j)).

Hopefully, at this point you should be able to do the task by yourself. However, here is a working code:

#include <Rinternals.h>
#include <Rdefines.h>
#include <R.h>
#include <stdlib.h>
#include <stdio.h>
SEXP processlist(SEXP lst){
   int i,j,elLength;
   int len = length(lst);
   SEXP tmp;
   char ***target = malloc(sizeof(char **)*len);
   for (i = 0;i < len; i++) {
     tmp = VECTOR_ELT(lst,i);
     elLength = length(tmp);
     target[i] = malloc(sizeof(char *) * elLength);
     for (j=0;j<elLength;j++) {
       target[i][j] = CHAR(STRING_ELT(tmp,j));
     }
   }
   printf("target[0][0]: %s\n",target[0][0]);
   printf("target[1][1]: %s\n",target[1][1]);
   printf("target[2][2]: %s\n",target[2][2]);
   for (i=0;i<len;i++)
     free(target[i]);
   free(target);
   return R_NilValue;
}
nicola
  • 24,005
  • 3
  • 35
  • 56
  • oh great, thank you! After reading your comment, i arrived at : `CHAR(STRING_ELT(VECTOR_ELT(lst, i), j));` and could print the strings. Next i was working on storing the strings again. I will try to finish it myself and can check here how it´s done right ;) Thanks a lot! – Tlatwork May 08 '18 at 13:31
  • Nicola, you say _if you want to copy the content of the R list..._. Suppose I return the `target` array and later retutrn to R and then come from R to C again, will these strings still exist? So _when_ do I need to copy the strings and _when_ (or: how long) can I use the pointers I get from R? – Paul Ogilvie May 09 '18 at 08:13
  • @PaulOgilvie If you use a `target` outside R with pointers pointing to memory regions handled by R bad things might happen. The R garbage collector may think at a given point that the memory is no longer associated with a R object and free it. So, if you try after that to access it, you end up with a segfault. So, if you intend to use those values outside R, you'd better perform a deep copy of your list. – nicola May 09 '18 at 13:38
  • Yes, I was thinking that. So your example (and OP's) code are fine because the strings are used while in a call from R to C. Any other use would require copying to C storage. Thanks for your explanation. – Paul Ogilvie May 09 '18 at 13:51
0

Assuming that CHAR(VECTOR_ELT(lst, i)); yields a string such as "hello", then char stor[] = CHAR(VECTOR_ELT(lst, i)); declares a character aray stor that is initialized to contain that string.

Then target[i] = stor; would make the character pointer target[i] point to that string. The next round of the loop would do the same for the next entry target[i] but you can assume that they all point to the same stor (and stor goes out of scope after the loop and so would invoke undefined behavior).

Instead, you must safe the string in stor by allocating memory for target[i] and then copying the string:

    target[i]= malloc(strlen(stor)+1);
    strcpy(target[i], stor);
Paul Ogilvie
  • 25,048
  • 4
  • 23
  • 41
  • Thanks you for your answer. According to the question `CHAR(VECTOR_ELT(lst, 0))` would yield `{"ab", "ac"} `. So a list of strings rather than a single string. Therefore, the assumption would not hold, i am afraid? – Tlatwork May 08 '18 at 12:37