Sisyphus > InlineX-C2XS-0.14 > InlineX

Download:
InlineX-C2XS-0.14.tar.gz

Annotate this POD

CPAN RT

Open  0
Report a bug
Source  

NAME ^

InlineX Cookbook

BASIC EXAMPLE ^

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.

**********************************************************************

AUTO_INCLUDE ^

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.

**********************************************************************

BUILD_NOISY ^

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,

**********************************************************************

USING ^

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'],

**********************************************************************

TYPEMAPS ^

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.)

**********************************************************************

UPDATE for 0.13 and later ^

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);

**********************************************************************