stas 02/03/08 01:35:17 Modified: src/docs/2.0/devel/core_explained core_explained.pod Log: - start WrapXS magic documentation. this commit creates an overview and prepares the layout of what's going to follow - documenting the techniques of writing wrappers/functions which return a single value or nothing. Revision Changes Path 1.16 +318 -0 modperl-docs/src/docs/2.0/devel/core_explained/core_explained.pod Index: core_explained.pod =================================================================== RCS file: /home/cvs/modperl-docs/src/docs/2.0/devel/core_explained/core_explained.pod,v retrieving revision 1.15 retrieving revision 1.16 diff -u -r1.15 -r1.16 --- core_explained.pod 5 Mar 2002 17:51:20 -0000 1.15 +++ core_explained.pod 8 Mar 2002 09:35:17 -0000 1.16 @@ -301,6 +301,7 @@ =head1 mpxs_ vs MPXS_ +<SCRATCH THAT> If you look at the source code there are functions starting with I<mpxs_> and those with I<MPXS_>. @@ -310,7 +311,324 @@ I<items>, I<MARK> and I<SP> arguments) or prefixed with I<MPXS_>, which are hooked directly into newXS(). +</SCRATCH THAT> + +=head1 Gluing Existing APIs + +If you have an API that you simply want to provide the Perl interface +without writing any code... + +META: complete + +WrapXS allows you to adjust some arguments and supply default values +for function arguments without writing any code + META: complete + + +=head1 Adding Wrappers for existing APIs and Creating New APIs + +In certain cases the existing APIs need to be adjusted. There are a +few reasons for doing this. + +First, is to make the given C API more Perlish. For example C +functions cannot return more than one value, and the pass by reference +technique is used. This is not Perlish. Perl has no problem returning +a list of value, and passing by reference is used only when an array +or a hash in addition to any other variables need to be passes or +returned from the function. Therefore we may want to adjust the C API +to return a list rather than passing a reference to a return value, +which is not intuitive for Perl programmers. + +Second, is to adjust the functionality, i.e. we still use the C API +but may want to adjust its arguments before calling the original +function, or do something with return values. And of course optionally +adding some new code. + +Third, is to create completely new APIs. It's quite possible that we +need more functionality built on top of the existing API. In that case +we simply create new APIs. + +The following sections discuss various techniques for retrieving +function arguments and returning values to the caller. They range from +using usual C argument passing and returning to more complex Perl +arguments' stack manipulation. Once you know how to retrieve the +arguments in various situations and how to put the return values on +the stack, the rest is usually normal C programming potentially +involving using Perl APIs. + +Let's look at various ways we can declare functions and what options +various declarions provide to us: + +=head2 Functions Returning a Single Value (or Nothing) + +If its know deterministically what the function returns and there is +only a single return value (or nothing is returned == I<void>), we are +on the C playground and we don't need to manipulate the returning +stack. However if the function may return a single value or nothing +at all, depending on the inputs and the code, we have to manually +manipulate the stack and therefore this section doesn't apply. + +Let's look at various requirements and implement these using simple +examples. The following testing code exercises the interfaces we are +about to develop, so refer to this code to see how the functions are +invoked from Perl and what is returned: + + file:t/response/TestApache/scoreboard.pm + ---------------------------------------- + package TestApache::scoreboard; + + use strict; + use warnings FATAL => 'all'; + + use Apache::Const -compile => 'OK'; + + use Apache::Test; + use Apache::TestUtil; + + use Apache::Scoreboard; + + sub handler { + my $r = shift; + + plan $r, tests => 7; + + my $a = 7; + my $b = 3; + my ($add, $subst); + + $add = Apache::Scoreboard::print($a, $b); + t_debug "print"; + ok !$add; + + $add = Apache::Scoreboard::add($a, $b); + ok t_cmp($a + $b, $add, "add"); + + $add = Apache::Scoreboard::add_sv($a, $b); + ok t_cmp($a + $b, $add, "add: return sv"); + + $add = Apache::Scoreboard::add_sv_sv($a, $b); + ok t_cmp($a + $b, $add, "add: pass/return svs"); + + ($add, $subst) = @{ Apache::Scoreboard::add_subst($a, $b) }; + ok t_cmp($a + $b, $add, "add_subst: add"); + ok t_cmp($a - $b, $subst, "add_subst: subst"); + + $subst = Apache::Scoreboard::subst_sp($a, $b); + ok t_cmp($a - $b, $subst, "subst via SP"); + + Apache::OK; + } + + 1; + +The first case is the simplest: pass two integer arguments, print +these to the STDERR stream and return nothing: + + file:xs/Apache/Scoreboard/Apache__Scoreboard.h + ---------------------------------------------- + static MP_INLINE + void mpxs_Apache__Scoreboard_print(int a, int b) + { + fprintf(stderr, "%d, %d\n", a, b); + } + + file:xs/maps/modperl_functions.map + ---------------------------------- + MODULE=Apache::Scoreboard + mpxs_Apache__Scoreboard_print + +Now let's say that the I<b> argument is optional and in case it wasn't +provided, we want to use a default value, e.g. 0. In that case we +don't need to change the code, but simply adjust the map file to be: + + file:xs/maps/modperl_functions.map + ---------------------------------- + MODULE=Apache::Scoreboard + mpxs_Apache__Scoreboard_print | | a, b=0 + +In the previous example, we didn't list the arguments in the map file +since they were automatically retrieved from the source code. In this +example we tell WrapXS to assign a value of C<0> to the argument b, if +it wasn't supplied by the caller. All the arguments must be listed and +in the same order as they are defined in the function. + +You may add an extra test that test teh default value assignment: + + $add = Apache::Scoreboard::add($a); + ok t_cmp($a + 0, $add, "add (b=0 default)"); + +The second case: pass two integer arguments and return their sum: + + file:xs/Apache/Scoreboard/Apache__Scoreboard.h + ---------------------------------------------- + static MP_INLINE + int mpxs_Apache__Scoreboard_add(int a, int b) + { + return a + b; + } + + file:xs/maps/modperl_functions.map + ---------------------------------- + MODULE=Apache::Scoreboard + mpxs_Apache__Scoreboard_add + +The third case is similar to the previous one, but we return the sum +as as a Perl scalar. Though in C we say SV*, in the Perl space we will +get a normal scalar: + + file:xs/Apache/Scoreboard/Apache__Scoreboard.h + ---------------------------------------------- + static MP_INLINE + SV *mpxs_Apache__Scoreboard_add_sv(pTHX_ int a, int b) + { + return newSViv(a + b); + } + + file:xs/maps/modperl_functions.map + ---------------------------------- + MODULE=Apache::Scoreboard + mpxs_Apache__Scoreboard_add_sv + +In the second example the XSUB function was converting the returned +I<int> value to a Perl scalar behind the scenes. In this example we +return the scalar ourselves. This is of course to demonstrate that you +can return a Perl scalar, which can be a reference to a complex Perl +datastructure, which we will see in the fifth example. + +The forth case demonstrates that you can pass Perl variables to your +functions without needing XSUB to do the conversion. In all previous +examples XSUB was automatically converting Perl scalars in the +argument list to the corresponding C variables, using the typemap +definitions. + + file:xs/Apache/Scoreboard/Apache__Scoreboard.h + ---------------------------------------------- + static MP_INLINE + SV *mpxs_Apache__Scoreboard_add_sv_sv(pTHX_ SV *a_sv, SV *b_sv) + { + int a = (int)SvIV(a_sv); + int b = (int)SvIV(b_sv); + + return newSViv(a + b); + } + + file:xs/maps/modperl_functions.map + ---------------------------------- + MODULE=Apache::Scoreboard + mpxs_Apache__Scoreboard_add_sv_sv + +So this example is the same simple case of addition, though we +manually convert the Perl variables to C variables, perform the +addition operation, convert the result to a Perl Scalar of kind I<IV> +(Integer Value) and return it directly to the caller. + +In case where more than one value needs to be returned, we can still +implement this without directly manipulating the stack before a +function returns. The fifth case demonstrates a function that returns +the result of addition and substruction operations on its arguments: + + file:xs/Apache/Scoreboard/Apache__Scoreboard.h + ---------------------------------------------- + static MP_INLINE + SV *mpxs_Apache__Scoreboard_add_subst(pTHX_ int a, int b) + { + AV *av = newAV(); + + av_push(av, newSViv(a + b)); + av_push(av, newSViv(a - b)); + + return newRV_noinc((SV*)av); + } + + file:xs/maps/modperl_functions.map + ---------------------------------- + MODULE=Apache::Scoreboard + mpxs_Apache__Scoreboard_add_subst + +If you look at the corresponding testing code: + + ($add, $subst) = @{ Apache::Scoreboard::add_subst($a, $b) }; + ok t_cmp($a + $b, $add, "add_subst: add"); + ok t_cmp($a - $b, $subst, "add_subst: subst"); + +you can see that this technique comes at a price of needing to +dereference the return value to turn it into a list. The actual code +is very similar to the C<Apache::Scoreboard::add_sv> function which +was doing only the addition operation and returning a Perl +scalar. Here we perform the addition and the substraction operation +and push the two results into a previously created I<AV*> data +structure, which represents an array. Since only the I<SV> +datastructures are allowed to be put on stack, we take a reference +I<RV> (which is of an I<SV> kind) to the existing I<AV> and return it. + +The sixth case demonstrates a situation where the number of arguments +or their types may vary and aren't known at compile time. Though +notice that we still know that we are returning at compile time (zero +or one arguments), I<int> in this example: + + file:xs/Apache/Scoreboard/Apache__Scoreboard.h + ---------------------------------------------- + static MP_INLINE + int mpxs_Apache__Scoreboard_subst_sp(pTHX_ I32 items, SV **MARK, SV **SP) + { + int a, b; + + if (items != 2) { + Perl_croak(aTHX_ "usage: ..."); + } + + a = mp_xs_sv2_int(*MARK); + b = mp_xs_sv2_int(*(MARK+1)); + + return a - b; + } + + file:xs/maps/modperl_functions.map + ---------------------------------- + MODULE=Apache::Scoreboard + mpxs_Apache__Scoreboard_subst_sp | | ... + +In the map file we use a special token C<...> which tells the XSUB +constructor to pass C<items>, C<MARK> and C<SP> arguments to the +function. The macro C<MARK> points to the first argument passed by the +caller in the Perl namespace. For example to access the second +argument to retrieve the value of C<b> we use C<*(MARK+1)>, which if +you remember represented as an I<SV> variable, which nees to be +converted to the corresponding C type. + +In this example we use the macro I<mp_xs_sv2_int>, automatically +generated based on the data from the I<xs/typemap> and +I<xs/maps/*_types.map> files, and placed into the +I<xs/modperl_xs_sv_convert.h> file. In the case of I<int> C type the +macro is: + + #define mp_xs_sv2_int(sv) (int)SvIV(sv) + +which simply converts the I<SV> variable on the stack and generates an +I<int> value. + +While in this example you have an access to the stack, you cannot +manipulate the return values, because the XSUB wrapper expects a +single return value of type I<int>, so even if you put something on +the stack it will be ignored. + + +=head2 Functions Returning Variable Number of Values + +We saw earlier that if we want to return an array one of the ways to +go is to return a reference to an array as a single return value, +which fits the C paradigm. So we simply declare the return value as +C<SV*>. + +This section talks about cases where it's unknown at compile time how +many return values will be or it's known that there will be more than +one return value--something that C cannot handle via return mechanism. + +META: ... + + +=head2 Mapping Macro's to XS subs =head1 MP_INLINE vs C Macros vs Normal Functions
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]