John Peacock <[EMAIL PROTECTED]> writes:
upg_version() creates a temporary string copy (Safefree()'d) of the incoming SV and then calls scan_version(), which in turn does this (where rv is the incoming SV which will ultimately be returned to the top level caller):
SV* sv = newSVrv(rv, "version"); /* create an SV and upgrade the RV */ (void)sv_upgrade(sv, SVt_PVAV); /* needs to be an AV type */
That makes no sense to me at all.
Probably because I didn't explain the calling structure well. Here the prototype for scan_version():
char * Perl_scan_version(pTHX_ char *s, SV *rv, bool qv);
since it is based on the other scan_*() style tokenizer routines. It takes a pointer into the current source, eats the portion that it is able to scan, and then returns the subsequent character for further parsing/tokenizing. So the SV that it returns has to be altered in-place, instead of returning a new rv directly.
You create a new RV with a reference to the incoming (mortal) SV. Making that refrence incrementents the REFCOUNT of the incoming mortal. You then clobber that reference SV and turn it into an AV.
newSVrv(rv, "version") upgrades rv into a reference in the stash "version" and returns a new SV which is occupying the RV slot of rv. Is that any clearer? It seems to be exactly what I want. Then I need to take the returned SV and upgrade it to an AV.
What you would normaly do is AV *av = newAV(); ... rv = newRV_noinc((SV *) av); return rv;
But you have made things difficult for yourself by passing in an SV The easy way to do it is:
AV *av = newAV(); SV *temp = newRV_noinc((SV *) av); sv_setsv(rv, temp); SvREFCNT_dec(temp);
But your version of my "temp" is the nsv in caller.
This looks promising, however.
You can avoid the create/destroy by doing lower level stuff:
sv_upgrade(rv, SVt_RV);
SvRV(rv) = (SV *) av;
And I could do this as well (and it is a little clearer as to what is happening). It should be equivalent to what I already did, but it may be better for reasons I am not currently seeing. I will also have to bless the rv into the version class, since newSVrv() does that for me. Like this:
AV *av = newAV(); sv_upgrade(rv, SVt_RV); /* upgrade the incoming SV to an RV */ SvRV(rv) = (SV *)av; /* point the reference to the AV */ (void *)sv_bless(rv,gv_stashpv("version",TRUE)); /* bless the RV in class*/
The top level code (in UNIVERSAL_VERSION) has done this:
SV *nsv = sv_newmortal(); sv_setsv(nsv, sv); sv = nsv; if ( !sv_derived_from(sv, "version")) upg_version(sv);
If I was doing this I would have done something like:
if ( !sv_derived_from(sv, "version")) {
SV *nsv = upg_version(sv);
sv_setsv(sv,nsv);
SvREFCNT_dec(nsv); }
Another thing I forgot to mention. ;~) In the UNIVERSAL::VERSION code, I have to return the existing $VERSION scalar (after upgrading to a version object). I have to make a copy of the requested version scalar (which is typically a constant) and upgrade that to a version object (so I can perform my comparison with like objects). I just cribbed the existing code from UNIVERSAL::VERSION to make mortal copies of the $VERSION and requested version scalars.
Thanks for the ideas
John