From hpages at fhcrc.org  Wed Jan 30 03:14:12 2008
From: hpages at fhcrc.org (Herve Pages)
Date: Tue, 29 Jan 2008 18:14:12 -0800
Subject: [Rd] Problem with new("externalptr")
In-Reply-To: <Pine.LNX.4.64.0801291906570.17475@localhost.localdomain>
References: <479FB970.2060303@fhcrc.org> <479FCCD6.3090700@fhcrc.org>
	<Pine.LNX.4.64.0801291906570.17475@localhost.localdomain>
Message-ID: <479FDD74.90905@fhcrc.org>

Luke Tierney wrote:
> On Tue, 29 Jan 2008, Herve Pages wrote:
> 
>> Hi again,
>>
>> Here is an example of an annoyance that I think is directly related to
>> the
>> problem with new("externalptr"). When you try to extend the
>> "externalptr" class:
> 
> You don't wnat to do that for the same reason you don't want to do it
> with environments: Like environment external pointers are reference
> objects, so things like unclass and other attribute changes end up
> being destructive.  Create the thing you want as a list wrapper around
> the external pointer.  You'll be a lot happier with the result in the
> long run.

Thanks Luke!

As you've probably seen further below in my post, wrapping instead of extending
is indeed what I'm doing. And I'm explaining why I don't really have the choice
right now.
I understand why, generally speaking, you would recommend wrapping instead of
extending, but, given the context where I'm making use of those external pointers
(the "ExternalInteger" class itself is not exported, only used internally as slots
of higher level objects, never accessed directly, etc..., etc...), I don't think
extending would actually be a problem.

Unfortunately, because of the problem with new("externalptr"), I can't even
experiment the "extending" approach, test it, compare it with the "wrapping"
approach, etc... and make my own opinion.
And even if I stick with the "wrapping" approach, it would help to have
new("externalptr") doing what I think is the right thing to do.

Cheers,
H.


> 
> luke
> 
> 
>>
>>  > setClass("ExternalInteger", contains="externalptr")
>>  [1] "ExternalInteger"
>>
>> then every call to new("ExternalInteger") will return the same
>> instance too.
>>
>> I've tried to define an "initialize" method for "ExternalInteger"
>> objects, but,
>> whatever I do, I end up with the same "ExternalInteger" instance. So
>> in the end
>> I had to define the "ExternalInteger" class this way:
>>
>>  > setClass("ExternalInteger", representation(xp="externalptr"))
>>
>> even if I'd really like to be able to use the "is a" semantic and not
>> the "has a"
>> semantic.
>>
>> Then I use my xp_new() C routine (see previous post) for initializing
>> the xp slot:
>>
>>  setMethod("initialize", "ExternalInteger",
>>    function(.Object, ...)
>>    {
>>        .Object at xp <- .Call("xp_new")
>>        ...
>>        .Object
>>    }
>>  )
>>
>> Then everytime I need to pass an "ExternalInteger" instance x to a C
>> routine,
>> I need to perform one extra step to reach the externalptr (need to
>> pass x at xp to
>> the routine instead of x itself).
>>
>> So unfortunately, things are quite ugly and more painful than necessary.
>>
>> Thanks,
>> H.
>>
>>
>> Herve Pages wrote:
>>> Hi,
>>>
>>> It seems that new("externalptr") is always returning the same
>>> instance, and
>>> not a new one as one would expect from a call to new(). Of course
>>> this is hard
>>> to observe:
>>>
>>>  > new("externalptr")
>>>   <pointer: (nil)>
>>>  > new("externalptr")
>>>   <pointer: (nil)>
>>>
>>> since not a lot of details are displayed.
>>>
>>> For example, it's easy to see that 2 consecutive calls to
>>> new("environment")
>>> create different instances:
>>>
>>>  > new("environment")
>>>   <environment: 0xc89d10>
>>>  > new("environment")
>>>   <environment: 0xc51248>
>>>
>>> But for new("externalptr"), I had to use the following C routine:
>>>
>>>   SEXP sexp_address(SEXP s)
>>>   {
>>>         SEXP ans;
>>>         char buf[40];
>>>
>>>         snprintf(buf, sizeof(buf), "%p", s);
>>>         PROTECT(ans = NEW_CHARACTER(1));
>>>         SET_STRING_ELT(ans, 0, mkChar(buf));
>>>         UNPROTECT(1);
>>>         return ans;
>>>   }
>>>
>>> Then I get:
>>>
>>>  > .Call("sexp_address", new("externalptr"))
>>>   [1] "0xde2ce0"
>>>  > .Call("sexp_address", new("externalptr"))
>>>   [1] "0xde2ce0"
>>>
>>> Isn't that wrong?
>>>
>>> I worked around this problem by writing the following C routine:
>>>
>>>   SEXP xp_new()
>>>   {
>>>         return R_MakeExternalPtr(NULL, R_NilValue, R_NilValue);
>>>   }
>>>
>>> so I can create new "externalptr" instances from R with:
>>>
>>>   .Call("xp_new")
>>>
>>> I understand that there is not much you can do from R with an
>>> "externalptr"
>>> instance and that you will have to manipulate them at the C level
>>> anyway.
>>> But since new("externalptr") exists and seems to work, wouldn't that be
>>> better if it was really creating a new instance at each call?
>>>
>>> Thanks!
>>> H.
>>>
>>> ______________________________________________
>>> R-devel at r-project.org mailing list
>>> https://stat.ethz.ch/mailman/listinfo/r-devel
>>>
>>
>> ______________________________________________
>> R-devel at r-project.org mailing list
>> https://stat.ethz.ch/mailman/listinfo/r-devel
>>
>



[Rd] Problem with new("externalptr")
Herve Pages