Build and Check a Perl XS Typemap with xsubpp
You will finish with a small XS source file, a separate typemap, and generated C that proves the mapping was applied. The example maps a C long through the explicit T_LONG XS type, then checks the generated input and output code. Allow about fifteen minutes if Perl development tools are already installed.
The route
Jump straight to the step you need, or tick off Done means at the end.
This guide describes the Perl 5.38.2 toolchain installed on this machine. Its ExtUtils::ParseXS version is 3.51. The local perlxstypemap(1) manual is dated 14 September 2026 and documents the typemap format, core XS types and interpolation variables. A typemap is build-time source: it does not change Perl or the operating system by itself.
1. Check the XS compiler
Run these ordinary, read-only checks from a working directory. No elevated privileges are needed.
command -v xsubpp
perl -MExtUtils::ParseXS -e 'print $ExtUtils::ParseXS::VERSION, "\n"'
xsubpp -v
On this machine the first command prints /usr/bin/xsubpp, the second prints 3.51, and the final command prints the installed compiler version. The exact formatting of xsubpp -v can vary, so the useful checkpoint is that the command exists and returns successfully.
If xsubpp is missing, install the Perl development package through your normal package-management process before continuing. Do not use sudo merely to run the compiler.
2. Write the typemap
Create a file named typemap. The TYPEMAP section associates a C type with an XS type. Here, T_LONG is the explicit core mapping for a C long.
TYPEMAP
long T_LONG
This is intentionally a small, visible mapping. A distribution typemap can instead associate a project type such as a typedef with an existing core map, or define new INPUT and OUTPUT code. Keep the C type spelling exactly aligned with the declaration used by the XS file.
Checkpoint: inspect the file before adding more sections.
sed -n '1,20p' typemap
The section name must be uppercase and start in column one. An unlabelled first section is treated as TYPEMAP, but spelling it out makes review easier.
3. Add one XSUB that uses the mapping
Create demo.xs beside the typemap:
MODULE = TypemapDemo PACKAGE = TypemapDemo
PROTOTYPES: ENABLE
long
add_one(value)
long value
CODE:
RETVAL = value + 1;
OUTPUT:
RETVAL
The XSUB accepts a Perl value, converts it to the declared C long, adds one in C, and converts the return value back to Perl. PROTOTYPES: ENABLE asks xsubpp to emit an XS prototype for this function; it is unrelated to the typemap itself.
There is no need to compile a complete shared object for this checkpoint. The purpose is to verify that xsubpp can select the typemap and expand the XSUB.
4. Generate and inspect the C
Run xsubpp with the typemap explicitly named:
xsubpp -typemap ./typemap ./demo.xs > ./demo.c
printf 'xsubpp status: %s\n' "$?"
Expected output is:
xsubpp status: 0
The command writes generated C to demo.c. It does not install anything and does not need root access. A non-zero status means the generated file is not a valid checkpoint; read the diagnostic on standard error and fix the XS or typemap rather than compiling partial output.
Now check the expansion:
grep -nE 'long[[:space:]]+value|SvIV|PUSHi|add_one' demo.c
You should find code containing the input conversion (long)SvIV(ST(0)), a long return variable and an integer push such as PUSHi((IV)RETVAL). Whitespace and generated line numbers are not stable. These names are the useful evidence that the selected T_LONG map handled both directions.
5. Add custom conversion only when the core map is insufficient
For ordinary integers, strings and Perl references, reuse a core XS type where it expresses the ownership and conversion rules you need. A typemap's INPUT section converts Perl data into a C variable; its OUTPUT section converts a C value back. Each map starts with the XS type name on an unindented line, followed by indented C code.
INPUT
MY_TYPE
$var = convert_from_perl($arg)
OUTPUT
MY_TYPE
convert_to_perl($arg, $var)
The words beginning with dollar signs are xsubpp substitutions, not shell variables. $var is the C variable, $type the raw declared C type, $arg the Perl stack entry, and $ntype the normalised type with asterisks changed to Ptr. Other available values include $pname, $Package, $ALIAS and $argoff.
Do not put shell syntax in these sections. Also remember that a line beginning with # is ignored as a comment in TYPEMAP, but is significant in INPUT and OUTPUT. That difference can turn a seemingly harmless comment into generated C.
6. Treat pointer and reference maps as ownership decisions
T_PTRREF hides a pointer in a scalar reference. T_PTROBJ additionally blesses it into a class and accepts subclasses. The class name is derived from the pointer type, with pointer stars normalised to Ptr. These maps do not allocate or free the pointed-to C object for you.
For reference maps, read the exact core entry before choosing one. The manual records that older forms such as T_SVREF, T_AVREF and T_HVREF do not decrement the reference count on output, while the _REFCOUNT_FIXED variants do. The fixed variants were introduced in Perl 5.15.4 and are available in this Perl 5.38.2 installation. A wrong choice can leak memory or release something at the wrong time, so do not change an existing map casually.
T_PACKED and T_PACKEDARRAY call functions named from the normalised type, such as XS_pack_foo_tPtr and XS_unpack_foo_tPtr. If those functions allocate memory, define and document who frees it. This is a boundary where a successful xsubpp run cannot prove memory safety.
7. Keep the generated file disposable
demo.c is an output, not the source of truth. Make changes in demo.xs or typemap, then regenerate it. In a CPAN distribution, the traditional separate file is normally named typemap; modern ExtUtils::ParseXS also supports embedding a typemap in XS with a TYPEMAP: <<HERE block. Use one approach consistently so reviewers know where mappings live.
Before committing a change, repeat both checks:
xsubpp -typemap ./typemap ./demo.xs > ./demo.c
grep -nE 'long[[:space:]]+value|SvIV|PUSHi|add_one' ./demo.c
Done means
xsubppis present and reports a successful run.- The typemap's C type matches the XS declaration exactly.
- The generated C contains the expected input and output conversions.
- Pointer ownership and reference-count behaviour have been checked against the selected core map.
- Future edits will regenerate the disposable C output instead of hand-editing it.