
InlineX Cookbook

As of version 0.13 of this module, it's possible to provide the C/CPP code to InlineX::C2XS/InlineX::CPP2XS by supplying either the CODE or SRC_LOCATION arguments as key/value pairs in the hashref that forms the the final argument given to c2xs()/cpp2xs(). This documentation was first written prior to version 0.13 - I'll leave the first parts of it as is, but bear in mind that the C source can now be stored anywhere in any file and you just provide the name and location (either fully qualified or relative) of that file using the SRC_LOCATION=>'./location/file.ext' key/value pair ... or you can just store the code as one big string in a variable (say, $code) in which case you supply the code using the CODE=>$code key/value pair. For a demo of these capabilities that became available in 0.13, see the section "UPDATE for 0.13 and later" at the end of this document.
The examples that follow here utilise InlineX::C2XS. The same principles apply to InlineX::CPP2XS.
Create a separate directory to work through this demo (let's call it 'test' - though you can call it whatever you like), then 'cd' to the newly created 'test' directory, and create 2 folders in it - 'src' and 'My-Mod-0.01'. The 'src' folder is where the C code gets placed (and the folder must be named 'src'). The 'My-Mod-0.01' folder is where the various files that InlineX can create will be written. (That folder does not have to be named 'My-Mod-0.01' - call it whatever you want. For the purpose of this exercise, I'm assuming you *have* named it'My-Mod-0.01').
In the 'src' folder place a C file named Mod.c that contains:
int plus (int x, int y) {
return x + y;
}
int minus (int x, int y) {
return x - y;
}
Since the module we are building in this particular exercise is named My::Mod, the C file must be named 'Mod.c'. That is, the .c file must have a '.c' extension, and it must have the same name as the .pm file.
To create Mod.xs, place the following file (which I've named 'create.pl') in the 'test' directory:
use warnings;
use strict;
use InlineX::C2XS qw(c2xs);
my $mod = 'My::Mod';
my $pkg = $mod;
my $build_dir = './My-Mod-0.01';
c2xs($mod, $pkg, $build_dir);
Then run create.pl.
You should now find Mod.xs in test/My-Mod-0.01. We've taken care of one of the files that we'll need to build My::Mod. Note that INLINE.h was not created. That's because the C code in Mod.c does not need INLINE.h. Let's change that by adding the following to the bottom of Mod.c:
void plus_minus(int x, int y) {
Inline_Stack_Vars;
Inline_Stack_Reset;
Inline_Stack_Push(sv_2mortal(newSViv(x + y)));
Inline_Stack_Push(sv_2mortal(newSViv(x - y)));
Inline_Stack_Done;
Inline_Stack_Return(2);
}
Again run create.pl
This time INLINE.h is needed and you should find it, along with Mod.xs, in test/My-Mod-0.01.
But, of course, to build the My::Mod module, you'll also need a Makefile.PL and a Mod.pm. So let's autogenerate them, too. It's just a matter of providing an extra argument to c2xs(). Amend create.pl to:
use warnings;
use strict;
use InlineX::C2XS qw(c2xs);
my $mod = 'My::Mod';
my $pkg = $mod;
my $build_dir = './My-Mod-0.01';
my $hashref = {VERSION =>0.01,
WRITE_MAKEFILE_PL => 1,
WRITE_PM => 1};
c2xs($mod, $pkg, $build_dir, $hashref);
Now when you run create.pl you'll find that Makefile.PL and Mod.pm are also created in test/My-Mod-0.01. (Mod.pm doesn't export any functions - you need to hand edit the @EXPORT and/or @EXPORT_OK entries in that file if you want to export any of the functions. Also there's no pod documentation - and the Makefile.PL contains some machine-specific specifications that should be cleaned up before sending the files off to CPAN. But that's all fairly basic and straightforward stuff.)
You may also want to provide a test script, a README, a MANIFEST and whatever other files you like. These additional files cannot be autogenerated by InlineX. For a test script that works with the autogenerated Mod.pm, just create a file named 'test.t' in test/My-Mod-0.01/t (you'll need to manually create that 't' directory) that looks like this:
use warnings;
use strict;
use My::Mod;
print "1..1\n";
my $x = 16;
my $y = 12;
my @z = My::Mod::plus_minus($x, $y);
my $ok = '';
if(My::Mod::plus($x, $y) == 28) {$ok .= 'a'}
if(My::Mod::minus($x, $y) == 4) {$ok .= 'b'}
if($z[0] == 28 && $z[1] == 4) {$ok .= 'c'}
if($ok eq 'abc') {print "ok 1\n"}
else {print "not ok 1 $ok\n"}
Now you can build My::Mod in the usual way (in the test/My-Mod-0.01 folder) by running:
perl Makefile.PL
make test
You could even install it by running 'make install' if you want.
There are other examples to be found in the demos folder of the InlineX-C2XS and InlineX-CPP2XS source distributions from CPAN.
Some other build options are given below. (For a complete list, see the InlineX-C2XS/InlineX-CPP2xs documentation.) In each case it's just a matter of adding a key/value pair to $hashref in create.pl. No other changes to create.pl are necessary.
**********************************************************************

You may need to include additional headers into the XS file. Do so by inserting the following key/value assignment into create.pl's $hashref:
AUTO_INCLUDE => "#include <x.h>\n#include \"y.h\"",
Also, if the AUTOWRAP feature needs to parse and use these headers, then AUTO_INCLUDE is the way to satisfy that requirement.
**********************************************************************

You'll note that during the running of create.pl there's some progress reports being generated by Inline::C. If you don't want to see those reports, insert the following key/value assignment into create.pl's $hashfef:
BUILD_NOISY => 0,
**********************************************************************

By default Parse::RecDescent is used to parse the C code. Inline comes with another C parser called 'ParseRegExp'. It's much faster than Parse::RecDescent. To use Parse::RegExp:
USING => ['ParseRegExp'],
**********************************************************************

Let's do another demo here, based upon the "Object Oriented Inline" example in the (excellent) Inline::C-Cookbook - to demonstrate the use of typemaps as much as anything else. Back in the 'test' folder, create another folder named '/My-Soldier-1.02' ... so we've now got folders named test/src, test/My-Mod-0.01 and test/My-Soldier-1.02.
Create a file named 'Soldier.c' as follows (and place it in test/src):
typedef struct {
char* name;
char* rank;
long serial;
} Soldier;
Soldier * new(char* class, char* name, char* rank, long serial) {
Soldier* soldier;
New(42, soldier, 1, Soldier);
soldier->name = savepv(name);
soldier->rank = savepv(rank);
soldier->serial = serial;
return soldier;
}
char* get_name(Soldier * obj) {
return obj->name;
}
char* get_rank(Soldier * obj) {
return obj->rank;
}
long get_serial(Soldier * obj) {
return obj->serial;
}
void DESTROY(Soldier* obj) {
Safefree(obj->name);
Safefree(obj->rank);
Safefree(obj);
}
Note that new() returns a Soldier*. Perl doesn't know anything about the Soldier* type ... so we need to provide a typemap with an 'OUTPUT' section that tells perl how to deal with this type. Note also that the other functions take (as their argument) a Soldier*. Again, since perl doesn't know anything about the Soldier* type, we need to provide a typemap with an 'INPUT' section that tells perl what to do. Here's one such typemap that satisfies both requirements:
Soldier * SOLDIER
INPUT
SOLDIER
$var = INT2PTR($type, SvIV(SvRV($arg)))
OUTPUT
SOLDIER
$arg = newSViv(0);
sv_setiv(newSVrv($arg, \"Soldier\"), (IV)$var);
SvREADONLY_on(SvRV($arg));
$arg;
Save that file as test/My-Soldier-1.02/typemap and place a copy of it in 'test'. Note that, in addition to our typemap being needed when we compile the My::Soldier module, the c2xs() function also needs it in order to write a correct XS file. That's why we've placed a copy of the typemap in both 'test' and 'test/My-Soldier-1.02' - so that both processes will be able to find it. A better solution is to have the TYPEMAPS entry in $hashref provide a fully qualified (absolute) path to the file, rather than a relative path. (Unfortunately, I don't know what that absolute path will be on your machine.)
I guess we'll also need a test file to test our new Soldier module. So save the following as test/My-Soldier-1.02/t/test.t:
use warnings;
use strict;
use My::Soldier;
print "1..1'\n";
my $obj1 = My::Soldier->new('Benjamin', 'Private', 11111);
my $obj2 = My::Soldier->new('Sanders', 'Colonel', 22222);
my $obj3 = My::Soldier->new('Matt', 'Sergeant', 33333);
my $ok = '';
if($obj1->My::Soldier::get_name() eq 'Benjamin') {$ok .= 'a'}
if($obj2->My::Soldier::get_name() eq 'Sanders') {$ok .= 'b'}
if($obj3->My::Soldier::get_name() eq 'Matt') {$ok .= 'c'}
if($obj1->My::Soldier::get_rank() eq 'Private') {$ok .= 'd'}
if($obj2->My::Soldier::get_rank() eq 'Colonel') {$ok .= 'e'}
if($obj3->My::Soldier::get_rank() eq 'Sergeant') {$ok .= 'f'}
if($obj1->My::Soldier::get_serial() == 11111) {$ok .= 'g'}
if($obj2->My::Soldier::get_serial() == 22222) {$ok .= 'h'}
if($obj3->My::Soldier::get_serial() == 33333) {$ok .= 'i'}
if($ok eq 'abcdefghi') {print "ok 1\n"}
else {print "not ok 1 $ok\n"}
Now we just need to rewrite test/create.pl appropriately:
use warnings;
use strict;
use InlineX::C2XS qw(c2xs);
my $mod = 'My::Soldier';
my $pkg = $mod;
my $build_dir = './My-Soldier-1.02';
my $hashref = {VERSION =>1.02,
WRITE_MAKEFILE_PL => 1,
TYPEMAPS => ['./typemap'],
WRITE_PM => 1};
c2xs($mod, $pkg, $build_dir, $hashref);
So now ... it's just a matter of running create.pl - then 'cd' to test/My-Soldier-1.02 and run:
perl Makefile.PL make test
(and 'make install' if you actually want to install this module.)
**********************************************************************

Returning to the first example code we used at the beginning of this cookbook, with version 0.13 or later, we don't need to have the source in ./src/Mod.c. (We can still do it that way, but we don't *have* to.)
We could, for example, put the code that was written into './src/Mod.c' into '/home/me/file.ext' instead. Then, in order to create Mod.xs, Mod.pm, and Makefile.PL in './My-Mod-0.01', we need 'create.pl' to look like this:
use warnings;
use strict;
use InlineX::C2XS qw(c2xs);
my $mod = 'My::Mod';
my $pkg = $mod;
my $build_dir = './My-Mod-0.01';
my $hashref = {VERSION =>0.01,
WRITE_MAKEFILE_PL => 1,
WRITE_PM => 1,
SRC_LOCATION => '/home/me/file.ext'};
c2xs($mod, $pkg, $build_dir, $hashref);
Another alternative is to put the code in a variable - in which case 'create.pl' is as follows:
use warnings;
use strict;
use InlineX::C2XS qw(c2xs);
my $mod = 'My::Mod';
my $pkg = $mod;
my $build_dir = './My-Mod-0.01';
my $code = "
int plus (int x, int y) {
return x + y;
}
int minus (int x, int y) {
return x - y;
}
void plus_minus(int x, int y) {
Inline_Stack_Vars;
Inline_Stack_Reset;
Inline_Stack_Push(sv_2mortal(newSViv(x + y)));
Inline_Stack_Push(sv_2mortal(newSViv(x - y)));
Inline_Stack_Done;
Inline_Stack_Return(2);
}
";
my $hashref = {VERSION =>0.01,
WRITE_MAKEFILE_PL => 1,
WRITE_PM => 1,
CODE => $code};
c2xs($mod, $pkg, $build_dir, $hashref);
**********************************************************************