perlxstut.pod
来自「MSYS在windows下模拟了一个类unix的终端」· POD 代码 · 共 1,264 行 · 第 1/4 页
POD
1,264 行
subroutines should be placed into the calling package's namespace. Becauseyou don't know if the user has already used your variable and subroutinenames, it's vitally important to carefully select what to export. Do I<not>export method or variable names I<by default> without a good reason.As a general rule, if the module is trying to be object-oriented then don'texport anything. If it's just a collection of functions and variables, thenyou can export them via another array, called C<@EXPORT_OK>. This arraydoes not automatically place its subroutine and variable names into thenamespace unless the user specifically requests that this be done.See L<perlmod> for more information.The C<$VERSION> variable is used to ensure that the .pm file and the sharedlibrary are "in sync" with each other. Any time you make changes tothe .pm or .xs files, you should increment the value of this variable.=head2 Writing good test scriptsThe importance of writing good test scripts cannot be overemphasized. Youshould closely follow the "ok/not ok" style that Perl itself uses, so thatit is very easy and unambiguous to determine the outcome of each test case.When you find and fix a bug, make sure you add a test case for it.By running "C<make test>", you ensure that your test.pl script runs and usesthe correct version of your extension. If you have many test cases, youmight want to copy Perl's test style. Create a directory named "t" in theextension's directory and append the suffix ".t" to the names of your testfiles. When you run "C<make test>", all of these test files will be executed.=head2 EXAMPLE 3Our third extension will take one argument as its input, round off thatvalue, and set the I<argument> to the rounded value.Add the following to the end of Mytest.xs: void round(arg) double arg CODE: if (arg > 0.0) { arg = floor(arg + 0.5); } else if (arg < 0.0) { arg = ceil(arg - 0.5); } else { arg = 0.0; } OUTPUT: argEdit the Makefile.PL file so that the corresponding line looks like this: 'LIBS' => ['-lm'], # e.g., '-lm'Generate the Makefile and run make. Change the BEGIN block to print"1..9" and add the following to test.pl: $i = -1.5; &Mytest::round($i); print $i == -2.0 ? "ok 5" : "not ok 5", "\n"; $i = -1.1; &Mytest::round($i); print $i == -1.0 ? "ok 6" : "not ok 6", "\n"; $i = 0.0; &Mytest::round($i); print $i == 0.0 ? "ok 7" : "not ok 7", "\n"; $i = 0.5; &Mytest::round($i); print $i == 1.0 ? "ok 8" : "not ok 8", "\n"; $i = 1.2; &Mytest::round($i); print $i == 1.0 ? "ok 9" : "not ok 9", "\n";Running "C<make test>" should now print out that all nine tests are okay.Notice that in these new test cases, the argument passed to round was ascalar variable. You might be wondering if you can round a constant orliteral. To see what happens, temporarily add the following line to test.pl: &Mytest::round(3);Run "C<make test>" and notice that Perl dies with a fatal error. Perl won'tlet you change the value of constants!=head2 What's new here?=over 4=item *We've made some changes to Makefile.PL. In this case, we've specified anextra library to be linked into the extension's shared library, the mathlibrary libm in this case. We'll talk later about how to write XSUBs thatcan call every routine in a library.=item *The value of the function is not being passed back as the function's returnvalue, but by changing the value of the variable that was passed into thefunction. You might have guessed that when you saw that the return valueof round is of type "void".=back=head2 Input and Output ParametersYou specify the parameters that will be passed into the XSUB on the line(s)after you declare the function's return value and name. Each input parameterline starts with optional white space, and may have an optional terminatingsemicolon.The list of output parameters occurs at the very end of the function, justbefore after the OUTPUT: directive. The use of RETVAL tells Perl that youwish to send this value back as the return value of the XSUB function. InExample 3, we wanted the "return value" placed in the original variablewhich we passed in, so we listed it (and not RETVAL) in the OUTPUT: section.=head2 The XSUBPP ProgramThe B<xsubpp> program takes the XS code in the .xs file and translates it intoC code, placing it in a file whose suffix is .c. The C code created makesheavy use of the C functions within Perl.=head2 The TYPEMAP fileThe B<xsubpp> program uses rules to convert from Perl's data types (scalar,array, etc.) to C's data types (int, char, etc.). These rules are storedin the typemap file ($PERLLIB/ExtUtils/typemap). This file is split intothree parts.The first section maps various C data types to a name, which correspondssomewhat with the various Perl types. The second section contains C codewhich B<xsubpp> uses to handle input parameters. The third section containsC code which B<xsubpp> uses to handle output parameters.Let's take a look at a portion of the .c file created for our extension.The file name is Mytest.c: XS(XS_Mytest_round) { dXSARGS; if (items != 1) croak("Usage: Mytest::round(arg)"); { double arg = (double)SvNV(ST(0)); /* XXXXX */ if (arg > 0.0) { arg = floor(arg + 0.5); } else if (arg < 0.0) { arg = ceil(arg - 0.5); } else { arg = 0.0; } sv_setnv(ST(0), (double)arg); /* XXXXX */ } XSRETURN(1); }Notice the two lines commented with "XXXXX". If you check the first sectionof the typemap file, you'll see that doubles are of type T_DOUBLE. In theINPUT section, an argument that is T_DOUBLE is assigned to the variablearg by calling the routine SvNV on something, then casting it to double,then assigned to the variable arg. Similarly, in the OUTPUT section,once arg has its final value, it is passed to the sv_setnv function tobe passed back to the calling subroutine. These two functions are explainedin L<perlguts>; we'll talk more later about what that "ST(0)" means in thesection on the argument stack.=head2 Warning about Output ArgumentsIn general, it's not a good idea to write extensions that modify their inputparameters, as in Example 3. Instead, you should probably return multiplevalues in an array and let the caller handle them (we'll do this in a laterexample). However, in order to better accommodate calling pre-existing Croutines, which often do modify their input parameters, this behavior istolerated.=head2 EXAMPLE 4In this example, we'll now begin to write XSUBs that will interact withpre-defined C libraries. To begin with, we will build a small library ofour own, then let h2xs write our .pm and .xs files for us.Create a new directory called Mytest2 at the same level as the directoryMytest. In the Mytest2 directory, create another directory called mylib,and cd into that directory.Here we'll create some files that will generate a test library. These willinclude a C source file and a header file. We'll also create a Makefile.PLin this directory. Then we'll make sure that running make at the Mytest2level will automatically run this Makefile.PL file and the resulting Makefile.In the mylib directory, create a file mylib.h that looks like this: #define TESTVAL 4 extern double foo(int, long, const char*);Also create a file mylib.c that looks like this: #include <stdlib.h> #include "./mylib.h" double foo(int a, long b, const char *c) { return (a + b + atof(c) + TESTVAL); }And finally create a file Makefile.PL that looks like this: use ExtUtils::MakeMaker; $Verbose = 1; WriteMakefile( NAME => 'Mytest2::mylib', SKIP => [qw(all static static_lib dynamic dynamic_lib)], clean => {'FILES' => 'libmylib$(LIBEEXT)'}, ); sub MY::top_targets { ' all :: static pure_all :: static static :: libmylib$(LIB_EXT) libmylib$(LIB_EXT): $(O_FILES) $(AR) cr libmylib$(LIB_EXT) $(O_FILES) $(RANLIB) libmylib$(LIB_EXT) '; }Make sure you use a tab and not spaces on the lines beginning with "$(AR)"and "$(RANLIB)". Make will not function properly if you use spaces.It has also been reported that the "cr" argument to $(AR) is unnecessaryon Win32 systems.We will now create the main top-level Mytest2 files. Change to the directoryabove Mytest2 and run the following command: % h2xs -O -n Mytest2 ./Mytest2/mylib/mylib.hThis will print out a warning about overwriting Mytest2, but that's okay.Our files are stored in Mytest2/mylib, and will be untouched.The normal Makefile.PL that h2xs generates doesn't know about the mylibdirectory. We need to tell it that there is a subdirectory and that wewill be generating a library in it. Let's add the argument MYEXTLIB tothe WriteMakefile call so that it looks like this: WriteMakefile( 'NAME' => 'Mytest2', 'VERSION_FROM' => 'Mytest2.pm', # finds $VERSION 'LIBS' => [''], # e.g., '-lm' 'DEFINE' => '', # e.g., '-DHAVE_SOMETHING' 'INC' => '', # e.g., '-I/usr/include/other' 'MYEXTLIB' => 'mylib/libmylib$(LIB_EXT)', );and then at the end add a subroutine (which will override the pre-existingsubroutine). Remember to use a tab character to indent the line beginningwith "cd"! sub MY::postamble { ' $(MYEXTLIB): mylib/Makefile cd mylib && $(MAKE) $(PASSTHRU) '; }Let's also fix the MANIFEST file so that it accurately reflects the contentsof our extension. The single line that says "mylib" should be replaced bythe following three lines: mylib/Makefile.PL mylib/mylib.c mylib/mylib.hTo keep our namespace nice and unpolluted, edit the .pm file and changethe variable C<@EXPORT> to C<@EXPORT_OK>. Finally, in the.xs file, edit the #include line to read: #include "mylib/mylib.h"And also add the following function definition to the end of the .xs file: double foo(a,b,c) int a long b const char * c OUTPUT: RETVALNow we also need to create a typemap file because the default Perl doesn'tcurrently support the const char * type. Create a file called typemap inthe Mytest2 directory and place the following in it: const char * T_PVNow run perl on the top-level Makefile.PL. Notice that it also created aMakefile in the mylib directory. Run make and watch that it does cd intothe mylib directory and run make in there as well.Now edit the test.pl script and change the BEGIN block to print "1..4",and add the following lines to the end of the script: print &Mytest2::foo(1, 2, "Hello, world!") == 7 ? "ok 2\n" : "not ok 2\n"; print &Mytest2::foo(1, 2, "0.0") == 7 ? "ok 3\n" : "not ok 3\n"; print abs(&Mytest2::foo(0, 0, "-3.4") - 0.6) <= 0.01 ? "ok 4\n" : "not ok 4\n";(When dealing with floating-point comparisons, it is best to not check forequality, but rather that the difference between the expected and actualresult is below a certain amount (called epsilon) which is 0.01 in this case)Run "C<make test>" and all should be well.=head2 What has happened here?Unlike previous examples, we've now run h2xs on a real include file. Thishas caused some extra goodies to appear in both the .pm and .xs files.=over 4
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?