Module::CAPIMaker - Provide a C API for your XS modules
perl -MModule::CAPIMaker -e make_c_api
If you are the author of a Perl module written using XS. Using Module::CAPIMaker you will be able to provide your module users with an easy and efficient way to access its functionality directly from their own XS modules.
The exporting/importing of the functions provided through the C API is completely handled by the support files generated by Module::CAPIMaker, and not the author of the module providing the API, neither the authors of the client modules need really to know how it works but on the other hand it does not harm to understand it and anyway it will probably easy the understanding of the module usage. So, read on :-) ..
Suppose that we have one module Foo::XS Foo::XS providing a C API with the help of Module::CAPIMaker and another module Bar that uses that API.
Foo::XS
Bar
When Foo::XS is loaded, the addresses of the functions available through the C API are published on the global hash %Foo::XS::C_API.
%Foo::XS::C_API
When Bar loads, first, it ensures that Foo::XS is loaded (loading it if required) and checks that the versions of the C API supported by the version of Foo::XS loaded include the version required. Then, it copies the pointers on the %Foo::XS::C_API hash to C static storage where they can be efficiently accessed without performing a hash look-up every time.
Finally calls on Bar to the C functions from Foo::XS are transparently routed through these pointers with the help of some wrapping macros.
The C API is defined in a file named c_api.decl. This file may contain the prototypes of the functions that will be available through the C API and several configuration settings.
c_api.decl
From this file, Module::CAPIMaker generates two sets of files, one set contains the files that are used by the module providing the C API in order to support it. The other set is to be used by client modules.
Module::CAPIMaker
They are as follows:
On the C API provider side, a file named c_api.h is generated. It defines the initialization function that populates the %C_API hash.
c_api.h
%C_API
There are two main files to be used by client modules, one is perl_${c_module_name}.c containing the definition of the client side C API initialization function.
perl_${c_module_name}.c
The second file is perl_${c_module_name}.h containing the prototypes of the functions made available through the C API and a set of macros to easy their usage.
perl_${c_module_name}.h
${c_module_name} is the module name lower-cased and with every non-alphanumeric sequence replaced by a underscore in order to make in C friendly. For instance, Foo::XS becomes foo_xs and the files generated are perl_foo_xs.c and perl_foo_xs.h.
${c_module_name}
foo_xs
perl_foo_xs.c
perl_foo_xs.h
A sample/skeleton Sample.xs file is also generated.
Sample.xs
The client files go into the directory c_api_client where you may also like to place additional files as for instance a typemap file.
c_api_client
typemap
The C API is defined through the file c_api.decl.
Two types of entries can be included in this file: function prototypes and configuration settings.
Function declarations are identical to those you will use in a C header file (without the semicolon at the end). In example:
int foo(double) char *bar(void)
Functions that use the THX macros are also accepted:
SV *make_object(pTHX_ double *)
You have to use the prototype variant (pTHX or pTHX_) and Module::CAPIMaker will replace it automatically by the correct variant of the macro depending of the usage.
pTHX
pTHX_
Configuration settings are of the form key=value where key must match /^\w+$/ and value can be anything. For instance
key=value
module_name = Foo::XS author = Valentine Michael Smith
A backslash at the end of the line indicates that the following line is a continuation of the current one:
some_words = bicycle automobile \ house duck
Here-docs can also be used:
some_more_words = <<END car pool tree disc book END
The following configuration settings are currently supported by the module:
Perl name of the module, for instance, Foo::XS.
C-friendly name of the module, for instance foo_xs
Version of the Perl module. This variable should actually be set from the Makefile.PL script and is not used internally by the C API support functions. It just appears on the comments of the generated files.
Makefile.PL
Name of the module author, to be included on the headers of the generated files.
In order to support evolution of the C API a min/max version approach is used.
The min_version/max_version numbers are made available through the %C_API hash. When the client module loads, it checks that the version it requires lays between these two numbers or otherwise croaks with an error.
min_version
max_version
By default required_version is made equal to max_version.
required_version
The directory where the client support files are placed. By default c_api_client.
Name of the client support header file (i.e. perl_foo_xs.h).
Name of the client C file providing the function to initialize the client side of the C API (i.e. perl_foo_xs.c).
Name of the support file used on the C API provider side. Its default value is c_api.h.
Name of the macro used to avoid multiple loading of the definitions inside ${module_h_filename}.
${module_h_filename}
It defaults to uc("${c_module_name}_H_INCLUDED"). For instance PERL_FOO_XS_H_INCLUDED.
uc("${c_module_name}_H_INCLUDED")
PERL_FOO_XS_H_INCLUDED
Name of the macro used to avoid multiple loading of the definitions on the header file c_api.h. It defaults to C_API_H_INCLUDED.
C_API_H_INCLUDED
It's possible to add a prefix to the name of the functions exported through the C API in the client side.
For instance, if the function bar() is exported from the module Foo::XS, setting export_prefix=foo_ will make that function available on the client module as foo_bar().
bar()
export_prefix=foo_
foo_bar()
The text in this variable is placed inside the file ${module_c_filename} at some early point. It can be used to inject typedefs and other definitions needed to make the file compile.
${module_c_filename}
The text inside this variable is placed at the end of the file ${module_c_filename}.
The text inside this variable is placed inside the file ${module_h_filename} at some early point.
The text inside this variable is placed at the end of the file ${module_h_filename}.
The name of the file with the definition of the C API. Defaults to c_api.decl.
Obviously this setting can only be set from the command line!
Internally the module uses Text::Template to generate the support files with a set of default templates. For maximum customizability a different set of templates can be used.
See "Customizing the C API generation process".
Once your c_api.decl file is ready use Module::CAPIMaker to generate the C API running the companion script make_perl_module_c_api. This script also accept a list of configuration setting from the command line. For instance:
make_perl_module_c_api
make_perl_module_c_api module_name=Foo::XS \ author="Valentine Michael Smith"
If you want to do it from some Perl script, you can also use the make_c_api sub exported by this module.
make_c_api sub
In order to initialize the C API from the module supporting it you have to perform the following two changes on your XS file:
Include c_api.h.
Call the macro INIT_C_API from the BOOT section.
INIT_C_API
BOOT
For instance,
#include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" /* your C code goes here */ #include "c_api.h" MODULE = Foo::XS PACKAGE = Foo::XS BOOT: INIT_C_API; /* your XS function declarations go here */
In order to get the C API interface files regenerated every time the file c_api.decl is changed, add the following lines at the end of your Makefile.PL script.
package MY; sub postamble { my $self = shift; my $author = $self->{AUTHOR}; $author = join(', ', @$author) if ref $author; $author =~ s/'/'\''/g; return <<MAKE_FRAG c_api.h: c_api.decl \tmake_perl_module_c_api module_name=\$(NAME) module_version=\$(VERSION) author='$author' MAKE_FRAG } sub init_dirscan { my $self = shift; $self->SUPER::init_dirscan(@_); push @{$self->{H}}, 'c_api.h' unless grep $_ eq 'c_api.h', @{$self->{H}}; }
You may also like to include the generated files into the file MANIFEST in order to not require your module users to also have Module::CAPIMaker installed.
MANIFEST
The module Module::CAPIMaker exports the subroutine make_c_api when loaded. This sub parses module settings from @ARGV and the file a_api.decl and performs the generation of the C API support files.
make_c_api
a_api.decl
In order to use functions provided by a XS module though a C API generated by Module::CAPIMaker, you have to perform the following steps:
Copy the files from the c_api_client directory into your module directory.
On your Makefile.PL script, tell ExtUtils::MakeMaker to compile and link the C file just copied. That can be attained adding OBJECT => '$(O_FILES)' arguments to the WriteMakefile call:
OBJECT => '$(O_FILES)'
WriteMakefile
WriteMakefile(..., OBJECT => '$(O_FILES)', ...);
Include the header file provided from all the compilation units (usually .c and .xs files) that want to use the functions available through the C API.
.c
.xs
Initialize the client side of the C API from the BOOT section of your XS module calling the load-or-croak macro. For instance:
#include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" #include "perl_foo_xs.h" MODULE=Bar PACKAGE=Bar BOOT: PERL_FOO_XS_LOAD_OR_CROAK;
Internally, the module uses Text::Template to generate the support files.
In order to allow for maximum customizability, the set of templates used can be changed.
As an example of a module using a customized set of templates see Math::Int64.
The default set of templates is embedded inside the sub-modules under Module::CAPIMaker::Template, you can use them as an starting point.
Finally, if you find the module limiting in some way don't hesitate to contact me explaining your issued. I originally wrote Module::CAPIMaker to solve my particular problems but I would gladly expand it to make it cover a wider range of problems.
Math::Int128, Math::Int64, Tie::Array::Packed are modules providing or using (or both) a C API created with the help of Module::CAPIMaker.
Copyright (C) 2012 by Salvador Fandiño, <sfandino@yahoo.com>.
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.12.4 or, at your option, any later version of Perl 5 you may have available.
To install Module::CAPIMaker, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Module::CAPIMaker
CPAN shell
perl -MCPAN -e shell install Module::CAPIMaker
For more information on module installation, please visit the detailed CPAN module installation guide.