Alien-TinyCDB
view release on metacpan or search on metacpan
.claude/skills/perl-xs/SKILL.md view on Meta::CPAN
#define NEED_mg_findext /* one NEED_ per ppport.h shim actually used */
#include "ppport.h"
#include <foolib/foolib.h> /* the library being wrapped */
```
`PERL_NO_GET_CONTEXT` belongs in every XS file: without it each function that
touches the interpreter looks it up through thread-local storage. With it, the
interpreter travels as a parameter â which is why your own static helpers take
`pTHX_` and are called with `aTHX_`.
Each `NEED_foo` asks `ppport.h` to emit a static implementation of `foo` for Perls
that lack it, and must appear exactly once per compilation unit.
## MODULE, PACKAGE, PREFIX
```
MODULE = Foo PACKAGE = Foo
PROTOTYPES: DISABLE
```
- Every XSUB after that line is installed into that package, until the next
`MODULE`/`PACKAGE` line. One `.xs` file can serve a whole class family â a
connection, the handles opened on it and its subsystems all live in one `Foo.xs`.
- `PROTOTYPES: DISABLE` goes once after the first `MODULE` line; xsubpp carries it
into the later packages of the same file. Leaving it out earns a warning per XSUB.
- `PREFIX = foolib_` strips a C prefix from the Perl-visible name. It pays when
wrapping a C API one-to-one, and only obscures things when the Perl API was
designed separately.
## The object rule
A C pointer reaches Perl as **magic on a blessed SV**, under an `MGVTBL` that is
unique to its type:
```c
static int foo_conn_free(pTHX_ SV *sv, MAGIC *mg) {
FOO_Conn *self = (FOO_Conn *)(void *)mg->mg_ptr;
if (self->conn) { foolib_close(self->conn); foolib_free(self->conn); }
Safefree(self);
return 0;
}
static const MGVTBL Foo_magic = { .svt_free = foo_conn_free };
```
`svt_free` is the destructor, and it is a better one than a Perl method: it fires
when the SV is collected, it still fires during global destruction, and no subclass
can override it away. **An XS object needs no `DESTROY` sub** â not in the XS, not
in the `.pm`.
The vtable address is also the type check. `mg_findext(sv, PERL_MAGIC_ext,
&Foo_magic)` matches only magic carrying that exact vtable, so a hand-blessed
hashref croaks at the boundary instead of segfaulting through an `INT2PTR` cast on
a pointer that was never there. That is the reason to skip `T_PTROBJ`, which stores
the pointer with `sv_setref_pv` and offers neither a free hook nor a real type
check.
## Four ways it goes wrong
1. **An unescaped `"` in the typemap.** xsubpp reads INPUT/OUTPUT templates as Perl
double-quoted strings, so a quote meant for the generated C must be written
`\"`. The failure surfaces as a C syntax error in code you never wrote.
2. **The refcount taken on the wrong SV.** `ST(0)` is the reference; `SvRV(ST(0))`
is the blessed referent carrying the magic. A child object that keeps its parent
alive must increment the **referent** â incrementing `ST(0)` survives scope exit
and segfaults on `undef $parent`, which is the case tests reach for last.
3. **`XSRETURN_UNDEF` bypasses the OUTPUT section.** It is the way to return undef
instead of an object, and it means everything allocated up to that point leaks
unless the branch frees it first.
4. **A NULL handle the C library tolerates.** After a close or free, many libraries
accept a NULL handle and return a plausible wrong answer instead of crashing â
`-1`, or an empty string. Every method needs an explicit open-check that croaks,
or the bug stays invisible in testing.
## Build
An XS distribution is built by `ExtUtils::MakeMaker`; the flags for an external
library come from whatever resolves them (`Alien::*`, `pkg-config`, hardcoded):
```perl
WriteMakefile(
LIBS => [ '-lfoo' ],
INC => '-I/usr/include',
OBJECT => 'Foo$(OBJ_EXT)', # matches the .xs basename
);
```
Under Dist::Zilla with `[@Author::GETTY]`, that `Makefile.PL` is generated from two
lines in `dist.ini` (`xs_alien` names the Alien module, `xs_object` the `.xs`
basename), and `dzil build` is then the only supported path â a hand-written
`Makefile.PL` in the working directory resolves its flags differently from the
release and makes a green local `make test` mean nothing. Details in
`getty-perl-release-author-getty`; the library side in `perl-alien`.
( run in 1.379 second using v1.01-cache-2.11-cpan-54e63673c56 )