I have some C data I want to pass back to R opaquely, and then back to C. I understand external pointers are the way to do so. I'm trying to find how they interact with garbage collection and object lifetime, and what I need to do so that the memory lives until the calling R process ends. Could anyone give me some pointers? I haven't found much documentation. An earlier message suggested looking at simpleref.nw, but I can't find that file. So the overall pattern, from R, would look like opaque <- setup(arg1, arg2, ....) # setup calls a C fn docompute(arg1, argb, opaque) # many times. docompute also calls C # and then when I return opaque and the memory it's wrapping get #cleaned up. If necessary I could do teardown(opaque) # at the end "C" is actually C++ via a C interface, if that matters. In particular, the memory allocated will likely be from the C++ run-time, and needs C++ destructors. -- Ross Boylan wk: (415) 514-8146 185 Berry St #5700 ross at biostat.ucsf.edu Dept of Epidemiology and Biostatistics fax: (415) 514-8150 University of California, San Francisco San Francisco, CA 94107-1739 hm: (415) 550-1062
use a C finalizer... void MyObject_finalize(SEXP opaque) { MyObject *obj = (MyObject*)R_ExternalPtrAddr(opaque); if(NULL != obj) delete obj; } and in your setup code... PROTECT(p = R_MakeExternalPtr(...)); R_RegisterCFinalizer(p,MyObject_finalize); On Dec 9, 2005, at 3:04 PM, Ross Boylan wrote:> I have some C data I want to pass back to R opaquely, and then back to > C. I understand external pointers are the way to do so. > > I'm trying to find how they interact with garbage collection and > object > lifetime, and what I need to do so that the memory lives until the > calling R process ends. > > Could anyone give me some pointers? I haven't found much > documentation. > An earlier message suggested looking at simpleref.nw, but I can't find > that file. > > So the overall pattern, from R, would look like > opaque <- setup(arg1, arg2, ....) # setup calls a C fn > docompute(arg1, argb, opaque) # many times. docompute also calls C > # and then when I return opaque and the memory it's wrapping get > #cleaned up. If necessary I could do > teardown(opaque) # at the end > > "C" is actually C++ via a C interface, if that matters. In > particular, > the memory allocated will likely be from the C++ run-time, and > needs C++ > destructors. > > -- > Ross Boylan wk: (415) 514-8146 > 185 Berry St #5700 ross at biostat.ucsf.edu > Dept of Epidemiology and Biostatistics fax: (415) 514-8150 > University of California, San Francisco > San Francisco, CA 94107-1739 hm: (415) 550-1062 > > ______________________________________________ > R-devel at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel--- Byron Ellis (ellis at stat.harvard.edu) "Oook" -- The Librarian
I don't entirely follow Ross's outline below (?how does the R object 'opaque' get to feature in the call to 'docompute'?), but I have written large amounts of R-linked Delphi code with persistent Delphi objects, using only the '.C' interface and no external pointers. You might find this useful if you want to avoid the complexities of '.Call' and SEXPs etc. NB the C/Delphi distincition is not important here. (i)The first '.C' call uses Delphi code to allocate (using Delphi's own memory manager) and set up a persistent object that R doesn't know about. The Delphi code then returns an "opaque" integer-valued handle to R, which is the address of the object in the Delphi DLL's world. (ii) For subsequent '.C' calls to Delphi from R, I pass in the handle, which I then typecast to an object pointer inside Delphi. Hence I can "find" the persistent object. This way there can be several persistent objects simultaneously-- I'm not limited to global variables in the Delphi DLL. (iii) There is a final cleanup '.C' call which deallocates the persistent object. I sometimes also automate the destruction in the cleanup code of the DLL, just in case the R user forgets to cleanup. I have also gotten this to work after replacing Delphi's memory manager with R's own-- there was no difference (but apparently no point either, from what I've been told). There are some further notes inside a PDF on Duncan Murdoch's page: http://www.stats.uwo.ca/faculty/murdoch/software/compilingDLLs/pascal.ht ml The notes are very Delphi-flavoured (and refer to S not R, since I've used the method both with S-plus and R) but the point should-- or might-- be clear. HTH Mark Mark Bravington CSIRO Mathematical & Information Sciences Marine Laboratory Castray Esplanade Hobart 7001 TAS ph (+61) 3 6232 5118 fax (+61) 3 6232 5012 mob (+61) 438 315 623> -----Original Message----- > From: r-devel-bounces at r-project.org > [mailto:r-devel-bounces at r-project.org] On Behalf Of Byron Ellis > Sent: Saturday, 10 December 2005 11:24 AM > To: Ross Boylan > Cc: R Development List > Subject: Re: [Rd] external pointers > > use a C finalizer... > > void MyObject_finalize(SEXP opaque) { > MyObject *obj = (MyObject*)R_ExternalPtrAddr(opaque); > if(NULL != obj) delete obj; > } > > and in your setup code... > > PROTECT(p = R_MakeExternalPtr(...)); > R_RegisterCFinalizer(p,MyObject_finalize); > > > > > > > On Dec 9, 2005, at 3:04 PM, Ross Boylan wrote: > > > I have some C data I want to pass back to R opaquely, and > then back to > > C. I understand external pointers are the way to do so. > > > > I'm trying to find how they interact with garbage collection and > > object lifetime, and what I need to do so that the memory > lives until > > the calling R process ends. > > > > Could anyone give me some pointers? I haven't found much > > documentation. > > An earlier message suggested looking at simpleref.nw, but I > can't find > > that file. > > > > So the overall pattern, from R, would look like opaque <- > setup(arg1, > > arg2, ....) # setup calls a C fn docompute(arg1, argb, opaque) # > > many times. docompute also calls C # and then when I return > opaque and > > the memory it's wrapping get #cleaned up. If necessary I could do > > teardown(opaque) # at the end > > > > "C" is actually C++ via a C interface, if that matters. In > > particular, the memory allocated will likely be from the > C++ run-time, > > and needs C++ destructors. > > > > -- > > Ross Boylan wk: (415) 514-8146 > > 185 Berry St #5700 > ross at biostat.ucsf.edu > > Dept of Epidemiology and Biostatistics fax: (415) 514-8150 > > University of California, San Francisco > > San Francisco, CA 94107-1739 hm: (415) 550-1062 > > > > ______________________________________________ > > R-devel at r-project.org mailing list > > https://stat.ethz.ch/mailman/listinfo/r-devel > > --- > Byron Ellis (ellis at stat.harvard.edu) > "Oook" -- The Librarian > > ______________________________________________ > R-devel at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel > >
Hi Simon Thanks for that... see below for my feeble counterblasts! (And two questions.)> Mark, > > On Dec 12, 2005, at 7:48 AM, <Mark.Bravington at csiro.au> wrote: > > > (i)The first '.C' call uses Delphi code to allocate (using Delphi's > > own memory manager) and set up a persistent object that R > doesn't know > > about. The Delphi code then returns an "opaque" > integer-valued handle > > to R, which is the address of the object in the Delphi DLL's world. > > That's a bad idea for a couple of reasons, the main being > that integer is not guaranteed to be able to hold a pointer - > it won't work on any 64-bit platform.Very true, though 64 bit systems are not a big worry for Delphi 6.0 writers... ;) I did once speculate about hacking around this sort of thing by encoding into character strings instead of integers, uggg.> Second drawback is that > you have no way to link the life of the R object to your > Delphi object, because there is no way gc will tell you that > the object is gone. This will lead to memory leaks. [Been > there, done that ;)] Both issues are solved by the external pointers.> > > (iii) There is a final cleanup '.C' call which deallocates the > > persistent object. I sometimes also automate the destruction in the > > cleanup code of the DLL, just in case the R user forgets to cleanup. > > How do you make sure the no one uses the now invalid integer value? > There can be many copies of your proxy object around and they > all point to nirvana ...Sorry, that was lack of clarity on my part about what I meant by "persistent". Whenever I use this mechanism, I'm careful to ensure that the C/Delphi objects only have lifetimes *within* a function, and to include an 'on.exit' call to the "destructor"-- I would never create global objects pointing to ephemeral C structures. Admittedly, this relies on programming self-discipline, and has no cast-iron anti-nirvana mechanism! Ross' original query seems very much along these lines, though. If I was really creating global persistent objects, then 'externalptr' would definitely be much better. Where would be best to read about externalptr? I'm having trouble finding material in the manuals or the site-search. And would I need to use all the .Call machinery and C headers and SEXP etc in order to handle externalptr objects?> > Cheers, > Simonbye Mark