[Rd] Using R_MakeExternalPtr
Ross Boylan
ross at biostat.ucsf.edu
Thu Jul 26 05:55:30 CEST 2007
See at bottom for an example.
On Wed, 2007-07-25 at 11:26 -0700, Jonathan Zhou wrote:
> Hi Hin-Tak,
>
> Here is the R code function in where I called the two C++ and further below
> are the 2 C++ functions I used to create the externalptr and use it :
>
> soam.Rapply <- function (x, func, ...,
> join.method=cbind,
> njobs,
> batch.size=100,
> packages=NULL,
> savelist=NULL)
> {
> if(missing(njobs))
> njobs <- max(1,ceiling(nrow(x)/batch.size))
>
> if(!is.matrix(x) && !is.data.frame(x))
> stop("x must be a matrix or data frame")
>
> if(njobs>1)
> {rowSet <- lapply(splitIndices(nrow(x), njobs), function(i) x[i, ,
> drop = FALSE])} else {rowSet <- list(x)}
>
> sesCon <- .Call("soamInit")
>
> script <- " "
>
> fname <- tempfile(pattern = "Rsoam_data", tmpdir = getwd())
> file(fname, open="w+")
> if(!is.null(savelist)) {
> dump(savelist, fname)
> script<-readLines(fname)
> }
>
> if(!is.null(packages))
> for(counter in 1:length(packages))
> {
> temp<-call("library", packages[counter], character.only=TRUE)
> dput(temp, fname)
> pack.call<-readLines(fname)
> script<-append(script, pack.call)
> }
>
> for(counter in 1:njobs)
> {
> caller <- paste("caller", counter, sep = "")
> soam.call<-call("dput", call("apply", X=rowSet[[counter]], MARGIN=1,
> FUN=func), caller)
> dput(soam.call, fname)
> soam.call<-readLines(fname)
>
> temp<-append(script, soam.call)
> final.script = temp[1]
> for(count in 2:length(temp)){
> final.script<-paste(final.script, temp[count], "\n")}
>
> .Call("soamSubmit", counter, sesCon, final.script, packages)
> }
>
> .Call("soamGetResults", sesCon, njobs, join.method, parent.frame())
>
> for(job in 1:njobs)
> {
> caller <- paste("result", job, sep = "")
> temp = dget(caller)
> if(job==1) {retval=temp} else {retval=join.method(retval,temp)}
> }
>
> .Call("soamUninit")
>
> retval
> }
>
> *** Here are the 2 C++ functions:
>
> extern "C"
> {
> SEXP soamInit ()
> {
> // Initialize the API
> SoamFactory::initialize();
>
> // Set up application specific information to be supplied to Symphony
> char appName[] = "SampleAppCPP";
>
> // Set up application authentication information using the default
> security provider
> DefaultSecurityCallback securityCB("Guest", "Guest");
>
> // Connect to the specified application
> ConnectionPtr conPtr = SoamFactory::connect(appName, &securityCB);
>
> // Set up session creation attributes
> SessionCreationAttributes attributes;
> attributes.setSessionName("mySession");
> attributes.setSessionType("ShortRunningTasks");
> attributes.setSessionFlags(SF_RECEIVE_SYNC);
>
> // Create a synchronous session
> Session* sesPtr = conPtr->createSession(attributes);
// I use Rf_protect, though I'd be surprised if that matters given your
use
>
> SEXP out = R_MakeExternalPtr((void*)temp, R_NilValue, R_NilValue);
>
// temp? don't you mean sesPtr?
> return out;
> }
> }
>
> extern "C"
> {
> void soamSubmit (SEXP jobID, //job ID
> SEXP sesCon, //session pointer
> SEXP caller, //objects
> SEXP pack) //packages
> {
> char* savelist = CHAR(STRING_ELT(caller, 0));
> string strTemp = "";
> int job = INTEGER(jobID)[0];
>
> void* temp = R_ExternalPtrAddr(sesCon);
> Session* sesPtr = reinterpret_cast<Session*>(temp);
>
> // Create a message
> MyMessage inMsg(job, /*pack,*/ savelist);
>
> // Send it
> TaskInputHandlePtr input = sesPtr->sendTaskInput(&inMsg);
> }
> }
I've been able to get things working with this pattern (which also is
about assuring memory is freed)
Here's the pattern:
// I needed R_NO_REMAP to avoid name collisions. You may not.
#define R_NO_REMAP 1
#include <R.h>
#include <Rinternals.h>
extern "C" {
// returns an |ExternalPtr|
SEXP makeManager(
@<makeManager args@>);
// user should not need to call
// cleanup
void finalizeManager(SEXP ptr);
}
SEXP makeManager(
@<makeManager args@>){
// .... stuff
Manager* pmanager = new Manager(pd, pm.release(),
*INTEGER(stepNumerator), *INTEGER(stepDenominator),
(*INTEGER(isexact)) != 0);
// one example didn't use |PROTECT()|
SEXP ptr;
Rf_protect(ptr = R_MakeExternalPtr(pmanager, R_NilValue,
R_NilValue));
R_RegisterCFinalizer(ptr, (R_CFinalizer_t) finalizeManager);
Rf_unprotect(1);
return ptr;
}
void finalizeManager(SEXP ptr){
Manager *pmanager = static_cast<Manager *>(R_ExternalPtrAddr(ptr));
delete pmanager;
R_ClearExternalPtr(ptr);
}
I'd love to hear from those more knowledgeable about whether I did
that right, and whether the FinalizerEx call can assure cleanup on
exit.
Make manager needes to be called from R like this
mgr <- .Call("makeManager", args)
The to use it I have things like this:
// ptr is the value returned by |makeManager()|
// |do_what| is an integer requesting the kind of operation
SEXP compute(SEXP ptr, SEXP do_what){
using namespace mspath;
Manager *pmanager = static_cast<Manager *>(R_ExternalPtrAddr(ptr));
// you can probably stop reading there
SEXP newvec;
Rf_protect(newvec = Rf_allocVector(REALSXP, 6u));
double *returned = REAL(newvec);
std::stringstream serror;
try {
pmanager->go(returned, *INTEGER(do_what));
*returned *= -2;
} catch(std::exception& exc) {
serror << "Caught exception: " << exc.what();
} catch(...) {
serror << "Some non-standard exception was thrown" <<
std::endl;
}
if (! serror.str().empty()) {
finalizeManager(ptr); // kill manager
Rf_error("%s", serror.str().c_str());
}
Rf_unprotect(1);
return newvec;
}
More information about the R-devel
mailing list