stas 02/03/19 23:10:21
Modified: src/docs/2.0/devel/core_explained core_explained.pod
Log:
- cover the mapping and implementation of the glue code for macros and
MPXS_ wrappers
Revision Changes Path
1.18 +259 -55
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.17
retrieving revision 1.18
diff -u -r1.17 -r1.18
--- core_explained.pod 18 Mar 2002 07:19:18 -0000 1.17
+++ core_explained.pod 20 Mar 2002 07:10:21 -0000 1.18
@@ -299,20 +299,6 @@
modperl_xs_sv_convert.h
modperl_xs_typedefs.h
-=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_>.
-
-If you want to mess directly with the stack (i.e. without specifying
-the prototype of the function), there are two ways to do that. Either
-the function has to have a prototype C<...> (which stands for
-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
@@ -374,9 +360,9 @@
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
+ file:t/response/TestApache/coredemo.pm
----------------------------------------
- package TestApache::scoreboard;
+ package TestApache::coredemo;
use strict;
use warnings FATAL => 'all';
@@ -386,7 +372,7 @@
use Apache::Test;
use Apache::TestUtil;
- use Apache::Scoreboard;
+ use Apache::CoreDemo;
sub handler {
my $r = shift;
@@ -397,24 +383,24 @@
my $b = 3;
my ($add, $subst);
- $add = Apache::Scoreboard::print($a, $b);
+ $add = Apache::CoreDemo::print($a, $b);
t_debug "print";
ok !$add;
- $add = Apache::Scoreboard::add($a, $b);
+ $add = Apache::CoreDemo::add($a, $b);
ok t_cmp($a + $b, $add, "add");
- $add = Apache::Scoreboard::add_sv($a, $b);
+ $add = Apache::CoreDemo::add_sv($a, $b);
ok t_cmp($a + $b, $add, "add: return sv");
- $add = Apache::Scoreboard::add_sv_sv($a, $b);
+ $add = Apache::CoreDemo::add_sv_sv($a, $b);
ok t_cmp($a + $b, $add, "add: pass/return svs");
- ($add, $subst) = @{ Apache::Scoreboard::add_subst($a, $b) };
+ ($add, $subst) = @{ Apache::CoreDemo::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);
+ $subst = Apache::CoreDemo::subst_sp($a, $b);
ok t_cmp($a - $b, $subst, "subst via SP");
Apache::OK;
@@ -425,18 +411,18 @@
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
+ file:xs/Apache/CoreDemo/Apache__CoreDemo.h
----------------------------------------------
static MP_INLINE
- void mpxs_Apache__Scoreboard_print(int a, int b)
+ void mpxs_Apache__CoreDemo_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
+ MODULE=Apache::CoreDemo
+ mpxs_Apache__CoreDemo_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
@@ -444,8 +430,8 @@
file:xs/maps/modperl_functions.map
----------------------------------
- MODULE=Apache::Scoreboard
- mpxs_Apache__Scoreboard_print | | a, b=0
+ MODULE=Apache::CoreDemo
+ mpxs_Apache__CoreDemo_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
@@ -455,40 +441,40 @@
You may add an extra test that test teh default value assignment:
- $add = Apache::Scoreboard::add($a);
+ $add = Apache::CoreDemo::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
+ file:xs/Apache/CoreDemo/Apache__CoreDemo.h
----------------------------------------------
static MP_INLINE
- int mpxs_Apache__Scoreboard_add(int a, int b)
+ int mpxs_Apache__CoreDemo_add(int a, int b)
{
return a + b;
}
file:xs/maps/modperl_functions.map
----------------------------------
- MODULE=Apache::Scoreboard
- mpxs_Apache__Scoreboard_add
+ MODULE=Apache::CoreDemo
+ mpxs_Apache__CoreDemo_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
+ file:xs/Apache/CoreDemo/Apache__CoreDemo.h
----------------------------------------------
static MP_INLINE
- SV *mpxs_Apache__Scoreboard_add_sv(pTHX_ int a, int b)
+ SV *mpxs_Apache__CoreDemo_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
+ MODULE=Apache::CoreDemo
+ mpxs_Apache__CoreDemo_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
@@ -502,10 +488,10 @@
argument list to the corresponding C variables, using the typemap
definitions.
- file:xs/Apache/Scoreboard/Apache__Scoreboard.h
+ file:xs/Apache/CoreDemo/Apache__CoreDemo.h
----------------------------------------------
static MP_INLINE
- SV *mpxs_Apache__Scoreboard_add_sv_sv(pTHX_ SV *a_sv, SV *b_sv)
+ SV *mpxs_Apache__CoreDemo_add_sv_sv(pTHX_ SV *a_sv, SV *b_sv)
{
int a = (int)SvIV(a_sv);
int b = (int)SvIV(b_sv);
@@ -515,8 +501,8 @@
file:xs/maps/modperl_functions.map
----------------------------------
- MODULE=Apache::Scoreboard
- mpxs_Apache__Scoreboard_add_sv_sv
+ MODULE=Apache::CoreDemo
+ mpxs_Apache__CoreDemo_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
@@ -528,10 +514,10 @@
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
+ file:xs/Apache/CoreDemo/Apache__CoreDemo.h
----------------------------------------------
static MP_INLINE
- SV *mpxs_Apache__Scoreboard_add_subst(pTHX_ int a, int b)
+ SV *mpxs_Apache__CoreDemo_add_subst(pTHX_ int a, int b)
{
AV *av = newAV();
@@ -543,18 +529,18 @@
file:xs/maps/modperl_functions.map
----------------------------------
- MODULE=Apache::Scoreboard
- mpxs_Apache__Scoreboard_add_subst
+ MODULE=Apache::CoreDemo
+ mpxs_Apache__CoreDemo_add_subst
If you look at the corresponding testing code:
- ($add, $subst) = @{ Apache::Scoreboard::add_subst($a, $b) };
+ ($add, $subst) = @{ Apache::CoreDemo::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
+is very similar to the C<Apache::CoreDemo::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
@@ -567,10 +553,10 @@
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
+ file:xs/Apache/CoreDemo/Apache__CoreDemo.h
----------------------------------------------
static MP_INLINE
- int mpxs_Apache__Scoreboard_subst_sp(pTHX_ I32 items, SV **MARK, SV **SP)
+ int mpxs_Apache__CoreDemo_subst_sp(pTHX_ I32 items, SV **MARK, SV **SP)
{
int a, b;
@@ -586,8 +572,8 @@
file:xs/maps/modperl_functions.map
----------------------------------
- MODULE=Apache::Scoreboard
- mpxs_Apache__Scoreboard_subst_sp | | ...
+ MODULE=Apache::CoreDemo
+ mpxs_Apache__CoreDemo_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
@@ -623,13 +609,231 @@
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.
+one return value--something that C cannot handle via its return
+mechanism.
+
+Let's rewrite the function C<mpxs_Apache__CoreDemo_add_subst> from
+the earlier section to return two results instead of a reference to a
+list:
+
+ file:xs/Apache/CoreDemo/Apache__CoreDemo.h
+ ----------------------------------------------
+ static XS(MPXS_Apache__CoreDemo_add_subst_sp)
+ {
+ dXSARGS;
+ int a, b;
+
+ if (items != 2) {
+ Perl_croak(aTHX_ "usage: Apache::CoreDemo::add_subst_sp($a, $b)");
+ }
+ a = mp_xs_sv2_int(ST(0));
+ b = mp_xs_sv2_int(ST(1));
+
+ SP -= items;
+
+ if (GIMME == G_ARRAY) {
+ EXTEND(sp, 2);
+ PUSHs(sv_2mortal(newSViv(a + b)));
+ PUSHs(sv_2mortal(newSViv(a - b)));
+ }
+ else {
+ XPUSHs(sv_2mortal(newSViv(a + b)));
+ }
+
+ PUTBACK;
+ }
+
+Before explaining the function here is the prototype we add to the map
+file:
+
+ file:xs/maps/modperl_functions.map
+ ----------------------------------
+ MODULE=Apache::CoreDemo
+ DEFINE_add_subst_sp | MPXS_Apache__CoreDemo_add_subst_sp | ...
+
+This declaration say that C<MPXS_Apache__CoreDemo_add_subst_sp>
+function will be called from the XS wrapper for the
+C<Apache::CoreDemo::add_subst_sp> with arguments:
+
+ aTHX_ I32 items, SP **sp, SV **MARK
+
+that's what the C<...> part in the third column stands for.
+
+In this function we manually manipulate the stack to retrieve the
+arguments passed on the Perl side and put the results back onto the
+stack. Therefore the first thing we do is to initialize a few special
+variables using the C<dXSARGS> macro defined in I<XSUB.h>, which in
+fact calls a bunch of other macros. These variables help to manipulate
+the stack. C<dSP> is one of these macros and it declares and initial�
+izes a local copy of the Perl stack pointer C<sp> which . This local
+copy should always be accessed as C<SP>.
+
+We retrieve the original function arguments using the C<ST()>
+macros. C<ST(0)> and C<ST(1)> point to the first and the second
+argument on the stack, respectively. But first we check that we have
+exactly two arguments on the stack, and if not we abort the
+function. The C<items> variable is the function argument.
+
+Once we have retrieved all the arguments from the stack we set the
+local stack pointer C<SP> to point to the bottom of the stack (like
+there are no items on the stack):
+
+ SP -= items;
+
+Now we can do whatever processing is needed and put the results back
+on the stack. In our example we return the results of addition and
+substraction operations if the function is called in the list
+context. In the scalar context the function returns only the result of
+the addition operation. We use the C<GIMME> macro which tells us the
+context.
+
+In the list context we make sure that we have two spare slots on the
+stack since we are going to push two items, and then we push them
+using the C<PUSHs> macro:
+
+ EXTEND(sp, 2);
+ PUSHs(sv_2mortal(newSViv(a + b)));
+ PUSHs(sv_2mortal(newSViv(a - b)));
+
+Alternatively we could use:
-META: ...
+ XPUSHs(sv_2mortal(newSViv(a + b)));
+ XPUSHs(sv_2mortal(newSViv(a - b)));
+The C<XPUSHs> macro eI<X>tends the stack before pushing the item into
+it if needed. If we plan to push more than a single item onto the
+stack, it's more efficient to extend the stack in one call.
+
+In the scalar context we push only one item, so here we use the
+C<XPUSHs> macro:
+
+ XPUSHs(sv_2mortal(newSViv(a + b)));
+
+The last command we call is:
+
+ PUTBACK;
+
+which makes the local stack pointer global. This is a must call if the
+state of the stack was changed when the function is about to
+return. The stack changes if something was popped from or pushed to
+it, or both and changed the number of items on the stack.
+
+In our example we don't need to call C<PUTBACK> if the function is
+called in the list context. Because in this case we return two
+variables, the same as two function arguments, the count didn't
+change. Though in the scalar context we push onto the stack only one
+argument, so the function won't return what is expected. The simplest
+way to avoid errors here is to always call C<PUTBACK> when the stack
+is changed.
+
+For more information refer to the I<perlcall> manpage which explains
+the stack manipulation process in great details.
+
+Finally we test the function in the list and scalar contexts:
+
+ file:t/response/TestApache/coredemo.pm
+ ----------------------------------------
+ ...
+ my $a = 7;
+ my $b = 3;
+ my ($add, $subst);
+
+ # list context
+ ($add, $subst) = Apache::CoreDemo::add_subst_sp($a, $b);
+ ok t_cmp($a + $b, $add, "add_subst_sp list context: add");
+ ok t_cmp($a - $b, $subst, "add_subst_sp list context: subst");
+
+ # scalar context
+ $add = Apache::CoreDemo::add_subst_sp($a, $b);
+ ok t_cmp($a + $b, $add, "add_subs_spt scalar context: add");
+ ...
+
+=head2 Wrappers Functions for C Macros
+
+Let's say you have a C macro which you want to provide a Perl
+interface for. For example let's take a simple macro which performs
+the power of function:
+
+ file:xs/Apache/CoreDemo/Apache__CoreDemo.h
+ ----------------------------------------------
+ #define mpxs_Apache__CoreDemo_power(x, y) pow(x, y)
+
+To create the XS glue code we use the following entry in the map file:
+
+ file:xs/maps/modperl_functions.map
+ ----------------------------------
+ MODULE=Apache::CoreDemo
+ double:DEFINE_power | | double:x, double:y
+
+This works very similar to the C<MPXS_Apache__CoreDemo_add_subst_sp>
+function presented earlier. But since this is a macro the XS wrapper
+needs to know the types of the arguments and the return type, so these
+are added. The return type is added just before the function name and
+separated from it by the colon (C<:>), the argument types are
+specified in the third column. The type is always separated from the
+name of the variable by the colon (C<:>).
+
+And of course finally we need to test that the function works in Perl:
+
+ file:t/response/TestApache/coredemo.pm
+ ----------------------------------------
+ ...
+ my $a = 7;
+ my $b = 3;
+ my $power = Apache::CoreDemo::power($a, $b);
+ ok t_cmp($a ** $b, $power, "power macro");
+ ...
+
+=head1 Wrappers for modperl_, apr_ and ap_ APIs
+
+If you already have a C function whose name starts from I<modperl_>,
+I<apr_> or I<ap_> and you want to do something before calling the real
+C function, you can write a XS wrapper using the same method as in the
+L<MPXS_Apache__CoreDemo_add_subst_sp
+|/Functions_Returning_Variable_Number_of_Values>. The only difference
+is that it'll be clearly seen in the map file that this is a wrapper
+for an existing C API.
+
+Let's say that we have an existing C function apr_power(), this is how
+we declare its wrapper:
+
+ file:xs/maps/apr_functions.map
+ ----------------------------------
+ MODULE=APR::Foo
+ apr_power | MPXS_ | x, y
+
+The first column specifies the existing function's name, the second
+tells that the XS wrapper will use the C<MPXS_> prefix, which means
+that the wrapper must be called C<MPXS_apr_power>. The third column
+specifies the argument names, but no matter what you specify there the
+C<...> will be passed:
+
+ aTHX_ I32 items, SP **sp, SV **MARK
+
+so you can leave that column empty.
+
+If the forth column is empty this function will be called
+C<APR::Foo::power> in the Perl namespace. But you can use that column
+to give a different Perl name, e.g with:
+
+ apr_power | MPXS_ | x, y | pow
+
+This function will be available from Perl as C<APR::Foo::pow>.
+
+Similarly you can write a C<MPXS_odperl_power> wrapper for a
+C<modperl_power()> function but here you have to explicitly give the
+Perl function's name in the forth column:
+
+ file:xs/maps/apr_functions.map
+ ----------------------------------
+ MODULE=Apache::CoreDemo
+ modperl_power | MPXS_ | x, y | mypower
-=head2 Mapping Macro's to XS subs
+and the Perl function will be called C<Apache::CoreDemo::mypower>.
+The C<MPXS_> wrapper's implementation is similar to
+L<MPXS_Apache__CoreDemo_add_subst_sp
+|/Functions_Returning_Variable_Number_of_Values>.
=head1 MP_INLINE vs C Macros vs Normal Functions
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]