#!/usr/bin/perl
use strict;
use warnings;
use Inline C => <<'END_C';
typedef struct Artist {
char *name;
SV *perlobj;
} Artist;
typedef struct CD {
Artist *artist;
/* ... */
} CD;
/* Constructor for Artist. */
SV*
new_artist(char *name) {
SV *perlobj, *perlref;
Artist *artist;
/* allocate and initialize Artist struct */
New(0, artist, 1, Artist);
artist->name = savepv(name);
/* create perl object and ref; store pointer to object in struct */
perlref = newSV(0);
artist->perlobj = sv_setref_pv(perlref, "Artist", (void*)artist);
return perlref;
}
/* Constructor for CD. */
SV*
new_cd(SV *artist_sv) {
CD *cd;
Artist *artist = INT2PTR( Artist*, SvIV(SvRV(artist_sv)) );
SV *cd_ref;
/* allocate and initialize CD struct */
New(0, cd, 1, CD);
cd->artist = artist;
/* WHY DOESN'T THIS SAVE THE ARTIST FROM DESTRUCTION? */
SvREFCNT_inc(artist->perlobj);
cd_ref = newSV(0);
sv_setref_pv(cd_ref, "CD", (void*)cd);
return cd_ref;
}
/* getter */
SV*
get_name_from_artist(SV *artist_sv) {
Artist *artist = INT2PTR( Artist*, SvIV(SvRV(artist_sv)) );
return newSVpv(artist->name, 0);
}
/* getter */
SV*
get_artist_name_from_cd(SV *cd_sv) {
CD *cd = INT2PTR( CD*, SvIV(SvRV(cd_sv)) );
return newSVpv(cd->artist->name, 0);
}
/* Destructor for Artist objects */
void
destroy_artist(SV *artist_sv) {
Artist *artist = INT2PTR( Artist*, SvIV(SvRV(artist_sv)) );
Safefree(artist->name);
Safefree(artist);
}
/* Destructor for CD objects */
void
destroy_cd(SV *cd_sv) {
CD *cd = INT2PTR( CD*, SvIV(SvRV(cd_sv)) );
SvREFCNT_dec(cd->artist->perlobj);
Safefree(cd);
}
END_C
package Artist;
use strict;
use warnings;
use Devel::Peek qw( Dump );
sub DESTROY {
my $artist = shift;
my $artist_name = main::get_name_from_artist($artist);
print STDERR "Artist::DESTROY called.\n"
. "$artist_name commits suicide -- but why?\n"
. "Somebody still loves you! You have a refcount of 2:\n";
Dump($artist);
main::destroy_artist($artist);
}
package CD;
use strict;
use warnings;
sub DESTROY {
my $cd = shift;
print STDERR "CD::DESTROY called.\n";
main::destroy_cd($cd);
}
package main;
use strict;
use warnings;
# Create an Artist object.
my $artist = new_artist("Johnny Houseburner");
# Save a copy of the Artist object in a CD object.
my $johnny_houseburner_cd = new_cd($artist);
# Displace the old Artist object in Perl-space. No destruction should occur,
# because the CD object holds a reference. But that doesn't work -- DESTROY
# gets called.
$artist = new_artist("FunThree");
my $funthree_cd = new_cd($artist);
# access invalid memory.
my $whats_his_name = get_artist_name_from_cd($johnny_houseburner_cd);
print STDERR "What was that guy's name again? $whats_his_name?\n";
####
$ perl cd_artist.plx
Artist::DESTROY called.
Johnny Houseburner commits suicide -- but why?
Somebody still loves you! You have a refcount of 2:
SV = RV(0x182186c) at 0x1856bf8
REFCNT = 1
FLAGS = (PADBUSY,PADMY,ROK)
RV = 0x1801380
SV = PVMG(0x1822080) at 0x1801380
REFCNT = 2
FLAGS = (OBJECT,IOK,pIOK)
IV = 3880080
NV = 0
PV = 0
STASH = 0x180b56c "Artist"
What was that guy's name again? yAGS?
CD::DESTROY called.
CD::DESTROY called.
Bus error
$
####
my $copy = $artist;
$artist = new_artist("FunThree");
####
typedef struct CD {
Artist *artist;
+ SV *artist_ref;
/* ... */
} CD;
@@ -43,7 +44,7 @@
cd->artist = artist;
/* WHY DOESN'T THIS SAVE THE ARTIST FROM DESTRUCTION? */
- SvREFCNT_inc(artist->perlobj);
+ cd->artist_ref = newSVsv(artist_sv);
cd_ref = newSV(0);
sv_setref_pv(cd_ref, "CD", (void*)cd);
@@ -77,7 +78,7 @@
void
destroy_cd(SV *cd_sv) {
CD *cd = INT2PTR( CD*, SvIV(SvRV(cd_sv)) );
- SvREFCNT_dec(cd->artist->perlobj);
+ SvREFCNT_dec(cd->artist_ref);
Safefree(cd);
}
####
/* Yecch. */
typedef struct BloatedObject {
Foo *foo;
SV *foo_ref;
Bar *bar;
SV *bar_ref;
} BloatedObject;
void
destroy_bloated_object(BloatedObject *obj) {
SvREFCNT_dec(obj->foo_ref);
SvREFCNT_dec(obj->bar_ref);
Safefree(obj);
}
/* Better. */
typedef struct LeanObject {
Foo *foo;
Bar *bar;
} LeanObject;
void
destroy_lean_object(LeanObject *obj) {
SvREFCNT_dec(obj->foo->perlobj);
SvREFCNT_dec(obj->bar->perlobj);
Safefree(obj);
}