On Fri, Jan 17, 2003 at 12:40:49PM +0000 Nick Ing-Simmons wrote:

> Tassilo Parseval <[EMAIL PROTECTED]> writes:
> >Hi all,
> >
> >In my XS I have two methods that basically mimic the behaviour of Perl's
> >localtime() and gmtime(). To avoid some duplicate code in the XSUBs I
> >thought I let them call a C function with an additional argument
> >'method' (to determine whether to use gmtime(3) or localtime(3)) and
> >then let this C function push the returns values directly onto the Perl
> >stack. That wasn't quite that easy since it turned out that this
> >involves quite some knowledge about all the stack macros. I've finally
> >come up with a working solution but I am sceptical, especially since I
> >don't quite understand why it works. :-)
> 
> Mostly by luck ;-) - the result returning is fine, but you are also 
> messing with mark stack in ways which don't look safe.

I sensed it. My anxiety wasn't totally lost then.

[...]

> >        if (method == 0)
> >            tstruct = localtime(&time);
> >        else    
> >            tstruct = gmtime(&time);
> >        
> >        PUSHMARK(SP);
> 
> PUSHMARK is for marking start of args for a perl/XS sub you are 
> going to call - you should not need it for returning values.
> If you found you needed it it is probably because you used dXSARGS 
> which does an extra "pop" of the mark stack. Pushing a mark back 
> hides that muddle but the mark you are pushing may be in wrong place.

So it was there that I started to take the wrong track. In a moment of
despair I read perlcall.pod which - as I know now - is the wrong place
since I don't want to call a Perl subroutine from C.

[...]

> I think the minimum is as follows - I have added the THX stuff needed for 
> threads/multiplicity (and which is suitably NOPed for you elsewhere):
> 
>     int func(pTHX_ ...) {
>         dSP;
>         int num_return_values;
>         /* push stuff on stack using XPUSH or EXTEND/PUSH */
>         PUTBACK;
>         return num_return_values;
>     }
> 
>     void XSfunc(...)
>         ...
>         PPCODE:
>             XSRETURN(func(aTHX_ ...));
> 
> For some peoples view of simpler you could pass SP to the function 
> as an SV ** and return modified value and compute number of items 
> from the difference. But I think my version above is a reasonable 
> trade off which still uses XS-oid macros.

Yes, I prefer it that way over passing the SP to the function. Your
suggestion almost works. I had to change it to

    dSP;
    (void)POPs;
    /* Push the stuff */
    PUTBACK;
    return num_vals;
    
If I don't make this change, I get: (from Perl: print Dumper [
$mail->rcvd_gmtime ])

    $VAR1 = [
        bless( do{\(my $o = 135287392)}, 'Mail::Transport::Dbx::Email'),
        0,
        9,
        22,
        1,
        7,
        102,
        4,
        212
    ];
    
and the last value is missing. So it looks that in

    void
    rcvd_gmtime (self)
            DBX_EMAIL *self
        CODE:
            XSRETURN(datify(aTHX_ &(self->email->email), 0));

'self' is still on the stack when datify is called and that is why I
currently POPs it off there.
    
> The main thing you have "wrong" is the messing with the mark stack.
> That is for input args, and the implied dXSARGS; that xsubpp generates 
> for you should handle all that.
> 
> The way mark stack works is that you do a PUSHMARK(sp) before you start 
> pushing args for a called routine - it records where on stack 1st arg is.
> It does this by pushing the address onto ANOTHER stack: the mark stack.
> Then called routine does a POPMARK which pops a value off the mark stack
> the difference between the value from there and the top of the main
> arg stack is the number of args.

I understand this part...or at least well enough to know that I don't
need to worry about the MARK stack in this context.

> Return values over-write the args - so 1st return value goes over 1st arg 
> etc. So in a CODE: style xsub using XS-ish macros args go as ST(0)..ST(N).
> (So as you know you are going to return 1 or 9 values you don't _need_ 
> to use PPCODE - but it is harmless.)

Without POPs it looks that ST(0) stays as it is and the first of my nine
return values is put into ST(1)...the eigth into ST(8) and the last one
missing. I think I will have a closer look at the .c file that xsubpp
creates from an ordinary XS. One thing is particularly odd: I can POPs
within the XSUB but after doing 'dSP;' in the C function called from
this XSUB SP(0) is again there.

> PPCODE: is different it does a stack adust by number of args before the 
> body gets invoked - so top of stack is now pointing at 1st arg so you 
> can just XPUSH() return values.
> 
> The other main thing you need to keep in mind is that EXTEND (or the 
> implied one in XPUSHs) can re-allocate stack and so it may move wholesale 
> in memory. I think the XS macro ST(n) dodges this issue by working in offsets
> from _the_ global variable which holds bottom-of-stack. But dSP / PUTBACK 
> and PUSH are working with a "cached" local copy of top-of-stack pointer.

Cached sounds as though it is the explanation for what I observed
further above. POPs() does a *sp--, while dSP sets sp back to
PL_stack_sp. Hmmh, these stacks are scary!

> I know the .h files are not as clear as we would like, but it is worth 
> taking a look at them when you are unclear. I believe all this is 
> covered in Simon's book ...

It'd probably be good to have a terse description of these mechanisms. I
find the Perl headers each day more bearable, but for getting a broader
view they are not very suitable. The macros are often too deeply nested
and after the third or fourth nesting I lost the track (or follow the
wrong one). :-( A book is more likely to cover that nicely.

Nick, thanks a lot for your time and the explanations! A lot of that
made sense...some things not yet completely. But I am confident since
today this was basically my first really intimate contact with the
argument stack. 

Tassilo
-- 
$_=q!",}])(tsuJ[{@"tnirp}3..0}_$;//::niam/s~=)]3[))_$-3(rellac(=_$({
pam{rekcahbus;})(rekcah{lrePbus;})(lreP{rehtonabus;})(rehtona{tsuJbus!;
$_=reverse;s/sub/(reverse"bus").chr(32)/xge;tr~\n~~d;eval;

Reply via email to