From edd at debian.org  Thu Jan 27 03:07:01 2011
From: edd at debian.org (Dirk Eddelbuettel)
Date: Wed, 26 Jan 2011 20:07:01 -0600
Subject: [Rd] Dealing with R list objects in C/C++
In-Reply-To: <A02FE3A5690A624994A6A0BC0DEEA920072898926A@NYKPCMMGMB05.INTRANET.BARCAPINT.COM>
References: <A02FE3A5690A624994A6A0BC0DEEA920072898926A@NYKPCMMGMB05.INTRANET.BARCAPINT.COM>
Message-ID: <19776.54085.632636.289766@max.nulle.part>


Hi Wayne,

On 26 January 2011 at 17:56, Wayne.Zhang at barclayscapital.com wrote:
| Hi,
| 
| I'd like to construct an R list object in C++, fill it with relevant data, and pass it to an R function which will return a different list object back.  I have browsed through all the R manuals, and examples under tests/Embedding, but can't figure out the correct way.  Below is my code snippet:
| 
|     #include <Rinternals.h>
| // Rf_initEmbeddedR and other setups already performed
| 
|     SEXP arg, ret;
| 
|     // this actually creates a pairlist.  I can't find any API that creates a list
| PROTECT(arg = allocList(3));
| 
| // I want the first element to be type integer, second double, and third a vector.
|     INTEGER(arg)[0]  = 1;            // <- runtime exception: "INTEGER() can only be applied to a 'integer', not a 'pairlist'
|     REAL(arg)[1] = 2.5;               // control never reached here
| 
|     VECTOR_PTR(arg)[2] = allocVector(REALSXP, 4);
|     REAL(VECTOR_PTR(arg)[2])[0] = 10.0;
|     REAL(VECTOR_PTR(arg)[2])[1] = 11.0;
|     REAL(VECTOR_PTR(arg)[2])[2] = 12.0;
|     REAL(VECTOR_PTR(arg)[2])[3] = 13.0;
| 
|     PROTECT(call = lang2(install(entryPoint.c_str()), arg));
| 
| ret = R_tryEval(call, R_GlobalEnv, &errorOccurred);
| 
| 
| I'll be grateful if you can point me to any online docs/samples.

This is a non-trivial problem when the use the C API provided by R. It is all
documented, but you need to study the 'Writing R Extensions' in some detail,
as well as maybe 'R Programming' by Gentleman and/or 'Software for Data
Analysis' by Chambers.

But there is another API you can use. It is provided by RInside (to embed R
inside C++) which uses Rcpp (for R and C++ integration).  Install those two
packages from CRAN, and then drop the few lines below as a file, say,
wayne.cpp in the examples/standard/ directory of RInside. Saying 'make wayne'
will build an executable, using proper flags and linker options, and you can
run that:

edd at max:~/svn/rinside/pkg/inst/examples/standard$ make wayne
g++ -I/usr/share/R/include -I/usr/local/lib/R/site-library/Rcpp/include -I"/usr/local/lib/R/site-library/RInside/include" -O3 -pipe -g -Wall    wayne.cpp  -L/usr/lib64/R/lib -lR  -lblas -llapack -L/usr/local/lib/R/site-library/Rcpp/lib -lRcpp -Wl,-rpath,/usr/local/lib/R/site-library/Rcpp/lib -L/usr/local/lib/R/site-library/RInside/lib -lRInside -Wl,-rpath,/usr/local/lib/R/site-library/RInside/lib -o wayne
edd at max:~/svn/rinside/pkg/inst/examples/standard$ ./wayne 
Showing list content:
L[0] 1
L[1] 2.5
L[2][0] 10
L[2][1] 11
Showing list content:
L[0] 42
L[1] 42
L[2][0] 10
L[2][1] 42
edd at max:~/svn/rinside/pkg/inst/examples/standard$ 

The code a list as you spec'ed with int, double and vector. The list is shown
on stdout, then passed to R, transformed by R and shown again at the C++ level.

Questions on RInside and Rcpp are welcome on the rcpp-devel list.

Hope this helps,  Dirk

-----------------------------------------------------------------------------
#include <RInside.h>                    // for the embedded R via RInside

void show(const Rcpp::List & L) {
    // this function is cumbersome as we haven't defined << operators
    std::cout << "Showing list content:\n";
    std::cout << "L[0] " << Rcpp::as<int>(L[0]) << std::endl;
    std::cout << "L[1] " << Rcpp::as<double>(L[1]) << std::endl;
    Rcpp::IntegerVector v = Rcpp::as<Rcpp::IntegerVector>(L[2]);
    std::cout << "L[2][0] " << v[0] << std::endl;
    std::cout << "L[2][1] " << v[1] << std::endl;
}

int main(int argc, char *argv[]) {

    // create an embedded R instance
    RInside R(argc, argv);               

    Rcpp::List mylist(3);
    mylist[0] = 1;
    mylist[1] = 2.5;
    Rcpp::IntegerVector v(2); v[0] = 10; v[1] = 11; // with C++0x we could assign directly
    mylist[2] = v;
    show(mylist);

    R["myRlist"] = mylist;
    std::string r_code = "myRlist[[1]] = 42; myRlist[[2]] = 42.0; myRlist[[3]][2] = 42; myRlist";
    
    Rcpp::List reslist = R.parseEval(r_code);
    show(reslist);

    exit(0);
}
-----------------------------------------------------------------------------


-- 
Dirk Eddelbuettel | edd at debian.org | http://dirk.eddelbuettel.com



[Rd] Dealing with R list objects in C/C++
Dirk Eddelbuettel