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

Replies are listed 'Best First'.
Re: Inline::C-Cookbook's "Object Oriented Inline"
by syphilis (Archbishop) on Aug 27, 2007 at 10:40 UTC
    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)

    That's only marginally true. Using Ken's rewrite, the typemap OUTPUT consists of one line of code. Sticking with the original version of Soldier::new(), five lines of OUTPUT code are required.

    For anyone who wants to <readmore>, below my sig is a version of the script that I originally posted, modified to use either of the typemaps (which are also presented below).

    I still haven't found an answer to either question 1) or 2) in my op.

    Cheers,
    Rob

Re: Inline::C-Cookbook's "Object Oriented Inline"
by sfink (Deacon) on Sep 05, 2007 at 20:20 UTC
    I'm pretty fuzzy about these things too, so I'll write up a response to make myself figure it out.

    First, the READONLY flag is set on the SV containing the pointer value (as an IV). This is for safety. Without this, you could do $$obj1++ and get a segmentation fault the next time you called any Inline method on the object. (It should really be a bus error, but I get a seg fault. Hmf.) Modifying pointer values from Perl is rarely useful and usually disastrous, so it's nice to lock them down to prevent accidental corruption.

    Second, you can have the equivalent effect as the Cookbook example with:

    obj_ref = sv_setref_pv(newSViv(0), class, (void *)soldier); SvREADONLY_on(SvRV(obj_ref)); return obj_ref;

    Now, as for exactly what's going on, I'm much shakier. I have long used the Cookbook code exactly as-is because it's never made sense to me. I now suspect that it has nothing to do with me; it really doesn't make sense.

    In particular, notice how obj_ref is initialized with newSViv. I think the "iv" part of that is irrelevant; the IV part of obj_ref has nothing to do with the IV that ends up holding the Soldier pointer. In fact, you can replace newSViv with newSVnv -- or even newSV, which would make the most sense. You will get a different result when you dump the variable -- instead of obj_ref dumping to a PVIV that happens to have its RV field (and ROK flag) set, you will get a plain RV. But that seems, to my eye, in all ways better. I would go so far as to say that the Cookbook code is buggy -- it is wasteful of memory and highly confusing to boot.

    So if you use newRV, it all seems to make sense: obj_ref is an RV that points to a blessed SV holding an IV, and that IV is the pointer value. The blessed SV should naturally be READONLY because you don't want anyone mucking with the pointer value.

    For the time being, I will ignore my own confusion as to why the IV-containing SV is blessed into a package, rather than the RV.

    With this understanding, I think the following code should have the equivalent effect:

    SV* obj = newSViv((IV)soldier); SV* obj_ref = newRV_noinc(obj); sv_bless(obj_ref, gv_stashpv(class, TRUE)); SvREADONLY_on(obj); return obj_ref;
    which is far easier to understand, though much less slick than Ken's. Especially when you get rid of the bogus newSViv, and just use:
    obj_ref = sv_setref_pv(newSV(0), class, (void *)soldier); SvREADONLY_on(SvRV(obj_ref)); return obj_ref;
    Wow. I think I finally understand what's going on now. Thank you for asking the question!

    Well, everything but why blessing modifies referenced SV's instead of the referencing SV's. Anyone?

    Oh, and one last thing -- I handle the typemap file by relying on the fact that I'm compiling with gcc, and can use its ({ ... }) construct. Here's an excerpt from my typemap:

    GrNode * T_GR_NODE INPUT T_GR_NODE $var = (GrNode*) ({ SV* arg = $arg; SV* tmp = arg && SvROK(arg +) ? SvRV(arg) : 0; tmp && SvIOK(tmp) ? SvIV(tmp) : 0; }); OUTPUT T_GR_NODE $arg = $var ? wrap("Rx::DOM::Node", $var) : &PL_sv_undef;
    I'm not sure why I use an external function in the OUTPUT map but do it all messily inline in the INPUT map. Obviously, you could put all the code you need into the OUTPUT too.

      In Perl, take two references to the same variable. Bless one. Note that the other reference is now blessed. Conclude that the blessed state is stored in the referenced variable not in the reference (not that surprising).

      In Perl, it is useless to bless something unless you have a reference to it; so blessing is done through the reference. In XS, the blessing API doesn't have the extra derefence cycle built into it, understandably.

      - tye        

        In Perl, take two references to the same variable. Bless one. Note that the other reference is now blessed. Conclude that the blessed state is stored in the referenced variable not in the reference (not that surprising).

        I proved it to myself by blessing a reference in Perl then passing it to dump_sv. My question is "why?"

        The semantic difference is exactly what you said -- the blessing is shared by all references to the value. But I still don't understand why those semantics are more useful than the alternative. I'm not claiming that it should be the other way around, just that I personally don't know the reason why it is one way and not the other.

        Thinking through it, I realize that if it were the other way around (blessed references were actually blessed references), then you could have the same data item with multiple references pointing to it, each blessed with a different package. If each of them defines a DESTROY, which DESTROY should get called? Or more to the point, when should DESTROY be called? The way it is now (blessed referents), DESTROY is called when the last reference to the data item goes away, which makes a lot of sense. If references were blessed, then you would have to call DESTROY whenever any of the references themselves went away, which would result in multiple DESTROYs getting called for the same data item (each with a different package). And destroying references isn't really that interesting anyway (they don't really have any data to diddle with); destroying the data item is.

        Ok, maybe I do understand the "why?", now.