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

Reply via email to