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]

Reply via email to