http://qs321.pair.com?node_id=635113

syphilis has asked for the wisdom of the Perl Monks concerning the following question:

Hi,
In perldoc Inline::C-Cookbook you'll find an "Object Oriented Inline" example. The following script (which works fine) is based on that example. I think the only changes I've made is to replace the strdup() calls (which are buggy) with savepv() calls - and I've added a Devel::Peek::Dump() call to reveal the innards of the objects being returned. Oh, yes ... and I've replaced malloc/free with New/Safefree:
use Devel::Peek; my $obj1 = Soldier->new('Benjamin', 'Private', 11111); my $obj2 = Soldier->new('Sanders', 'Colonel', 22222); my $obj3 = Soldier->new('Matt', 'Sergeant', 33333); for my $obj ($obj1, $obj2, $obj3) { print ($obj->get_serial, ") ", $obj->get_name, " is a ", $obj->get_rank, "\n"); } Dump($obj1); print "\n"; package Soldier; use Inline C => <<'END'; typedef struct { char* name; char* rank; long serial; } Soldier; SV* new(char* class, char* name, char* rank, long serial) { Soldier* soldier; New(42, soldier, 1, Soldier); SV* obj_ref = newSViv(0); SV* obj = newSVrv(obj_ref, class); soldier->name = savepv(name); soldier->rank = savepv(rank); soldier->serial = serial; sv_setiv(obj, (IV)soldier); SvREADONLY_on(obj); return obj_ref; } char* get_name(SV* obj) { return ((Soldier*)SvIV(SvRV(obj)))->name; } char* get_rank(SV* obj) { return ((Soldier*)SvIV(SvRV(obj)))->rank; } long get_serial(SV* obj) { return ((Soldier*)SvIV(SvRV(obj)))->serial; } void DESTROY(SV* obj) { Soldier* soldier = (Soldier*)SvIV(SvRV(obj)); Safefree(soldier->name); Safefree(soldier->rank); Safefree(soldier); } END __END__ Outputs: 11111) Benjamin is a Private 22222) Sanders is a Colonel 33333) Matt is a Sergeant SV = PVIV(0x2f5b8c) at 0x2f4940 REFCNT = 1 FLAGS = (PADBUSY,PADMY,ROK) IV = 0 RV = 0x2f3ca4 SV = PVMG(0x2febbc) at 0x2f3ca4 REFCNT = 1 FLAGS = (OBJECT,IOK,READONLY,pIOK) IV = 3143860 NV = 0 PV = 0 STASH = 0x45fca8 "Soldier" PV = 0x2f3ca4 "" CUR = 0 LEN = 0
Some time ago, Ken Williams suggested a more succinct rewrite of the new() function. (See http://www.mail-archive.com/inline@perl.org/msg01370.html for the post to which I refer.) In light of Ken's post I changed the new() function to this:
SV* new(char* class, char* name, char* rank, long serial) { Soldier* soldier; New(42, soldier, 1, Soldier); soldier->name = savepv(name); soldier->rank = savepv(rank); soldier->serial = serial; return sv_setref_pv(newSViv(0), class, (void *)soldier); }
That change, however, has a small effect on the output of the above script. Instead of getting FLAGS = (OBJECT,IOK,READONLY,pIOK) as per the above Devel::Peek::Dump(), I now get FLAGS = (OBJECT,IOK,pIOK). The READONLY flag that the original version of the script explicitly turned on is missing.

Two questions:
1) Does/could it matter that the READONLY flag is missing ?
2) How do I re-instate that READONLY flag in the modified (more succinct) version of the new() function ?

I'm inclined to think that the answer to 2) is probably straightforward and that my attempts to find the answer have simply been plagued by my own idiocy. I tried the following rendition of new() but it, too, failed to turn on any readonly flags (according to Devel::Peek::Dump, anyway):
SV* new(char* class, char* name, char* rank, long serial) { Soldier* soldier; SV *ret, *temp = newSViv(0); New(42, soldier, 1, Soldier); soldier->name = savepv(name); soldier->rank = savepv(rank); soldier->serial = serial; ret = sv_setref_pv(temp, class, (void *)soldier); SvREADONLY_on(temp); SvREADONLY_on(ret); return ret; }
FWIW, I've long been using the technique that appears in the Inline::C Cookbook ... and I'm starting to wonder whether I ought to switch to using Ken's suggested succinctification.

Update: Part of the attraction of Ken's rewrite is that it seems to lend itself more readily to typemapping (as regards the OUTPUT section of the typemap). But I'm unsure about that, too :-)

Cheers,
Rob