7. Perl has a feature to load
an external C library.
use DynaLoader;
my @paths = dl_findfile(...);
my $lib = dl_load_file($path);
8. This won't work correctly
unless the library conforms
with Perl's convention.
use DynaLoader;
my @paths = dl_findfile(...);
my $lib = dl_load_file($path);
9. So we need something
in-between.
Perl XS C library
12. The simplest form of the one-
in-between starts like this:
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include <foo.h>
MODULE = Foo PACKAGE = Foo
int
func(const char* str)
13. These three lines are to use
Perl API.
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include <foo.h>
MODULE = Foo PACKAGE = Foo
int
func(const char* str)
14. This is for portability
between perls.
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include <foo.h>
MODULE = Foo PACKAGE = Foo
int
func(const char* str)
15. This is to import C functions
from the library.
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include <foo.h>
MODULE = Foo PACKAGE = Foo
int
func(const char* str)
17. The function declarations to
export (XSUBs) follow.
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include <foo.h>
MODULE = Foo PACKAGE = Foo
int
func(const char* str)
18. As with .h files, there should
be only declarations.
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include <foo.h>
MODULE = Foo PACKAGE = Foo
int
func(const char* str)
27. To build an interface
library, we need to
turn its XS declaration
into pure C code.
28. If you already have
Makefile.PL, run it, and
then, make.
$ perl Makefile.PL && make
29. If you have Build.PL, do
something like this (or
maybe ./Build build).
$ perl Build.PL && ./Build
30. If you don't have either,
Milla or Minilla will help
you (ask miyagawa-san).
31. The most important XSUB part
will be translated like this:
XS_EUPXS(XS_Foo_func)
{
dVAR; dXSARGS;
if (items != 1)
croak_xs_usage(cv, "str");
{
int RETVAL;
dXSTARG;
const char* str = (const char *)SvPV_nolen(ST(0));
RETVAL = func(str);
XSprePUSH; PUSHi((IV)RETVAL);
}
XSRETURN(1);
}
32. A function exposed to the Perl world
takes a Perl variable as its argument.
XS_EUPXS(XS_Foo_func)
{
dVAR; dXSARGS;
if (items != 1)
croak_xs_usage(cv, "str");
{
int RETVAL;
dXSTARG;
const char* str = (const char *)SvPV_nolen(ST(0));
RETVAL = func(str);
XSprePUSH; PUSHi((IV)RETVAL);
}
XSRETURN(1);
}
33. However, a Perl variable is
actually a structure in C.
XS_EUPXS(XS_Foo_func)
{
dVAR; dXSARGS;
if (items != 1)
croak_xs_usage(cv, "str");
{
int RETVAL;
dXSTARG;
const char* str = (const char *)SvPV_nolen(ST(0));
RETVAL = func(str);
XSprePUSH; PUSHi((IV)RETVAL);
}
XSRETURN(1);
}
34. You can't pass it directly to a C
function, and vice versa.
XS_EUPXS(XS_Foo_func)
{
dVAR; dXSARGS;
if (items != 1)
croak_xs_usage(cv, "str");
{
int RETVAL;
dXSTARG;
const char* str = (const char *)SvPV_nolen(ST(0));
RETVAL = func(str);
XSprePUSH; PUSHi((IV)RETVAL);
}
XSRETURN(1);
}
35. The Perl variable is converted
and cast into a C value here.
XS_EUPXS(XS_Foo_func)
{
dVAR; dXSARGS;
if (items != 1)
croak_xs_usage(cv, "str");
{
int RETVAL;
dXSTARG;
const char* str = (const char *)SvPV_nolen(ST(0));
RETVAL = func(str);
XSprePUSH; PUSHi((IV)RETVAL);
}
XSRETURN(1);
}
36. Then, the function imported
from the library is called.
XS_EUPXS(XS_Foo_func)
{
dVAR; dXSARGS;
if (items != 1)
croak_xs_usage(cv, "str");
{
int RETVAL;
dXSTARG;
const char* str = (const char *)SvPV_nolen(ST(0));
RETVAL = func(str);
XSprePUSH; PUSHi((IV)RETVAL);
}
XSRETURN(1);
}
37. And the return value is converted
into a Perl value again.
XS_EUPXS(XS_Foo_func)
{
dVAR; dXSARGS;
if (items != 1)
croak_xs_usage(cv, "str");
{
int RETVAL;
dXSTARG;
const char* str = (const char *)SvPV_nolen(ST(0));
RETVAL = func(str);
XSprePUSH; PUSHi((IV)RETVAL);
}
XSRETURN(1);
}
46. How to map should be described
in the "INPUT" and "OUTPUT"
sections, if necessary.
INPUT
T_PV
$var = ($type)SvPV_nolen($arg)
OUTPUT
T_PV
sv_setpv((SV*)$arg, $var);
47. These code fragments will be
interpolated while processing
an XS file.
INPUT
T_PV
$var = ($type)SvPV_nolen($arg)
OUTPUT
T_PV
sv_setpv((SV*)$arg, $var);
48. Adding orphan C types
is easy.
TYPEMAP
my_id T_IV
my_str T_PV
50. If you are confident, just
add a new code fragment in
your local typemap.
INPUT
T_PV
if (!SvOK($arg)) {
$var = NULL;
} else {
$var = ($type)SvPV_nolen($arg);
}
51. Or, you might want to add another
typedef in the XS file, before
MODULE and PACKAGE declarations.
typedef char * my_nullable_str;
MODULE Foo PACKAGE FOO
int
func(my_nullable_str str)
52. And then, add a behavior of this
new type in your local typemap.
TYPEMAP
my_nullable_str T_PV_OR_NULL
INPUT
T_PV_OR_NULL
if (!SvOK($arg)) {
$var = NULL;
} else {
$var = ($type)SvPV_nolen($arg);
}
53. There is also a much
trickier way to do it.
MODULE Foo PACKAGE FOO
int
func(char * str_or_null)
54. If you don't get why this
works, see perlref.
INPUT
T_PV
$var = ${
$var =~ /_or_null$/
? qq{NULL}
: qq{($type)SvPV_nolen($arg)}
}
55. Remove a star and prepend
"OUT" (or "IN_OUT") for an
argument called by reference.
MODULE Foo PACKAGE FOO
int
func(char * str, OUT int len)
62. With the help of
C::Scan, you can use it
to expose C functions as
well.
$ h2xs -Axan Foo /path/to/header.h
63. Defaulting to backwards compatibility with perl 5.xx.x
If you intend this module to be compatible with
earlier perl versions, please specify a minimum perl
version with the -b option.
Writing Foo/ppport.h
Scanning typemaps...
Scanning /home/xxxx/perl5/perlbrew/perls/perl-
5.xx.x/lib/5.xx.x/ExtUtils/typemap
Scanning /path/to/header.h for functions...
Scanning /path/to/header.h for typedefs...
Writing Foo/lib/Foo.pm
Writing Foo/Foo.xs
Writing Foo/typemap
Writing Foo/Makefile.PL
Writing Foo/README
Writing Foo/t/Foo.t
Writing Foo/Changes
Writing Foo/MANIFEST
64. If the library is stable, this may
give you a good starting point.
69. Convert::H::XS takes a C header file,
and looks for the minimum
information to write XS components.
use Convert::H::XS;
my $converter = Convert::H::XS->new;
$converter->process($h_file);
71. You can tweak those
pieces of information
with a callback.
$converter->write_functions(
"xs/functions.inc",
sub {
my ($type, $name, $args) = @_;
...
return ($type, $name, $args);
});
80. Issues are often raised by
other QA/toolchain people.
• Do not ship modules with Module::Install 1.04
http://weblog.bulknews.net/post/33907905561/do-not-ship-modules-with-module-install-1-04
• stop shipping MYMETA to CPAN
http://weblog.bulknews.net/post/44251476706/stop-shipping-mymeta-to-cpan
81. We need to confirm
they are measurable,
and widespread.