Changes between Initial Version and Version 1 of DevelopersPerlExtending


Ignore:
Timestamp:
11/15/06 10:36:33 (8 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}}}