Excerpts from the mail message of Clinton A. Pierce:
)
) Ok, I've been through perlxstut, and I'm pretty comfortable there. I
) just wish it had one more example covering something like this.
Yes, I think the coverage of things requiring memory allocation is
almost zero.
) What I have is a library that essentially populates a structure (the
) memory for which the caller is assumed to have allocated) and then
) performs calculations on that structure.
Well, let's jump right to a solution. In XS:
SV *
populate()
preinit:
struct pinfo rec;
code:
populate( &rec );
XSRETURN_PVN( (char *)&rec, sizeof(rec) );
SV *
calculate( payroll )
char * payroll
preinit:
struct pinfo rec;
code:
if( SvCUR(ST(0)) < sizeof(rec) )
XSRETURN_UNDEF; /* Passed in too short of a string */
memcpy( (char *)&rec, payroll, sizeof(rec) );
caculate( &rec );
XSRETURN_PVN( (char *)&rec, sizeof(rec) );
Perl:
$rec= populate();
$rec= calculate( $rec );
or just:
$rec= calculate populate;
) I was just hoping to get the structure
) stored in a perl scalar that I could unpack as I needed...
Yo got it, mang!
Now, the analysis, in case you care. Since the library lets the
caller allocate the buffer, you can do this easily. For your
case, I'd probably do:
* Have the C code allocate a temporary buffer, pass a pointer to
that into your library, copy the buffer contents to an SV.
This has several important advantages:
+ Copying buffer contents into an SV is well covered [in
documentation and APIs] and so is very easy to do.
+ Allocating a temporary buffer in C is usually within the
skill set of an XS writer.
+ This is usually hard to do well with a typemap so you
don't have to learn about typemaps to do it. :)
There are a few cases where you probably wouldn't want to do
this:
- If the buffer contains pointers to items in the buffer
[copying invalidates such pointers].
- If the buffer is large [copying large buffers takes too
much CPU time].
- If the required buffer size is hard to predict [you can
still handle this case this way, but people who do this
often make poor compromises, like assuming that needing
more than X bytes is unreasonable].
- You are wrapping a ton of these type of APIs so you'd
really rather have a typemap.
- You want to handle input-only, output-only, and update
buffers in a consistant manner.
Another solution is:
* Have the Perl code pass in an SV to the XS, have the XS ensure
that the SV has a big enough buffer, pass the SV buffer to the
library, if the library modified the buffer contents, then
"tell" the SV that its value has changed. This fixes all of
the problems mentioned, though two are only partially addressed:
+ If the required buffer size is hard to predict, using this
solution will usually result in an interface where the
Perl code that calls the XS can force an extra large buffer
to be allocated.
+ For some cases, you can't do this well in a typemap.
Of course, it has a few major disadvantages:
- The documentation falls far short here.
- You'll need several macros for each step instead of just
one macro at the end.
- There are a lot of details [most of which are not
critical] that you won't get right at first.
If you are interested in this, grab the latest Win32API::Registry
and read F<buffers.h>.
Then there is the solution of:
* Have the XS code wrap an object around the buffer.
That is what T_PTROBJ is about. I personally hate this option
[if you want to create an object, do it in Perl code]. So I'd
never consider it unless the library won't allow me to allocate
the buffer.
) The example in perlxs confuses me when I get to the part about
) "Netconfig *T_PTROBJ". What is this?
I don't think this is completely documented. It is about the
only support in XS that can kind of handle buffers that are not
allocated by Perl. You stuff a pointer to the buffer into an SV
and bless it [making a Perl object] and then write XS routines
that act as methods for this object.
) What's this typemap file and where does it go, and what should
) it be named? Do I need an INPUT and OUTPUT section?
If it called "typemap" and it goes in the same directory as your
*.xs file. You can see a sample in perl/lib/ExtUtils/typemap.
To handle C<populate>, you'd only need an OUTPUT section. For
C<calculate>, you'd need both. Code in the INPUT section is used
for [by default] all parameters. Code in the OUTPUT section is
used for things mentioned in a routine's OUTPUT section [include
RETVAL which is in the OUTPUT section by default].
) And why does perlxs muddle the discussion about passing structures back
) and forth with a discussion about perl namespaces? That just confuses
) me further.
Because handling the worst cases of dealing with structures
requires dealing with memory not allocated by Perl which requires
black magic which the author thinks is best done by creating
objects, which messes with perl namespaces??
) struct pinfo *payroll;
) payroll=(struct pinfo *)malloc(sizeof (struct pinfo));
Why not just this:
struct pinfo rec;
payroll= &rec;
Then you don't have to C<free(payroll)> [which you forgot to do].
--
Tye McQueen Nothing is obvious unless you are overlooking something
http://www.metronet.com/~tye/ (scripts, links, nothing fancy)