in reply to Perl XS: garbage-collecting my malloc'd buffer

This is almost the right way to do it. Altering parameters isn't a particularly perlish thing to do--it's a habit many people have because it's the only way to return multiple values from languages with more limited return semantics, such as C.

What you should do instead is switch from CODE to PPCODE and XPUSH() both the status and the return SV (which you built almost correctly--calling sv_2mortal on a value in the input list is dicey, but works correctly for return values) so that your function returns two values rather than one.

In general, you're on the right track--if you need to return a string, return an SV. (I'd go so far as to say always return an SV, but many folks would disagree)

Anyway, the code probably ought to look like:

SV * my_xs_func(p_struct, len) void *p_struct; IV len PPCODE: { char *tmp_buffer = (char *)malloc(len); XPUSHs(sv_2mortal(newSViv(my_func(p_struct, tmp_buffer, len) +))); XPUSHs(sv_2mortal(newSVpv(tmp_buffer, 0))); free(tmp_buffer); }

Replies are listed 'Best First'.
Re^2: Perl XS: garbage-collecting my malloc'd buffer (perlish?)
by tye (Sage) on Mar 03, 2003 at 17:29 UTC

    I'd think that ($len,$buf)= my_xs_func(...) would often be inconvenient and thus not very "perlish" (Perl places a large value on programmer convenience) and I consider the interface provided by Perl's own read and related functions to be "perlish". So I'd go with the original design.

    You should also note that your code assumes that the buffer gets populated with a '\0'-terminated string. [ Update: as does the original node's second code block, so that is probably a safe assumption (: ]

    If I was sure that the values being created were very unlikely to be very large and that they didn't contain internal pointers, then I might go with copying the generated value but only if the integer returned by the function was just the length so I could use:

    my $result= my_xs_funct($struct,$len); my $outlen= length($result);
    Mileage will certainly vary.

                    - tye
      I didn't put that much thought into the process, though I really despise routines that alter their parameters needlessly. (And in this case it really seems that way) The XS code could potentially be cleaned up some, with the length getting subsumed into the length of the resulting SV string, which'd be the better way to do it, I expect. Checking the want status is also reasonable, and having different returns based on it. Either of those (multiple returns, or want-based returns) are 'better' than altering the input, or at least so go my preferences.

      Still, I very much mistrust code that uses returned buffers from libraries. While it's OK in some cases, I've found myself burned often enough with library buffer reuse or freeing at odd times that I much prefer making a copy of the data. It's very rarely large enough to be a performance issue, and those cases can be handled specially. It's generally better, especially for folks who aren't used to writing XS code, to make the copy and optimize it away later, since that gets them the safe case by default.

        No, you can't get Perl to use buffers allocated by other libraries (I don't think it is OK in any cases -- and I've tried and been impressed with how hard Perl makes it to even use magic to get such working). But I wasn't doing that. I was having Perl allocate the buffer and having the C routine use it.

        If you are worried about the C routine (or some other routine from that library) reallocating or free()ing the buffer passed to it, then you'll have problems in those cases even with your solution. To avoid that you'd have to copy the data out of the Perl buffer before calling subsequent routines (something that isn't even shown to be happening). If the C routine in question reallocated or freed the buffer, then the interface to the C routine would simply be broken and there would be no safe way to use it. So I don't see any problem with my approach.

        I despise XS code that is complex. If I wanted to go to the point of checking wantarray etc. then I'd have a Perl subroutine wrapper for the XS code and do such interface massaging there rather than in the XS code. (:

                        - tye