Changes between Initial Version and Version 1 of DevelopersPerlExtending


Ignore:
Timestamp:
Nov 15, 2006, 10:36:33 AM (11 years ago)
Author:
dkearney
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • DevelopersPerlExtending

    v1 v1  
     1Building a Perl Module
     2
     3Example.pm
     4{{{
     5    # example taken from Extending and Embedding Perl book
     6    package Example;
     7
     8    use 5.006;
     9    use strict;
     10    use warnings;
     11
     12    use base qw(Exporter);
     13
     14    our $VERSION = '0.01';
     15
     16    our @EXPORT_OK = qw(myfunc);
     17
     18    sub myfunc {
     19        my $arg = shift;
     20        return $arg;
     21    }
     22
     23    1;
     24
     25}}}
     26
     27This is a simple example of a perl module.
     28You tell perl what the package name is, Example.
     29As always, use strict, pull the Exporter package from the base module.
     30We use @EXPORT_OK so that the myfunc does not always get exported
     31to the users namespace. @EXPORT_OK contains all functions that can be
     32exported and have to be explicitly requested by the user. We do not
     33use the @EXPORT array because that always exports the function, even
     34even when you dont ask for it.
     35
     36<need to show a code example of difference>
     37
     38Creating the correct directory structure of a module is made quick
     39and easy with the h2xs script. h2xs's purpose is to translate a c/c++
     40header file to the correct xs file. To create a perl module, we might
     41the command
     42
     43{{{
     44    h2xs -X Example
     45}}}
     46
     47This would build the correct directory structure for a module named
     48Example. Because we used the -X flag, the Example.xs file would not
     49be provided. This is ok for modules implemented in perl. The xs file
     50acts as glue for creating bindings from perl back to c/c++ code.
     51
     52<need to explain the module directory structure>
     53
     54We can test our new module by pointing perl's library search path to
     55the directory where our module lives and running a test command.
     56
     57{{{
     58    export PERL5LIB=/home/derrick/playground/perl/extending/Example/lib
     59    perl -MExample -e 'print Example::myfunc( "hi derrick\n" )'
     60}}}
     61
     62That is a very simple way to create a module, lets now place our .pm
     63file into a module directory suitable for distribution.
     64
     65{{{
     66    cp Example.pm Example/lib/.
     67    export PERL5LIB=""
     68    cd Example
     69}}}
     70
     71We use perl's Makefile.PL and !MakeMaker module to generate a makefile
     72for our Example module.
     73
     74{{{
     75    perl Makefile.PL
     76    make
     77}}}
     78
     79The above commands build (or really copy in this case) the module.
     80With the following command, we can test the module.
     81
     82{{{
     83    perl -Mblib -MExample -e 'print Example::myfunc( "hi derrick\n" )'
     84}}}
     85
     86Simple C Function to Perl Example
     87
     88Next we'll explore a perl module that calls a c function.
     89We start by using h2xs to create a new module directory.
     90
     91{{{
     92    h2xs -A -n CFuncToPerl
     93}}}
     94
     95The -A flag means "Omit all autoload facilities"
     96The -n flag is used to specify the name of the extension.
     97I do not know why we use -A, but many people do it in their examples.
     98We use -n because we did not specify an actual C header file.
     99
     100Introducing the Lingo
     101Terms to know:
     102    XS      - eXternal Subroutines, wrappers that allow you to access C code [[BR]]
     103    XSUB    - individual wrapper / subroutine within an XS file [[BR]]
     104    xsubpp  - XS compiler [[BR]]
     105
     106What is the XS file?
     107If we take a look into our newly created module directory, we will see a
     108file named CFuncToPerl.xs. This is the XS file. It holds code that resembles
     109a C-like syntax and is eventually processed into C source files by the
     110xsubpp processor. The C file generated by xsubpp is then compiled into
     111a library that you link into perl as a module.
     112
     113Lets start by editing CFuncToPerl.xs to add a C function called
     114void print_hello(). The function is simple, it prints out the word "hello".
     115Here is the code for the function:
     116
     117{{{
     118    void print_hello(void)
     119    {
     120        printf("hello\n");
     121    }
     122}}}
     123
     124We open up the file CFuncToPerl.xs and see the basics of an xs file:
     125
     126{{{
     127    #include "EXTERN.h"
     128    #include "perl.h"
     129    #include "XSUB.h"
     130
     131    #include "ppport.h"
     132
     133    MODULE = CFuncToPerl    PACKAGE = CFuncToPerl
     134}}}
     135
     136You can see the #include statements for header files, and then there is the
     137MODULE and PACKAGE tags. This line establishes the name of the module.
     138All code above this line is considered C code, and everything below is
     139considered XS code. Adding our function to the xs file, the new xs file
     140looks as follows:
     141
     142{{{
     143    #include "EXTERN.h"
     144    #include "perl.h"
     145    #include "XSUB.h"
     146
     147    #include "ppport.h"
     148
     149    void print_hello(void)
     150    {
     151        printf("hello\n");
     152    }
     153
     154    MODULE = CFuncToPerl    PACKAGE = CFuncToPerl
     155
     156    void
     157    print_hello();
     158}}}
     159
     160You'll notice we not only added our function, but also added two lines of
     161code under the MODULE line. With those two lines we are telling the xsubpp
     162that we within the CFuncToPerl package, there is a function called
     163print_hello. The print_hello function prototype is print_hello(). It takes
     164in no parameters and returns no data.
     165
     166We can quickly compile this example using:
     167
     168{{{
     169    perl Makefile.PL
     170    make
     171}}}
     172
     173and test it using
     174
     175{{{
     176    perl -Mblib -MCFuncToPerl -e "CFuncToPerl::print_hello()"
     177}}}
     178
     179Perl's xsubpp recognizes many of the simple C data types like void, int,
     180and double. Lets try adding an example with some doubles.
     181
     182Our new function is:
     183
     184{{{
     185    double subtract (double a, double b)
     186    {
     187        return (b-a);
     188    }
     189}}}
     190
     191We add the xsub definition as follows:
     192{{{
     193    double
     194    subtract (a,b)
     195        double a
     196        double b
     197}}}
     198
     199Here, as described in the xsub, the perl function invocation would look
     200like this:
     201{{{
     202    my ($result) = subtract(a,b)
     203}}}
     204
     205This basically says subtract is looking to take in two double values,
     206and return a double value represented by $result
     207
     208Now take a look at another way of writing a function. This add function
     209takes in two double values and a pointer to the result. The function returns
     210an integer signaling an error has occured in the function.
     211
     212{{{
     213    int add(double a, double b, double* result)
     214    {
     215        int err = 0;
     216        if (result != NULL) {
     217            *result = a+b;
     218        }
     219        else {
     220            err++;
     221        }
     222
     223        return err;
     224    }
     225}}}
     226
     227The xsub that would allow us to access this function utilizes the IN and
     228OUTLIST keywords. By default, all unspecified paramters are considered IN.
     229
     230{{{
     231    int
     232    add (IN double a, IN double b, OUTLIST double result)
     233}}}
     234
     235or
     236
     237{{{
     238    int
     239    add (a, b, OUTLIST double result)
     240        double a
     241        double b
     242}}}
     243
     244The OUTLIST keyword tells XS that the C function will write results to the
     245address pointed to by the parameter. XS also orgranizes the Perl function
     246prototype to return the return value as well as the parameters listed with
     247the OUTLIST keyword. So the Perl function prototype for the add function
     248would be:
     249
     250{{{
     251    my ($result) = add($a,$b);
     252}}}
     253
     254If you wanted to return the integer return value of the function, you
     255would describe the XSUB as follows:
     256
     257{{{
     258    int
     259    add_again(a, b, OUTLIST double result)
     260        double a
     261        double b
     262      CODE:
     263        RETVAL = add(a,b,&result);
     264      OUTPUT:
     265        RETVAL
     266}}}
     267
     268The above code produces the following perl function prototype:
     269
     270{{{
     271    my ($status,$result) = multiply($a,$b)
     272}}}
     273
     274Note the CODE:, OUTPUT:, and RETVAL: keywords that are used in the
     275example above. The CODE: keyword is used to provide more sophisticated
     276handling of calls to C functions within an XSUB. The OUTPUT: keyword is used
     277tell perl which values of the XSUB should be refreshed and returned to the
     278user as a result of the function. The RETVAL: keyword is automatically
     279assigned to the return value of the C function in the XSUB. One catch is
     280with the use of the CODE: keyword. When the CODE: keyword is used, RETVAL is
     281no longer automatically returned to the user. This is why we use the OUTPUT:
     282keyword to tell the XSUB to refresh the value of RETVAL and return it to the
     283user. In the example above we also used the OUTLIST keyword so in total, we
     284are returning two values: double* result from the C function and the
     285integer return value of the C function.
     286
     287Next we will tackle calling functions from C Library inside of Perl.
     288We draw up the C library, lets call it Poo
     289
     290poo.h
     291{{{
     292    #include <stdio.h>
     293    #include <stdlib.h>
     294    #include <string.h>
     295
     296    void print_hello (void);
     297    int add (double a, double b, double* result);
     298    double subtract (double a, double b);
     299
     300}}}
     301
     302poo.c
     303{{{
     304    #include "poo.h"
     305
     306    void print_hello (void)
     307    {
     308        printf("hello\n");
     309    }
     310
     311    int add (double a, double b, double* result)
     312    {
     313        int err = 0;
     314        if (result != NULL) {
     315            *result = a+b;
     316        }
     317        else {
     318            err++;
     319        }
     320
     321        return err;
     322    }
     323
     324    double subtract (double a, double b)
     325    {
     326        return (b-a);
     327    }
     328}}}
     329
     330Makefile
     331{{{
     332    CC              = gcc
     333    DEBUG           = -g -Wall
     334    LIB_POO         = -Wl,-rpath,./ -L./ -lpoo
     335    CFLAGS          = -fPIC
     336
     337    LDLIB_MACOSX = -dynamiclib -o $@.dylib
     338    LDLIB_LINUX = -shared -Wl,-rpath,./ -Wl,-soname,$@.so -o $@.so.0.0
     339
     340    libpoo: poo.o
     341        if test "`uname`" == "Darwin"; then \
     342            $(CXX) $(DEGUG) $(LDLIB_MACOSX) $^; \
     343            ar -r $@.a $^; \
     344            ranlib -s $@.a; \
     345        else \
     346            $(CXX) $(DEGUG) $(LDLIB_LINUX) $^; \
     347            /sbin/ldconfig -n ./; \
     348            ar -r $@.a $^; \
     349            ranlib $@.a; \
     350        fi
     351
     352    poo.o: poo.c
     353        $(CC) $(CFLAGS) $(DEBUG) -o $@ -c $?
     354
     355    poo: poo_main.c libpoo
     356        $(CC) $(DEBUG) $(LIB_POO) -o $@ $<
     357
     358    clean:
     359        - rm -f *.o poo libpoo
     360}}}
     361
     362Create a new, clean module.
     363
     364{{{
     365    h2xs -A -n CLibToPerl
     366}}}
     367
     368Add our XSUB's to the CLibToPerl.xs file. They are the same as those
     369we wrote in CFuncToPerl.xs:
     370
     371CLibToPerl.xs:
     372{{{
     373    #include "EXTERN.h"
     374    #include "perl.h"
     375    #include "XSUB.h"
     376
     377    #include "ppport.h"
     378
     379
     380    MODULE = CLibToPerl    PACKAGE = CLibToPerl
     381
     382    void
     383    print_hello();
     384
     385    int
     386    add (a, b, OUTLIST double result)
     387        double a
     388        double b
     389
     390    int
     391    add_again(a, b, OUTLIST double result)
     392        double a
     393        double b
     394      CODE:
     395        RETVAL = add(a,b,&result);
     396      OUTPUT:
     397        RETVAL
     398
     399    double
     400    subtract (a,b)
     401        double a
     402        double b
     403}}}
     404
     405Adjust Makefile.PL to tell perl's xsubpp where to find header files and
     406libraries it needs to compile against C library libpoo
     407
     408Makefile.PL:
     409{{{
     410    use 5.008008;
     411    use ExtUtils::MakeMaker;
     412    # See lib/ExtUtils/MakeMaker.pm for details of how to influence
     413    # the contents of the Makefile that is written.
     414    WriteMakefile(
     415        NAME              => 'CLibToPerl',
     416        VERSION_FROM      => 'lib/CLibToPerl.pm', # finds $VERSION
     417        PREREQ_PM         => {}, # e.g., Module::Name => 1.1
     418        ($] >= 5.005 ?     ## Add these new keywords supported since 5.005
     419          (ABSTRACT_FROM  => 'lib/CLibToPerl.pm', # retrieve abstract from module
     420           AUTHOR         => 'derrick <derrick@>') : ()),
     421        LIBS              => ['-L/home/derrick/playground/perl/extending/Poo -lpoo'], # e.g., '-lm'
     422        DEFINE            => '', # e.g., '-DHAVE_SOMETHING'
     423        INC               => '-I. -I/home/derrick/playground/perl/extending/Poo', # e.g., '-I. -I/usr/include/other'
     424    # Un-comment this if you add C files to link with later:
     425        # OBJECT            => '$(O_FILES)', # link all the C files too
     426    );
     427
     428}}}
     429
     430
     431Building the module is no different than previous times.
     432
     433{{{
     434    perl Makefile.PL
     435    make
     436}}}
     437
     438If everything builds successfully, we can try adding tests to the file
     439t/CLibToPerl.t
     440
     441t/CLibToPerl.t:
     442{{{
     443    # Before `make install' is performed this script should be runnable with
     444    # `make test'. After `make install' it should work as `perl CLibToPerl.t'
     445
     446    #########################
     447
     448    # change 'tests => 1' to 'tests => last_test_to_print';
     449
     450    use Test::More tests => 5;
     451    BEGIN { use_ok('CLibToPerl') };
     452
     453    #########################
     454
     455    # Insert your test code below, the Test::More module is use()ed here so read
     456    # its man page ( perldoc Test::More ) for help writing this test script.
     457
     458
     459    ok(CLibToPerl::add(1,2) == 3, "Adding 1 and 2");
     460
     461    ok(CLibToPerl::subtract(1,3) == 2, "Subtract 1 from 3");
     462
     463    my ($status, $result) = CLibToPerl::add_again(2,3);
     464    ok((($status == 0) && ($result == 5)), "Adding 2 and 3 and returning status");
     465    ok((CLibToPerl::add_again(2,3) == 5), "Adding 2 and 3 and ignoring status");
     466}}}
     467
     468And we run the tests and hope for success!
     469
     470{{{
     471    perl -Mblib -MCLibToPerl t/CLibToPerl.t
     472}}}