Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

Change a SCALAR into a HASH using just Perl without any XS code?

by tod222 (Pilgrim)
on Oct 31, 2010 at 15:35 UTC ( [id://868596]=perlquestion: print w/replies, xml ) Need Help??

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

    I don't see where you specify what value you want the hash to have.

    Alternatively, see Object::Destroyer

      You're right, I didn't specify a value for the hash, because it doesn't contain anything -- it's XS code acting like a hash, or in the problematic case, XS code acting like a scalar.

      I tried the following which appeared to work in a trivial test case, but it corrupted memory causing a Segfault in another location when I tested it in real code:

      my $aref = $f->getVariants(); # reference to array containing many Foo my $sfoo = shift(@$aref); my $tiefoo = {}; # warning - this causes segfaults bless $tiefoo, 'Bar::Foo'; tie %{$tiefoo}, 'Bar::Foo', $sfoo; $tiefoo->ACQUIRE();

      So my conclusion is that it's not possible to fix this in pure Perl. The C++ or SWIG wrapper code must be modified.

        because it doesn't contain anything

        Creating an empty hash is very easy...

        There's obviously some information the module requires from this hash. You can't create a hash that meets the module's needs until you figure out what this information is. This is what you need to do first.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://868596]
Approved by davies
Front-paged by Arunbear
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others scrutinizing the Monastery: (3)
As of 2024-04-16 19:09 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found