Nick Ing-Simmons wrote:

Steve Hay <[EMAIL PROTECTED]> writes:


I understand the second case there -- when the xsub is called as "hello(1)" -- but that leaves me wondering what ST(0) was in the case where the xsub was called without arguments.



It is a stack - a hunk of memory.


So if you don't have any args one it is:
- the SV returned by last thing caller called, or - if that didn't return anything then 1st arg of last call, or - some arg passed/returned by someone or - garbage that will give you a segfault


if nothing has been called Nth arg of the caller.

So it's not a great thing to be relying on then ;)

(And that would explain why different people observed different results when just using ST(0) rather than assigning something new to it - different things will happen depending on what context the sample code is dumped into.)





Presumably I should therefore rewrite the above as:

  void
  hello()
      PREINIT:
          char    *chartext;

      CODE:
          Newz(0, chartext, 13, char);
          strcpy(chartext, "Hello, world");
          ST(0) = newSV(0);
          sv_usepvn(ST(0), chartext, 13);
          XSRETURN(1);

to be sure of what ST(0) really is.



Yes.


By the way what is the reason you do a PREINIT which does no init rather
than


CODE:
{
 char *chartext;
 ...
}

?

I once fell foul of the problem described in the PREINIT entry in the perlxs manpage, namely: "If a variable is declared inside a CODE: section it will follow any typemap code that is emitted for the input parameters. This may result in the declaration ending up after C code, which is C syntax error."

I was very new to XS at the time (even newer than I am now ;), and was confused for ages by it. I've used PREINIT to declare all my variables ever since then just to play it safe.

Is there a problem with using PREINIT all the time in this way?




After a quick skim-read of the perlcall manpage I tried writing another xsub called callhello() that calls the hello() xsub above and simply returns whatever hello() returned:

void
callhello()
CODE:
dSP;
PUSHMARK(SP);


PUTBACK; // not essential with no args


call_pv("Foo::hello", G_SCALAR);


SPAGAIN; // always required


ST(0) = POPs;


PUTBACK; // Not essential if returning next ...

XSRETURN(1);

However, when I call that from Perl ("my $greeting = Foo::callhello()") I find that callhello() returned 1 rather than "hello".


You need in general the PUTBACK/SPAGAIN when stack pointer has changed.
In particular calling something may grow the stack.

Obviously my skim-read of the perlcall manpage skimmed a bit too much :( I missed the SPAGAIN bit. Simply adding that alone to my example fixes it!


As POPs changes stack pointer it is perhaps less risky to your sanity to do things in stages:


call_pv(...); // stack may move SPAGAIN; // Get current stack
val = POPs; // stack changes
PUTBACK; // update stack
ST(0) = val;
XSRETURN(1);


OK, that sounds like good advice. I'm all for avoiding problems before they crop up!

At this point I have:

   void
   hello(...)
       PREINIT:
           char    *chartext;

       CODE:
           Newz(0, chartext, 13, char);
           strcpy(chartext, "Hello, world");
           ST(0) = newSV(0);
           sv_usepvn(ST(0), chartext, 13);
           XSRETURN(1);

   void
   callhello()
       PREINIT:
           SV *val;

       CODE:
           dSP;
           PUSHMARK(SP);
           PUTBACK;
           call_pv("Foo::hello", G_SCALAR);
           SPAGAIN;
           val = POPs;
           PUTBACK;
           ST(0) = val;
           PUTBACK;
           XSRETURN(1);

So it seems that you can, after all, assign to ST(0) even when an xsub is called with no arguments from another xsub. I'm puzzled by that: I thought that before assigning to ST(0), ..., ST(n) you had to call EXTEND(SP, n + 1). Calling EXTEND(SP, n) would suffice when the xsub is called from Perl, but not when called from another xsub. In this case we are calling from another xsub; n is 0, so I thought I would have needed to call EXTEND(SP, 1) first.

There's one other problem too: The above code leaks if I call callhello() a million times over in a for loop. Where is the leak coming from?





(Am I correct in thinking that I don't require the "ST(0) = newSV(0)" bit this time since I'm assigning something else to ST(0) anyway rather than telling an already extant ST(0) to "use" a char * elsewhere.)

I then tried again, this time passing an argument to the hello() xsub (after changing it as before to accept arguments):

  void
  callhello()
      CODE:
          dSP;
          ENTER;
          SAVETMPS;
          PUSHMARK(SP);
          XPUSHs(sv_2mortal(newSViv(1)));
          PUTBACK;
          call_pv("Foo::hello", G_SCALAR);
          ST(0) = POPs;
          FREETMPS;
          LEAVE;
          XSRETURN(1);

This time callhello() works, returning "Hello, world".


This is most likely because this time YOUR XPUSHs grew the stack,
not the call, so SP is still the value you PUTBACK.

But there is a new snag with that code. You have added a SAVETMPS/FREETMPS
pair. So if Foo::hello had used the 'mortal' style the SV you re-used
will be free-d when you hit the FREETMPS, and callhello's caller
would see an undef.

The SAVETMPS/FREETMPS was to have the (mortal) argument that I'm passing to Foo::hello free()'d; I'd forgotten that it free()'s any mortal return values as well.

Here's the example with hello() returning a mortal SV * that has its own char * space:

   void
   hello(...)
       PREINIT:
           char    *chartext;

       CODE:
           Newz(0, chartext, 13, char);
           strcpy(chartext, "Hello, world");
           ST(0) = sv_2mortal(newSVpv(chartext, 13));
           XSRETURN(1);

   void
   callhello()
       PREINIT:
           SV *val;

       CODE:
           dSP;
           ENTER;
           SAVETMPS;
           PUSHMARK(SP);
           XPUSHs(sv_2mortal(newSViv(1)));
           PUTBACK;
           call_pv("Foo::hello", G_SCALAR);
           SPAGAIN;
           val = POPs;
           PUTBACK;
           ST(0) = val;
           PUTBACK;
           FREETMPS;
           LEAVE;
           XSRETURN(1);

It does indeed result in callhello()'s caller getting undef.

So how would I avoid coming a cropper like that? I want the arguments that callhello() passes to hello() to be freed before callhello() returns, but I don't want hello()'s return value to be free()'d then as well.

This seems to work:

       ...
       call_pv("Foo::hello", G_SCALAR);
       SPAGAIN;
       val = newSVsv(POPs);
       PUTBACK;
       FREETMPS;
       LEAVE;
       ST(0) = sv_2mortal(val);
       PUTBACK;
       XSRETURN(1);

Is that OK or is there a more efficient way to do this?

- Steve



Reply via email to