PP.pod (PDL-2.082) | : | PP.pod (PDL-2.083) | ||
---|---|---|---|---|
skipping to change at line 74 | skipping to change at line 74 | |||
C<pp_addhdr> so far will be included. Therefore, if you add C functions, | C<pp_addhdr> so far will be included. Therefore, if you add C functions, | |||
make sure to make them C<static> to avoid clashes with later F<.c> files. | make sure to make them C<static> to avoid clashes with later F<.c> files. | |||
But a better practice is make them be separate C files, with any necessary | But a better practice is make them be separate C files, with any necessary | |||
F<.h> to be included by them and the F<.pd> file. You can then add them | F<.h> to be included by them and the F<.pd> file. You can then add them | |||
to your F<Makefile.PL> (note this is the C<_int> version, see separate | to your F<Makefile.PL> (note this is the C<_int> version, see separate | |||
notes on how to "opt-in" for your own modules): | notes on how to "opt-in" for your own modules): | |||
my @pack = (["pnm.pd", qw(Pnm PDL::IO::Pnm)]); | my @pack = (["pnm.pd", qw(Pnm PDL::IO::Pnm)]); | |||
my %hash = pdlpp_stdargs_int(@pack); | my %hash = pdlpp_stdargs_int(@pack); | |||
$hash{OBJECT} .= ' get$(OBJ_EXT)'; | $hash{OBJECT} .= ' get$(OBJ_EXT)'; | |||
sub MY::postamble { pdlpp_postamble_int(@pack); }; | sub MY::postamble { pdlpp_postamble_int(@pack); } | |||
WriteMakefile(%hash); | WriteMakefile(%hash); | |||
=head2 pp_addpm | =head2 pp_addpm | |||
=for ref | =for ref | |||
Add code to the generated .pm file | Add code to the generated .pm file | |||
=head2 pp_addxs | =head2 pp_addxs | |||
skipping to change at line 109 | skipping to change at line 109 | |||
=for example | =for example | |||
pp_add_macros(SUCC => sub { "($_[0] + 1)" }); | pp_add_macros(SUCC => sub { "($_[0] + 1)" }); | |||
# ... | # ... | |||
Code => '$a() = $SUCC($b());', | Code => '$a() = $SUCC($b());', | |||
=head2 pp_add_typemaps | =head2 pp_add_typemaps | |||
=for ref | =for ref | |||
Available from 2.082. Add an XS typemap for use as C<OtherPars>. Takes | Available from 2.082. Add an XS typemap for use as C<OtherPars> or from | |||
manually-added XS. Takes | ||||
one named argument, either C<typemap> (an L<ExtUtils::Typemaps> object), | one named argument, either C<typemap> (an L<ExtUtils::Typemaps> object), | |||
C<string>, or C<file>. | C<string>, or C<file>. | |||
=for example | =for example | |||
pp_add_typemaps(string=><<'EOT'); | pp_add_typemaps(string=><<'EOT'); | |||
TYPEMAP: <<END_OF_TYPEMAP | TYPEMAP: <<END_OF_TYPEMAP | |||
TYPEMAP | TYPEMAP | |||
NV_ADD1 T_NV_ADD1 | NV_ADD1 T_NV_ADD1 | |||
skipping to change at line 144 | skipping to change at line 145 | |||
=for ref | =for ref | |||
Add BEGIN-block wrapping to code for the generated .pm file | Add BEGIN-block wrapping to code for the generated .pm file | |||
=head2 pp_bless | =head2 pp_bless | |||
=for ref | =for ref | |||
Sets the package to which the XS code is added (default is PDL) | Sets the package to which the XS code is added (default is PDL) | |||
=head2 pp_boundscheck | ||||
=for ref | ||||
Control state of PDL bounds checking activity | ||||
=head2 pp_core_importList | =head2 pp_core_importList | |||
=for ref | =for ref | |||
Specify what is imported from PDL::Core | Specify what is imported from PDL::Core | |||
=head2 pp_def | =head2 pp_def | |||
=for ref | =for ref | |||
skipping to change at line 1348 | skipping to change at line 1343 | |||
$CROAK("Diagonal: must have at least 1 dimension"); | $CROAK("Diagonal: must have at least 1 dimension"); | |||
$DOCOMPALLOC(); /* malloc()s the whichdims */ | $DOCOMPALLOC(); /* malloc()s the whichdims */ | |||
for(i=0; i<$COMP(whichdims_count); i++) | for(i=0; i<$COMP(whichdims_count); i++) | |||
$COMP(whichdims)[i] = tmp[i]; | $COMP(whichdims)[i] = tmp[i]; | |||
free(tmp); | free(tmp); | |||
/* ... */ | /* ... */ | |||
', | ', | |||
# ... | # ... | |||
); | ); | |||
The C<MakeComp> code is placed in the C<pdl_(funcname)_run>, so access | The C<MakeComp> code is placed in the C<pdl_run_(funcname)>, so access | |||
to C<Pars> (which will just be C<pdl *>s)/C<OtherPars> values is just | to C<Pars> (which will just be C<pdl *>s)/C<OtherPars> values is just | |||
via their names, not a macro. | via their names, not a macro. The default code (which also applies | |||
to C<OtherPars>) makes a copy of values where it knows how to do so, | ||||
including C<SV*> and C<char*>. | ||||
You can also provide a C<CompFreeCodeComp> key, in case your C<MakeComp> | ||||
needs tidying up after it. | ||||
As of 2.058, you can instead give a C99 "incomplete | As of 2.058, you can instead give a C99 "incomplete | |||
array" type parameter as an C<OtherPars> entry: | array" type parameter as an C<OtherPars> entry: | |||
pp_def( | pp_def( | |||
'diagonal', | 'diagonal', | |||
OtherPars => 'PDL_Indx whichdims[]', | OtherPars => 'PDL_Indx whichdims[]', | |||
MakeComp => ' | MakeComp => ' | |||
if ($COMP(whichdims_count) < 1) | if ($COMP(whichdims_count) < 1) | |||
$CROAK("Diagonal: must have at least 1 dimension"); | $CROAK("Diagonal: must have at least 1 dimension"); | |||
/* ... */ | /* ... */ | |||
', | ', | |||
# ... | # ... | |||
); | ); | |||
There is an XS F<typemap> entry (only for C<PDL_Indx> array types for | There is an XS F<typemap> entry (for C<PDL_Indx> and C<pdl*> array types for | |||
now) that adds a C<(varname)_count> variable having extracted the index | now) that relies on a C<(varname)_count> variable being declared in the | |||
XS C<INPUT> section (PP does this for you), to extract the index | ||||
numbers from an array-ref parameter, and sets the count variable to the | numbers from an array-ref parameter, and sets the count variable to the | |||
right value. PP then makes a copy of the data available. The C function | right value. PP then makes a copy of the data available. The C function | |||
(here, C<pdl_diagonal_run>)'s caller (here, the generated XS function) | (here, C<pdl_run_diagonal>)'s caller (here, the generated XS function) | |||
is responsible for freeing the array passed in (here, PDL's C<smalloc> | is responsible for freeing the array passed in (here, PDL's C<smalloc> | |||
function is used, so the user need do nothing different). | function is used, so the user need do nothing different). | |||
=head4 XS-only OtherPars | ||||
As of 2.083, you can prefix the names of C<OtherPars> with C<$>, e.g. | ||||
pp_def('minus', | ||||
OtherPars => 'int $swap', | ||||
# ... | ||||
); | ||||
This will mean they are available in C<HdrCode> and C<FtrCode>, but not | ||||
elsewhere in the generated code (e.g. C<MakeComp>, C<Code>). | ||||
=head2 Other functions in the Code section | =head2 Other functions in the Code section | |||
The only PP function that we have used in the examples so far is C<loop>. | The only PP function that we have used in the examples so far is C<loop>. | |||
Additionally, there are currently two other functions which are recognised | Additionally, there are currently two other functions which are recognised | |||
in the C<Code> section: | in the C<Code> section: | |||
=head3 broadcastloop | =head3 broadcastloop | |||
As we heard above the signature of a PP defined function defines the | As we heard above the signature of a PP defined function defines the | |||
dimensions of all the pdl arguments involved in a I<primitive> operation. | dimensions of all the pdl arguments involved in a I<primitive> operation. | |||
skipping to change at line 1530 | skipping to change at line 1543 | |||
Pars => ' p(m); x(n); [o] y(); [t] work(wn); ', | Pars => ' p(m); x(n); [o] y(); [t] work(wn); ', | |||
RedoDimsCode => '$SIZE(wn) = $SIZE(n) + $SIZE(m) * $SIZE(m);', | RedoDimsCode => '$SIZE(wn) = $SIZE(n) + $SIZE(m) * $SIZE(m);', | |||
Code => ' | Code => ' | |||
externalfunc( $P(p), $P(x), $SIZE(m), $SIZE(n), $P(work) ); | externalfunc( $P(p), $P(x), $SIZE(m), $SIZE(n), $P(work) ); | |||
' | ' | |||
); | ); | |||
As of 2.075, you can use the dimensions of passed-in ndarrays as they | As of 2.075, you can use the dimensions of passed-in ndarrays as they | |||
are available when the C<RedoDimsCode> is run. | are available when the C<RedoDimsCode> is run. | |||
Before the code in the Code section is executed PP | Before the code in the Code section is executed PP | |||
will create the proper storage for C<work> if it does not | will create the proper storage for C<work> (one area per POSIX thread, | |||
exist. Note that you only took the first dimension of C<p> | in case of broadcasting that multi-threads - the user cannot supply this). | |||
Note that you only took the first dimension of C<p> | ||||
and C<x> because the user may have sent ndarrays with extra | and C<x> because the user may have sent ndarrays with extra | |||
broadcasting dimensions. Of course, the temporary ndarray C<work> (note the | broadcasting dimensions. | |||
[t] flag) should not be given any broadcast dimensions anyway. | ||||
You can also use C<RedoDimsCode> to set the dimension of a | You can also use C<RedoDimsCode> to set the dimension of a | |||
ndarray flagged with [o]. In this case you set the dimensions | ndarray flagged with [o]. In this case you set the dimensions | |||
for the named dimension in the signature using $SIZE() as in | for the named dimension in the signature using $SIZE() as in | |||
the preceding example. However, because the ndarray is | the preceding example. However, because the ndarray is | |||
flagged with [o] instead of [t], broadcasting dimensions will | flagged with [o] instead of [t], broadcasting dimensions will | |||
be added if required just as if the size of the dimension | be added if required just as if the size of the dimension | |||
were computed from the signature according to the usual | were computed from the signature according to the usual | |||
rules. Here is an example from PDL::Math | rules. Here is an example from PDL::Math | |||
skipping to change at line 1645 | skipping to change at line 1658 | |||
pp_def('init_meat', | pp_def('init_meat', | |||
Pars => 'double x(n); double y(n);', | Pars => 'double x(n); double y(n);', | |||
OtherPars => 'gsl_spline *spl', | OtherPars => 'gsl_spline *spl', | |||
Code =>'gsl_spline_init,($COMP(spl),$P(x),$P(y),$SIZE(n)));' | Code =>'gsl_spline_init,($COMP(spl),$P(x),$P(y),$SIZE(n)));' | |||
); | ); | |||
where I have removed a macro wrapper call, but that would obscure the | where I have removed a macro wrapper call, but that would obscure the | |||
discussion. | discussion. | |||
You can also have C<OtherPars> entries that are "incomplete arrays" | ||||
of C<pdl*>, both for input and output: | ||||
OtherPars => 'pdl *ins[]', # $COMP(ins_count) will be available | ||||
# OR | ||||
OtherPars => '[o] pdl *outs[]', # update $COMP(outs_count) in your code | ||||
Note that the output F<typemap> entry does a C<free> on the array of | ||||
C<pdl*> pointers, so ensure that you C<malloc> it in your code, without | ||||
leaking. | ||||
=head3 OtherPars as outputs | =head3 OtherPars as outputs | |||
As of 2.081, you can specify an C<OtherPar> as an output. This looks like: | As of 2.081, you can specify an C<OtherPar> as an output. This looks like: | |||
pp_def('output_op', | pp_def('output_op', | |||
Pars => 'in(n=2)', | Pars => 'in(n=2)', | |||
OtherPars => '[o] PDL_Anyval v0; [o] PDL_Anyval v1', | OtherPars => '[o] PDL_Anyval v0; [o] PDL_Anyval v1', | |||
Code => ' | Code => ' | |||
pdl_datatypes dt = $PDL(in)->datatype; | pdl_datatypes dt = $PDL(in)->datatype; | |||
ANYVAL_FROM_CTYPE($COMP(v0), dt, $in(n=>0)); | ANYVAL_FROM_CTYPE($COMP(v0), dt, $in(n=>0)); | |||
ANYVAL_FROM_CTYPE($COMP(v1), dt, $in(n=>1)); | ANYVAL_FROM_CTYPE($COMP(v1), dt, $in(n=>1)); | |||
', | ', | |||
); | ); | |||
The passed-in stack SV will be mutated in place, so this code will then work: | The passed-in stack SV will be mutated in place, so this code will then work: | |||
output_op([5,7], my $v0, my $v1); | output_op([5,7], my $v0, my $v1); | |||
is_deeply [$v0,$v1], [5,7], 'output OtherPars work'; | is_deeply [$v0,$v1], [5,7], 'output OtherPars work'; | |||
($v0, $v1) = output_op([5,7]); # you can omit them, then they get returned | ||||
is_deeply [$v0,$v1], [5,7], 'output OtherPars work 1a'; | ||||
An operation with output C<OtherPars> cannot broadcast, since that would | An operation with output C<OtherPars> cannot broadcast, since that would | |||
cause undefined results. A runtime check is generated that throws an | cause undefined results. A runtime check is generated that throws an | |||
exception if any C<Par> would cause broadcasting. | exception if any C<Par> would cause broadcasting. | |||
Note the syntax for C<OtherPars> has C<[o]> go I<before> the type, while | Note the syntax for C<OtherPars> has C<[o]> go I<before> the type, while | |||
it goes I<after> the type in C<Pars>. It was felt this was the best way | it goes I<after> the type in C<Pars>. It was felt this was the best way | |||
to avoid ambiguity given C types can have C<[]> in them. | to avoid ambiguity given C types can have C<[]> in them. | |||
This relies on the relevant C<OtherPar> having an C<OUTPUT> entry in an | This relies on the relevant C<OtherPar> having an C<OUTPUT> entry in an | |||
XS typemap. | XS typemap. | |||
As of 2.083, it is also possible to specify C<OtherPars> as C<[io]>, | ||||
which means they I<must> be supplied (rather than being optional, like | ||||
an C<[o]> one), but will still be updated after the operation has | ||||
finished. | ||||
=head2 Other useful PP keys in data operation definitions | =head2 Other useful PP keys in data operation definitions | |||
You have already heard about the C<OtherPars> key. Currently, there are not | You have already heard about the C<OtherPars> key. Currently, there are not | |||
many other keys for a data operation that will be useful in normal (whatever | many other keys for a data operation that will be useful in normal (whatever | |||
that is) PP programming. In fact, it would be interesting to hear about | that is) PP programming. In fact, it would be interesting to hear about | |||
a case where you think you need more than what is provided at the moment. | a case where you think you need more than what is provided at the moment. | |||
Please speak up on one of the PDL mailing lists. Most other keys recognised | Please speak up on one of the PDL mailing lists. Most other keys recognised | |||
by C<pp_def> are only really useful for what we call I<slice operations> | by C<pp_def> are only really useful for what we call I<slice operations> | |||
(see also above). | (see also above). | |||
skipping to change at line 1753 | skipping to change at line 1784 | |||
=head3 OtherParsDefaults | =head3 OtherParsDefaults | |||
OtherPars => 'int a; int b', | OtherPars => 'int a; int b', | |||
OtherParsDefaults => { b => 0 }, | OtherParsDefaults => { b => 0 }, | |||
Allows specifying default values for C<OtherPars>. It is an error to | Allows specifying default values for C<OtherPars>. It is an error to | |||
specify a default for one that is before another that does not have | specify a default for one that is before another that does not have | |||
a default. | a default. | |||
=head3 ArgOrder | ||||
Pars => 'x(); y(); [o]z()' | ||||
OtherPars => 'int a; int b', | ||||
ArgOrder => [qw(x y a b z)], | ||||
# or, a non-reference true value to enable flexible arg-handling and | ||||
# move defaultable to the end, followed by output ndarrays then OtherPars | ||||
Pars => 'x(); y(); [o]z()' | ||||
OtherPars => 'int a; int b', | ||||
ArgOrder => 1, | ||||
Allows specifying a different order for providing the operation's | ||||
arguments. This affects only the generated XS (not C C<pdl_run_(name)>) | ||||
parameter list; the internal ordering of C<pdl*> in various C arrays | ||||
is unaffected. | ||||
Providing a non-reference true value enables flexible argument-handling | ||||
and moves defaultable to the end, followed by output ndarrays then | ||||
output C<OtherPars>. Also, all outputs (ndarray and C<OtherPars>) will | ||||
be returned on the stack, even if supplied as arguments. | ||||
It is an error to specify arguments that are not provided, or to give | ||||
a false value, or to have "optional" arguments after mandatory ones. | ||||
=head4 XS argument-handling change | ||||
This also changes PP's XS argument handling; normally you can specify: | ||||
=over | ||||
=item * | ||||
just the input/io arguments | ||||
=item * | ||||
(if the operation has default values provided) those plus values for all argumen | ||||
ts with defaults | ||||
=item * | ||||
all of those plus output arguments, in other words all non-C<[t]> arguments | ||||
=back | ||||
With C<ArgOrder> given, "optional" arguments (outputs and ones with | ||||
defaults) will be filled in from the leftmost missing one. | ||||
=head3 HdrCode | ||||
This is C code that is inserted in the XS function before the call to | ||||
the generated C<pdl_run_(funcname)>. It will have access to all the Pars | ||||
and OtherPars as C values. | ||||
=head3 FtrCode | ||||
As of 2.083. | ||||
This is C code that is inserted in the XS function after the call to | ||||
the generated C<pdl_run_(funcname)>. It will have access to all the Pars | ||||
and OtherPars as C values. | ||||
=head2 Other PDL::PP functions to support concise package definition | =head2 Other PDL::PP functions to support concise package definition | |||
So far, we have described the C<pp_def> and C<pp_done> functions. PDL::PP | So far, we have described the C<pp_def> and C<pp_done> functions. PDL::PP | |||
exports a few other functions to aid you in writing concise PDL extension | exports a few other functions to aid you in writing concise PDL extension | |||
package definitions. | package definitions. | |||
=head3 pp_addhdr | =head3 pp_addhdr | |||
Often when you interface library functions as in the above example | Often when you interface library functions as in the above example | |||
you have to include additional C include files. Since the XS file is | you have to include additional C include files. Since the XS file is | |||
skipping to change at line 1867 | skipping to change at line 1959 | |||
pp_add_isa(' Some::Other::Class '); | pp_add_isa(' Some::Other::Class '); | |||
=head3 pp_bless | =head3 pp_bless | |||
If your pp_def routines are to be used as object methods use | If your pp_def routines are to be used as object methods use | |||
C<pp_bless> to specify the package (i.e. class) to which | C<pp_bless> to specify the package (i.e. class) to which | |||
your I<pp_def>ed methods will be added. For example, | your I<pp_def>ed methods will be added. For example, | |||
C<pp_bless('PDL::MyClass')>. The default is C<PDL> if this is | C<pp_bless('PDL::MyClass')>. The default is C<PDL> if this is | |||
omitted. | omitted. | |||
The value given here (or the default, C<PDL>), anywhere in the F<.pd> | ||||
file, will be the package into which all PP operations get added, even | ||||
for operations whose C<pp_def> was called I<before> the C<pp_bless>. | ||||
This is because that package is inserted at the start of the generated | ||||
XS code by C<pp_done>. The only way this changes is if C<pp_addxs> is | ||||
called, which will add the given code (or none if an empty string is | ||||
given) to the C<$::PDLPACK> package, | ||||
I<and then changes the package to the pp_bless value>. For historical | ||||
reasons, this cannot be changed. So, to have several different packages | ||||
in one F<.pd> file, do something like this: | ||||
# any pp_def up till now will get put in PDL::Pack2 | ||||
pp_bless('PDL::Pack1'); | ||||
pp_addxs(''); | ||||
pp_def('func1', ...); | ||||
pp_bless('PDL::Pack2'); | ||||
pp_addxs(''); | ||||
pp_def('otherfunc', ...); | ||||
=head3 pp_addxs | =head3 pp_addxs | |||
Sometimes you want to add extra XS code of your own | Sometimes you want to add extra XS code of your own | |||
(that is generally not involved with any broadcasting/indexing issues | (that is generally not involved with any broadcasting/indexing issues | |||
but supplies some other functionality you want to access from the Perl | but supplies some other functionality you want to access from the Perl | |||
side) to the generated XS file, for example | side) to the generated XS file, for example | |||
pp_addxs('',' | pp_addxs('',' | |||
# Determine endianness of machine | # Determine endianness of machine | |||
skipping to change at line 2350 | skipping to change at line 2461 | |||
=head3 P2Child | =head3 P2Child | |||
Forces C<Pars> to be C<PARENT> and C<CHILD>, the function's | Forces C<Pars> to be C<PARENT> and C<CHILD>, the function's | |||
C<GenericTypes> to be all of them, no C<HaveBroadcasting> or C<CallCopy>, | C<GenericTypes> to be all of them, no C<HaveBroadcasting> or C<CallCopy>, | |||
and turns on C<DefaultFlow> (so do not supply any of those args). | and turns on C<DefaultFlow> (so do not supply any of those args). | |||
Intended for affine transformations with dataflow. | Intended for affine transformations with dataflow. | |||
=head3 DefaultFlow | =head3 DefaultFlow | |||
If true, sets in the C<pdl_transvtable> (see L<PDL::Internals>) the | If I<set>, sets in the C<pdl_transvtable> (see L<PDL::Internals>) the | |||
C<iflags> such that the trans will start with dataflow both forwards | C<iflags> such that the trans will start with dataflow both forwards | |||
and backwards. | and backwards. Note that setting this to any value (including 0) will | |||
trigger the behaviour. | ||||
=head3 HaveBroadcasting | =head3 HaveBroadcasting | |||
Default true. If so, generate code implementing broadcasting (see | Default true. If so, generate code implementing broadcasting (see | |||
L<PDL::Indexing>). | L<PDL::Indexing>). | |||
=head3 CallCopy | =head3 CallCopy | |||
For parameters that get created, normally the C<< PDL->initialize >> | For parameters that get created, normally the C<< PDL->initialize >> | |||
will be used (or on a subclass). If this is true (which is the default | will be used (or on a subclass). If this is true (which is the default | |||
skipping to change at line 2577 | skipping to change at line 2689 | |||
section is C code that gets called by Perl when your module is loaded and is | section is C code that gets called by Perl when your module is loaded and is | |||
useful for automatic initialization. You can learn more about XS and the BOOT | useful for automatic initialization. You can learn more about XS and the BOOT | |||
section at L<perlxs>. | section at L<perlxs>. | |||
=head3 pp_addhdr | =head3 pp_addhdr | |||
Adds pure-C code to your XS file. XS files are structured such that pure C | Adds pure-C code to your XS file. XS files are structured such that pure C | |||
code must come before XS specifications. This allows you to specify such | code must come before XS specifications. This allows you to specify such | |||
C code. | C code. | |||
=head3 pp_boundscheck | ||||
PDL normally checks the bounds of your accesses before making them. You can | ||||
turn that on or off at runtime by setting MyPackage::set_boundscheck. This | ||||
function allows you to remove that runtime flexibility and B<never> do bounds | ||||
checking. It also returns the current boundschecking status if called | ||||
without any argumens. | ||||
NOTE: I have not found anything about bounds checking in other documentation. | ||||
That needs to be addressed. | ||||
=head2 Generating Perl Code | =head2 Generating Perl Code | |||
Many functions imported when you use PDL::PP allow you to modify the | Many functions imported when you use PDL::PP allow you to modify the | |||
contents of the generated .pm file. In addition to pp_def and pp_done, | contents of the generated .pm file. In addition to pp_def and pp_done, | |||
the role of these functions is primarily to add code to various parts of | the role of these functions is primarily to add code to various parts of | |||
your generated .pm file. | your generated .pm file. | |||
=head3 pp_addpm | =head3 pp_addpm | |||
Adds Perl code to the generated .pm file. PDL::PP actually keeps track of | Adds Perl code to the generated .pm file. PDL::PP actually keeps track of | |||
End of changes. 18 change blocks. | ||||
30 lines changed or deleted | 132 lines changed or added |