Thanks to Sisyphus and muppet for the pointer to perlxstut Example
#4. It's funny, I went through that tutorial back in May or June,
even typing in all the example code. But it seems I didn't retain
the bits I didn't understand.
On Nov 8, 2005, at 8:59 PM, muppet wrote:
dirty: write your extra C and H files. include them in MANIFEST.
#include them from your xs code's verbatim C section.
Most of the code I'm talking about needs to be #include-d anyway:
1) Wrapper functions around PerlIO_xxxxx, e.g. KinoIO_read_vint below
2) typedefs and init/manip/destroy routines for the new types
3) global constants, e.g. "#define A_GLOBAL_CONSTANT 255"
4) Utilities, e.g. Kino_confess which allows Carp::confess to be
invoked from C a la the sprintf croak/warn.
All that stuff is supposed to be accessible throughout the
amalgamated KinoSearch.xs -- it's dirty by design. But it makes
sense to break it up into a few C modules, e.g. KinoIO.h/KinoIO.c.
... [ time passes while Marvin experiments ] ...
Hey wait a minute... you're going to need to #include at least one of
the .h files in the .xs file regardless, aren't you? Otherwise
there's no way to get at the stuff in the compiled libraries from
Perl. So I take it what you're calling "dirty" is the *absence* of
more rigorous steps, not the mere existence of an #include directive
in the verbatim C section of the .xs file.
proper: follow the example of EXAMPLE 4 in perlxstut, and build a
little library for yourself, which you link in. c.f. L<perlxstut>
OK, ultimately, this is what I settled on. But I had to pass through
the other stages before I understood enough to make it work. Didn't
know what make did, didn't know what ar or ranlib did... I could do
decent enough memory management to write a multi-field mergesort
algo with a bunch of pointer acrobatics and not leak, but I knew
next to nothing about compiling! That's where starting off with
Inline::C gets ya.
quick and just as good: add your extra files' names to MANIFEST so
that they get dirstributed, and add extra object files to the
OBJECT key to WriteMakefile, like so:
WriteMakefile (
...
OBJECT => q[$(BASE_EXT)$(OBJ_EXT) myextracfile$(OBJ_EXT)],
...
);
Worked like a charm, once I got all my .c/.h files sorted out and
figured out how to use include guards to stop those redefinitions...
that is, until I tried to tidy up and move the new files into a src/
directory. boom!
Note that you are overwriting the default for OBJECT, so we supply
that default manually -- that's what $(BASE_EXT)$(OBJ_EXT) is. You
specify only the object file version of your filename -- for
myextracfile.c you'd specify myextracfile$(OBJ_EXT), which gets
expanded to myextracfile.o or myextracfile.obj or whatever is
correct for the platform; the make rules can figure out that this
needs to be built from a c file with the same name. See
L<ExtUtils::MakeMaker> for more info -- search for OBJECT.
An excellent explanation. Thanks.
Make is actually quite easy, once you understand how it does
bookkeeping.
This was useful to know. Between Make's rep as "evil" due to the tab/
space issue and hearing Michael Schwern (maintainer of MakeMaker) let
off steam about MakeMaker at Portland Perl Mongers meetings, I had
gotten the impression that it was much more complex than it actually
turns out to be.
This was the best of the various overviews I stumbled across while
googling:
http://www.cprogramming.com/tutorial/makefiles.html
Cheers,
Marvin Humphrey
Rectangular Research
http://www.rectangular.com/
/**************************************************************/
/* read in a Variable INTeger, stored in 1-5 bytes */
U32
KinoIO_read_vint (PerlIO *fh) {
unsigned char aUChar;
int bitshift;
int check_val;
U32 aU32;
/* start by reading one byte; use the lower 7 bits */
check_val = PerlIO_read(fh, &aUChar, 1);
if (check_val < 1)
Kino_confess("KinoIO_read_vint error: %d", check_val);
aU32 = aUChar & 0x7f;
/* keep reading and shifting as long as the high bit is set */
for (bitshift = 7; (aUChar & 0x80) != 0; bitshift += 7) {
check_val = PerlIO_read(fh, &aUChar, 1);
if (check_val < 1)
Kino_confess("KinoIO_read_vint error: %d", check_val);
aU32 |= (aUChar & 0x7f) << bitshift;
}
return aU32;
}