Kenneth Duda | 8 May 18:32

Making smart pointers a bit smarter

Hi folks,

SWIG  (at least swig -python) has nice support for wrapping C++ smart
pointers.  One thing that I feel could be improved is the handling of
NULL smart pointers.  When converting a NULL C++ raw pointer to
python, SWIG (correctly, in my view) converts it to None.  However,
SWIIG doesn't do anything similar for smart pointers --- it basically
wraps the NULL smart pointer and returns it.  A NULL smart pointer
wrapped this way is a booby trap --- any attempt to use it for
anything causes a SEGV.

More specifically, the default typemap results in the following code
to return a smart pointer to python:

  {
      FooSmartptr * resultptr;
      resultptr = new FooSmartptr((FooSmartptr &) result);
      resultobj = SWIG_NewPointerObj((void *) resultptr,
SWIGTYPE_p_FooSmartptr, 1);
  }

With the following typemap, the behavior can be improved (in my
opinion) so that None is returned if the smart pointer is NULL:

%typemap(out) FooSmartptr {
 if( $1.operator->() ) {
    FooSmartptr * resultptr;
    resultptr = new FooSmartptr( $1 );
    $result = SWIG_NewPointerObj( resultptr, SWIGTYPE_p_FooSmartptr, 1 );
 } else {
(Continue reading)

Marcelo Matus | 9 May 19:29

Re: Making smart pointers a bit smarter

Anything that get prevents a seg fault is welcome :),

Maybe you can try to implement this as a "feature",
that could be enabled by default for smart pointers
but that cam also be disabled for testing and backward
compatibility.

Marcelo

Kenneth Duda wrote:

>Hi folks,
>
>SWIG  (at least swig -python) has nice support for wrapping C++ smart
>pointers.  One thing that I feel could be improved is the handling of
>NULL smart pointers.  When converting a NULL C++ raw pointer to
>python, SWIG (correctly, in my view) converts it to None.  However,
>SWIIG doesn't do anything similar for smart pointers --- it basically
>wraps the NULL smart pointer and returns it.  A NULL smart pointer
>wrapped this way is a booby trap --- any attempt to use it for
>anything causes a SEGV.
>
>More specifically, the default typemap results in the following code
>to return a smart pointer to python:
>
>  {
>      FooSmartptr * resultptr;
>      resultptr = new FooSmartptr((FooSmartptr &) result);
>      resultobj = SWIG_NewPointerObj((void *) resultptr,
>SWIGTYPE_p_FooSmartptr, 1);
(Continue reading)

Josh Cherry | 9 May 20:42

Re: Making smart pointers a bit smarter


On Sun, 8 May 2005, Kenneth Duda wrote:

> "None" is much friendlier than a wrapped NULL smart pointer --- it's
> easy to test for "None", and if you try to use it as a pointer, you
> get a sensible Python exception (rather than a SEGV).
>
> All of this is fine --- the reason I'm writing is I would like to make
> this improved smart pointer wrapping the default behavior of SWIG.
> Before I spend the time to do so, I wanted to guage the interest level
> from the SWIG maintainers --- is the patch likely to be accepted
> (assuming I code it competently), or is it something you're not
> interested in (i.e., is there a good reason to not do this)?

I understand the motivation for doing this, but I also see some
downsides, and some ways to protect against segfaults while avoiding the
downsides.

Sometimes in C++ one creates a smart pointer that points at NULL and
subsequently modifies it, using methods of the smart pointer class.  This
way of doing things would be impossible with None.  Also, there is
potential for loss of information and for throwing away a useful object.
This isn't an issue for a smart pointer that contains a pointer as its
sole data member, which is returned by operator->, but not all smart
pointers are like that.  Perhaps operator-> returns NULL but the object is
full of information and has other methods that are still very useful
(perhaps I can decrement my pointer and it will now point at something;
perhaps operator-> is a minor part of what the class does, and returns
NULL more often then not).

(Continue reading)

Kenneth Duda | 9 May 23:19

Re: Making smart pointers a bit smarter

Thanks for the comments.  I agree with all of the limitations you
point out (though they happen to not matter for the smart pointers I'm
using).

> Why not check for nullness on the other end, in the wrapper functions that
> dereference the smart pointers?  I think that that avoids all these
> problems.  

This sounds like it would be better.  How would you imagine checking
for nullness, if testing operator->() for NULL can't be trusted?  I
suppose I could wrap the null check in a try block and treat any
exception as equivalent to NULL.  Would you recommend that?

> Better yet, why not move much of smart pointer support out of C++ and into
> the target language?

Let me see if I understand this suggestion. It means eliminating most
methods of the wrapped C++ smart pointer, leaving only __deref__ and
__notzero__ (that could do the NULL test safely); then, modify the
python wrapper of the smart pointer so instead of saying

   def someMethod(*args): return _MyLibrary.MyClassSmartptr_someMethod( *args )

it would say

   def someMethod(self, *args):
      if self: return self.__deref__().someMethod( *args )
      else: raise NullPointerException()

If this is right, I'd tend to agree it's better than the current smart
(Continue reading)

Josh Cherry | 10 May 16:07

Re: Making smart pointers a bit smarter


On Mon, 9 May 2005, Kenneth Duda wrote:

> This sounds like it would be better.  How would you imagine checking
> for nullness, if testing operator->() for NULL can't be trusted?  I
> suppose I could wrap the null check in a try block and treat any
> exception as equivalent to NULL.  Would you recommend that?

I was imagining using operator->.  I was thinking that it's not a problem
if testing is done here, which is sort of true.  Right now I get basically
the behavior we're talking about because my operator-> throws an
exception, which gets caught by the exception handling I've set up for
everything using %exception and turned into a target language exception.
If this test were to wind up inside of the "try" block set up by
%exception, things would continue to work the same for me even without
special exception handling here.

> Let me see if I understand this suggestion. It means eliminating most
> methods of the wrapped C++ smart pointer, leaving only __deref__ and
> __notzero__ (that could do the NULL test safely); then, modify the
> python wrapper of the smart pointer so instead of saying
>
>    def someMethod(*args): return _MyLibrary.MyClassSmartptr_someMethod( *args )
>
> it would say
>
>    def someMethod(self, *args):
>       if self: return self.__deref__().someMethod( *args )
>       else: raise NullPointerException()

(Continue reading)

Hermann Rodrigues | 12 May 16:16

Re: Making smart pointers a bit smarter

I solve an related problem, wrapping C++ Boost shared_ptr code to
Java, using the following typemaps:

namespace boost {

template < typename T >
class shared_ptr {
protected:
  shared_ptr();

public:
  %typemap(out) shared_ptr<T> {
    if ( (&$1)->get() == 0 )
      return $null;

    *( boost::shared_ptr<T> ** )&jresult = new boost::shared_ptr<T>( (
boost::shared_ptr<T> & )result );
  }

  %typemap(javaout) shared_ptr<T> {
    long ptr = $jnicall;
    if ( ptr == 0 )
      return null;
    return new $javaclassname( ptr, $owner );
  }

  T *operator->() const;
};

}
(Continue reading)


Gmane