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.
>
>Here the C function and how it's called from one of the two XSUBs below:
>
> int datify (FILETIME *wintime, int method) {
> dXSARGS;
dSP;
should suffice here dXSARGS; does other stuff to do with geting number
of _input_ arguments.
> time_t time = FileTimeToUnixTime(wintime, NULL);
> struct tm *tstruct;
> SP -= items;
PPCODE: should have do the stack adjust for input args already.
If you need this it is most likely because extra dXSARGS has messed
with things.
> 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.
> if (GIMME == G_ARRAY) {
> EXTEND(SP, 9);
> PUSHs(sv_2mortal(newSViv(tstruct->tm_sec)));
> [...] /* nine values overall */
> PUSHs(sv_2mortal(newSViv(tstruct->tm_isdst)));
> PUTBACK;
> return 9;
> } else {
> SV *str = newSVpvf("%s %s %2d %02d:%02d:%02d %d",
> dayname[tstruct->tm_wday],
> [...]
> tstruct->tm_year + 1900);
> EXTEND(SP, 1);
> PUSHs(sv_2mortal(str));
> PUTBACK;
> return 1;
> }
>
> [...]
>
> MODULE = Mail::Transport::Dbx PACKAGE = Mail::Transport::Dbx::Email
>
> void
> date_local (self)
> DBX_EMAIL *self;
> PREINIT:
> int count;
> PPCODE:
> count = datify(&(self->email->date), 0);
> XSRETURN(count);
>
>I encounter strange behaviour if I make slight changes, for instance
>decrementing SP in date_local() instead of datify(). In this case, one
>of the return values is dropped.
>
>The only thing I know for sure so far is that I have to use dXSARGS or
>dSP (probably depending on whether I use 'items') to be able to use the
>Perl stack in a C function. But I am not at all sure about ENTER, LEAVE
>SAVETMPS and FREETMPS. So what would be a minimum sceleton to call a
>C-function from a XSUB and have the C-function set up the return stack
>accordingly? Currently I would say:
>
> int func(...) {
> dXARGS;
> int num_return_values;
> SP -= items;
> PUSHMARK(SP);
> /* push stuff on stack using XPUSH or EXTEND/PUSH */
> PUTBACK;
> return num_return_values;
> }
>
> void XSfunc(...)
> ...
> PPCODE:
> XSRETURN(func());
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.
>
>Is that how it is supposed to work or do other ways exist? I would like
>to know those as they might make the whole stack-thing clearer to me.
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.
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.)
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.
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 ...
>
>Thanks in advance for any explanations,
>Tassilo
--
Nick Ing-Simmons
http://www.ni-s.u-net.com/