Skip to content

Passing externalptr to .C()

4 messages · Duncan Murdoch, Rick Sayre, Brian Ripley

#
Greetings.

2.15.0 added this behavior
http://developer.r-project.org/blosxom.cgi/R-devel/NEWS/2012/03/29#n2012-03-29

     o Passing R objects other than atomic vectors, functions, lists and
       environments to .C() is now deprecated and will give a warning.
       Most cases (especially NULL) are actually coding errors.  NULL
       will be disallowed in future.


This seems to make sense, except that this case includes externalptrs.

I have quite a bit of code, designed to interface with various
external hardware devices, which uses this sort of idiom:

# for example
getDeviceInfo <- function(handle) {
     .C("groovyDevice_getDeviceInfo", PACKAGE="groovyDevice",
	handle,			
	status = as.integer(0))[-1]	# skip handle
}

where "handle" was the result of a .Call to a routine which
returned a SEXP which was the result of a R_MakeExternalPtr() call

The "c" routine looked like:
void
groovyDevice_getDeviceInfo(SEXP handle, int *status)
{
     groovyHandle *gh = R_ExternalPtrAddr(handle);
     *status = GroovyStatus(gh);
}

This all used to work fine.  As of 2.15.0, I now get this:
Warning message:
In getDeviceInfo() :
   passing an object of type 'externalptr' to .C (arg 1) is deprecated

Passing the same handle to a .Call() does [of course] work fine.

I thought my usage was exactly as designed.  How then should I be
passing an externalptr to a .C() call?

Cheers
#
On 12-05-11 5:20 PM, Rick Sayre wrote:
I think you shouldn't be doing that.  You should be using .Call.

If your code can handle external pointer objects (as in the example), 
this should be an easy transition.  I haven't tested this, but I think 
the conversion to the .Call interface (less error checking) is simply

  SEXP_getDeviceInfo(SEXP handle, SEXP status)
  {
        groovyHandle *gh = R_ExternalPtrAddr(handle);
        INTEGER(Sstatus)[0] = GroovyStatus(gh);
        return R_NilValue;
  }


If your code is using the pointer just as an opaque handle, I'd suggest 
keeping an array of them, and pass an index into that array:  then .C 
will be fine.

Duncan Murdoch
4 days later
#
Thanks very much for the quick reply.

I'd like to avoid static state in the .so, which is why I'm using
the opaque pointer.  It is indeed possible to convert everything to
.Call(), but the work seems unnecessary given that it used to work
just fine and I am going out of my way to pass things with correct
type conversion and semantics.  Again, this seems like exactly the
usage externalptr was designed for, doesn't it?

It seems I can avoid the warnings by putting the externalptr in
a list:

getDeviceInfo<- function(handle) {
     .C("groovyDevice_getDeviceInfo", PACKAGE="groovyDevice",
	list(handle),	# Avoid pedantic warnings via encapsulation
	status = as.integer(0))[-1] # skip handle
}

void
groovyDevice_getDeviceInfo(SEXP *handle, int *status)
{
     groovyHandle *gh = R_ExternalPtrAddr(*handle);
     *status = GroovyStatus(gh);
}

Perhaps this will help others in the same boat in which I find myself.

Cheers

	--Rick
On 05/11/2012 03:34 PM, Duncan Murdoch wrote:
#
On 16/05/2012 23:30, Rick Sayre wrote:
Actually, 'Writing R Extensions' says:

  .. external pointers should only be used as part of an object with 
normal semantics, for example an attribute or an element of a list.

so that *is* the 'correct semantics' ....
As has been said here many times recently, .C is an old (some say 
obselete) interface, and pre-dates objects such as external pointers. 
It was chance that this ever worked: it was never designed to.

If you find yourself with C[++] code which uses Rinternals.h it should 
use the .Call/.External interfaces, and there is no guarantee that using 
.C for such things will continue to work (as it is all too easy to break 
R's copying semantics and corrupt loaded R code).