Hi! On Mon, Nov 22, 2004 at 11:48:37AM +0000 Nick Ing-Simmons wrote:
> Tassilo von Parseval <[EMAIL PROTECTED]> writes: > >Hi, > > > >here's one that I have no idea how to go about: > > > >Take this Perl code: > > > > my $i = iterator([1 .. 3]); > > while (defined (my $val = $i->()) { > > print $val, "\n"; > > } > > __END__ > > 1 > > 2 > > 3 > > > >I am now trying to implement iterator() as XSUB so its job is to return > >a closure (closing over iterator's argument). > > Perl closures are quite complicated things. I noticed. :) > You will probaly get better informed audience for your question on p5p. That seems to be the policy, but I try to avoid that whenever possible. As I need these information for a module, it's not really core-related. Only from a technical point of view. > >Ideally, the function > >reference it returns should also be implemented as XSUB. > > But if you are doing it all in XSUB/C you don't really need > a perl closure do you? No, not at all. But I need the characteristics of a closure. The XSUB has to keep its state somewhere. So behavior-wise it's a closure. > You can just create a new CV (associated with your XS code like newXS > would do) and then attach the "saved" arg to (say) CvANY. > But that may not provide hooks to free the copy (can't remember) > when CV gets freed. Neither do I. For the moment, I just mortalized the CV and hope it's ok. It turns out that I was able to find a solution with your remarks. At first I didn't know at all what you meant. Then I found CvXSUBANY (which you presumably meant). Then, after looking through pp_entersub I noticed that the C function implementing the XSUB is called with one argument (vie function pointer). I looked at xsubpp's output and what the XS(name) macro is about. It's really quite simple. XS(name) expands to: void name (pTHX_ CV *cv); so an XSUB has access to a hidden variable 'cv'. And that was the very thing I needed. Now my solution looks like this: typedef struct { AV **arrays; int narrays; int curidx; } closure_args; # ... void _array_iterator () PROTOTYPE: CODE: { register int i; int exhausted = 1; int len; closure_args *args = (closure_args*)CvXSUBANY(cv).any_ptr; for (i = 0; i < args->narrays; i++) { AV *av = args->arrays[i]; if (args->curidx <= av_len(av)) { ST(i) = *av_fetch(args->arrays[i], args->curidx, FALSE); exhausted = 0; continue; } ST(i) = &PL_sv_undef; } if (exhausted) { Safefree(args->arrays); Safefree(args); XSRETURN_EMPTY; } args->curidx++; XSRETURN(len); } SV * each_array (...) PROTOTYPE: \@;[EMAIL PROTECTED]@[EMAIL PROTECTED]@[EMAIL PROTECTED]@[EMAIL PROTECTED]@[EMAIL PROTECTED]@[EMAIL PROTECTED]@[EMAIL PROTECTED]@[EMAIL PROTECTED]@[EMAIL PROTECTED]@[EMAIL PROTECTED]@[EMAIL PROTECTED]@[EMAIL PROTECTED]@[EMAIL PROTECTED]@[EMAIL PROTECTED]@[EMAIL PROTECTED]@ CODE: { register int i; closure_args *args; CV *closure = newXS("List::MoreUtils::_array_iterator", XS_List__MoreUtils__array_iterator, __FILE__); sv_setpv((SV*)closure, ""); New(0, args, 1, closure_args); New(0, args->arrays, items, AV*); args->narrays = items; args->curidx = 0; for (i = 0; i < items; i++) args->arrays[i] = (AV*)SvRV(ST(i)); CvXSUBANY(closure).any_ptr = args; RETVAL = newRV_inc((SV*)closure); } OUTPUT: RETVAL I didn't yet made a thorough check for memory leaks. valgrind does report a few lost bytes but it claims that they are all in /usr/bin/perl. Another thing of concern is that it doesn't yet work on perl5.005_04 (the only other version I checked). It compiles fine and runs without segfaults and such, it just produces no output at all. I don't see any perl5.8.5isms in the above. > >My idea was to get the CV* of the iterator to be returned, maybe thusly: > > > > CV *closure = get_cv("_iterator", FALSE); > > ... > > XSRETURN(sv_2mortal(newSVrv_noinc((SV*)closure))); > > > >A CV has a padlist so I thought I could simply abuse closure's padlist > >to store the array-ref passed to iterator(). And then, each time this > >iterator is triggered, the XSUB implementing it can access its own > >padlist and shift a value from the array-ref stored inside. > > I _think_ a perl closure isn't the original CV but a copy with > most things the same but a private pad. > > > > >So far I don't know whether the above is even theoretically feasible. I > >notice that CvPADLIST(closure) is null which is probably a bad sign. And > >then I have no idea how an XSUB could access its own padlist. It's not > >in PL_curpad as it looks. > > AFAIK XSUBs don't normally have pads. So you would probably have to > allocate one. > PL_curpad is a compile time thing, the CV's pad hangs off the CV somehow. I think I don't need any of that any longer. CvXSUBANY is ideal for attaching arbitrary data to an XSUB. Cheers, Tassilo -- $_=q#",}])!JAPH!qq(tsuJ[{@"tnirp}3..0}_$;//::niam/s~=)]3[))_$-3(rellac(=_$({ pam{rekcahbus})(rekcah{lrePbus})(lreP{rehtonabus})!JAPH!qq(rehtona{tsuJbus#; $_=reverse,s+(?<=sub).+q#q!'"qq.\t$&."'!#+sexisexiixesixeseg;y~\n~~dddd;eval