tod222 has asked for the wisdom of the Perl Monks concerning the following question:
I have a C++ class wrapped with SWIG. It works nicely and doesn't leak memory when I create and destroy lots of objects normally.
But the class has a method that returns a std::vector containing several new instances of the class which are variants of itself.
It almost works -- the SWIG wrapper returns an array reference to the Perl caller, but the objects in the array leak memory when they are destroyed.
This is correct behavior on SWIGs part, as the objects were created by C++, not SWIG or Perl. I want to plug the leak by calling ACQUIRE for each object, which will cause Perl to explicity call the C++ destructor via the DESTROY function created by SWIG.
Unlike normally-created objects which are HASHes, the objects delivered in the array are SCALARs:
my $f = Foo->new(); # normal creation print ref $f; # prints "Foo" print $f; # prints "Foo=HASH(0x19a0ef8)" my $aref = $f->getVariants(); # reference to array containing many Foo +s print ref $aref->[0]; # prints "Foo" print $aref->[0]; # prints "Foo=SCALAR(0x19c8290)"
Not surprisingly, calling ACQUIRE with one of the SCALARS gives the error "Not a HASH reference at..." when it tries to call tied, since the ACQUIRE method generated by SWIG assumes a HASH. Here's the SWIG-generated Perl for class Foo:
package Bar::Foo; use vars qw(@ISA %OWNER %ITERATORS %BLESSEDMEMBERS); @ISA = qw( Bar ); %OWNER = (); %ITERATORS = (); sub new { my $pkg = shift; my $self = Barc::new_Foo(@_); bless $self, $pkg if defined($self); } sub DESTROY { return unless $_[0]->isa('HASH'); my $self = tied(%{$_[0]}); return unless defined $self; delete $ITERATORS{$self}; if (exists $OWNER{$self}) { Barc::delete_Foo($self); delete $OWNER{$self}; } } *AllVariants = *Barc::Foo_AllVariants; sub DISOWN { my $self = shift; my $ptr = tied(%$self); delete $OWNER{$ptr}; } sub ACQUIRE { my $self = shift; my $ptr = tied(%$self); $OWNER{$ptr} = 1; }
For reference, here are the lines from the SWIG-generated C++ wrapper that appear to create the Foo objects.
The first line appears in the wrapper for the new method. The second line is from the wrapper for the method returning the array of objects:
SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_Foo, SWIG_OWNER + | SWIG_SHADOW) SWIG_NewPointerObj(x, SWIGTYPE_p_Foo, 0)
I could make a Perl-specific helper class in C++ with a getNext method which could then be used by a Perl method in the wrapper, like so:
%perlcode %{ sub getAllVariants { my ($self) = @_; my $fv = FooVariants->new($self); my $aref = []; while ( my $foo = $fv->getNext() ) { $foo->ACQUIRE(); # C++ code does not delete this, so ensure del +ete_ method is called push( @$aref, $foo ); } return $aref; } %}
I know this technique works, but I'd prefer an all-Perl method. Is there a way to change a SCALAR into a HASH using just Perl without any XS or C++ code?
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: Change a SCALAR into a HASH using just Perl without any XS code?
by ikegami (Patriarch) on Oct 31, 2010 at 15:54 UTC | |
by tod222 (Pilgrim) on Nov 01, 2010 at 05:32 UTC | |
by ikegami (Patriarch) on Nov 01, 2010 at 15:57 UTC |