1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174
|
=head1 NAME
mod_perl 2.0 Source Code Explained
=head1 Description
This document explains how to navigate the mod_perl source code,
modify and rebuild the existing code and most important: how to add
new functionality.
=head1 Project's Filesystem Layout
In its pristine state the project is comprised of the following
directories and files residing at the root directory of the project:
Apache-Test/ - test kit for mod_perl and Apache2::* modules
ModPerl-Registry/ - ModPerl::Registry sub-project
build/ - utilities used during project build
docs/ - documentation
lib/ - Perl modules
src/ - C code that builds libmodperl.so
t/ - mod_perl tests
todo/ - things to be done
util/ - useful utilities for developers
xs/ - source xs code and maps
Changes - Changes file
LICENSE - ASF LICENSE document
Makefile.PL - generates all the needed Makefiles
After building the project, the following root directories and files
get generated:
Makefile - Makefile
WrapXS/ - autogenerated XS code
blib/ - ready to install version of the package
=head1 Directory src
=head2 Directory src/modules/perl/
The directory I<src/modules/perl> includes the C source files needed
to build the I<libmodperl> library.
Notice that several files in this directory are autogenerated during
the I<perl Makefile> stage.
When adding new source files to this directory you should add their
names to the C<@c_src_names> variable in I<lib/ModPerl/Code.pm>, so
they will be picked up by the autogenerated I<Makefile>.
=head1 Directory xs/
Apache2/ - Apache specific XS code
APR/ - APR specific XS code
ModPerl/ - ModPerl specific XS code
maps/ -
tables/ -
Makefile.PL -
modperl_xs_sv_convert.h -
modperl_xs_typedefs.h -
modperl_xs_util.h -
typemap -
=head2 xs/Apache2, xs/APR and xs/ModPerl
The I<xs/Apache2>, I<xs/APR> and I<xs/ModPerl> directories include I<.h> files which
have C and XS code in them. They all have the I<.h> extension because
they are always C<#include-d>, never compiled into their own object
file. and only the file that C<#include-s> an I<.h> file from these
directories should be able to see what's in there. Anything else
belongs in a I<src/modules/perl/foo.c> public API.
=head2 xs/maps
The I<xs/maps> directory includes mapping files which describe how
Apache Perl API should be constructed and various XS typemapping.
These files get modified whenever:
=over
=item *
a new function is added or the API of the existing one is modified.
=item *
a new struct is added or the existing one is modified
=item *
a new C datatype or Perl typemap is added or an existing one is
modified.
=back
The execution of:
% make source_scan
or:
% perl build/source_scan.pl
converts these map files into their Perl table representation in the
I<xs/tables/current/> directory. This Perl representation is then used
during C<perl Makefile.PL> to generate the XS code in the I<./WrapXS/>
directory by the xs_generate() function. This XS code is combined of
the Apache API Perl glue and mod_perl specific extensions.
If you need to skip certain unwanted C defines from being picked by
the source scanning you can add them to the array
C<$Apache2::ParseSource::defines_unwanted> in
I<lib/Apache2/ParseSource.pm>.
Notice that I<source_scan> target is normally not run during the
project build process, since the source scanning is not stable yet,
therefore everytime the map files change, C<make source_scan> should
be run manually and the updated files ending up in the
I<xs/tables/current/> directory should be committed to the svn
repository.
I<lib/ModPerl/CScan.pm> requires Data::Flow from CPAN
which is used by I<build/source_scan.pl>
There are three different types of map files in the I<xs/maps/>
directory:
=over
=item * Functions Mapping
apache_functions.map
modperl_functions.map
apr_functions.map
=item * Structures Mapping
apache_structures.map
apr_structures.map
=item * Types Mapping
apache_types.map
apr_types.map
modperl_types.map
=back
The following sections describe the syntax of the files in each group
=head3 Functions Mapping
The functions mapping file is comprised of groups of function
definitions. Each group starts with a header similar to XS syntax:
MODULE=... PACKAGE=... PREFIX=... BOOT=... ISA=...
where:
=over
=item * C<MODULE>
the module name where the functions should be put. e.g. C<MODULE
Apache2::Connection> will place the functions into
I<WrapXS/Apache2/Connection.{pm,xs}>.
=item * C<PACKAGE>
the package name functions belong to, defaults to C<MODULE>. The
value of I<guess> indicates that package name should be guessed based
on first argument found that maps to a Perl class. If the value is
not defined and the function's name starts with I<ap_> the C<Apache2>
package will be used, if it starts with I<apr_> then the C<APR>
package is used.
=item * C<PREFIX>
prefix string to be stripped from the function name. If not specified
it defaults to C<PACKAGE>, converted to C name convention, e.g.
C<APR::Base64> makes the prefix: I<apr_base64_>. If the converted
prefix does not match, defaults to I<ap_> or I<apr_>.
=item * C<BOOT>
The C<BOOT> directive tells the XS generator, whether to add the boot
function to the autogenerated XS file or not. If the value of C<BOOT>
is not true or it's simply not declared, the boot function won't be
added.
If the value is true, a boot function will be added to the XS file.
Note, that this function is not declared in the map file.
The boot function name must be constructed from three parts:
'mpxs_' . MODULE . '_BOOT'
where C<MODULE> is the one declared with C<MODULE=> in the map file.
For example if we want to have an XS boot function for a class
C<APR::IO>, we create this function in I<xs/APR/IO/APR__IO.h>:
static void mpxs_APR__IO_BOOT(pTHX)
{
/* boot code here */
}
and now we add the C<BOOT=1> declaration to the
I<xs/maps/modperl_functions.map> file:
MODULE=APR::IO PACKAGE=APR::IO BOOT=1
Notice that the C<PACKAGE=> declaration is a must.
When I<make xs_generate> is run (after running I<make source_scan>),
it autogenerates I<Wrap/APR/IO/IO.xs> and amongst other things will
include:
BOOT:
mpxs_APR__IO_BOOT(aTHXo);
=item * C<ISA>
META: complete
=back
Every function definition is declared on a separate line (use C<\> if
the line is too long), using the following format:
C function name | Dispatch function name | Argspec | Perl alias
where:
=over
=item * C function name
The name of the real C function.
Function names that do not begin with C</^\w/> are skipped. For
details see: C<%ModPerl::MapUtil::disabled_map>.
The return type can be specified before the C function name. It
defaults to I<return_type> in C<{Apache2,ModPerl}::FunctionTable>.
META: DEFINE nuances
=item * Dispatch function name
Dispatch function name defaults to C function name. If the dispatch
name is just a prefix (I<mpxs_>, I<MPXS_>) the C function name is
appended to it.
See the explanation about function naming and arguments passing.
=item * Argspec
The argspec defaults to arguments in
C<{Apache2,ModPerl}::FunctionTable>. Argument types can be specified
to override those in the C<FunctionTable>. Default values can be
specified, e.g. C<arg=default_value>. Argspec of C<...> indicates
I<passthru>, calling the function with C<(aTHX_ I32 items, SP **sp, SV
**MARK)>.
=item * Perl alias
the Perl alias will be created in the current C<PACKAGE>.
=back
=head3 Structures Mapping
See %ModPerl::MapUtil::disabled_map in lib/ModPerl/MapUtil.pm
META: complete
=head3 Types Mapping
META: complete
=head3 Modifying Maps
As explained in the beginning of this section, whenever the map file
is modified you need first to run:
% make source_scan
Next check that the conversion to Perl tables is properly done by
verifying the resulting corresponding file in
I<xs/tables/current>. For example I<xs/maps/modperl_functions.map> is
converted into I<xs/tables/current/ModPerl/FunctionTable.pm>.
If you want to do a visual check on how XS code will be generated, run:
% make xs_generate
and verify that the autogenerated XS code under the directory
I<./WrapXS> is correct. Notice that for functions, whose arguments or
return types can't be resolved, the XS glue won't be generated and a
warning will be printed. If that's the case add the missing type's
typemap to the types map file as explained in L<Adding Typemaps for
new C Data
Types|/Adding_Typemaps_for_new_C_Data_Types> and run the XS generation
stage again.
You can also build the project normally:
% perl Makefile.PL ...
which runs the XS generation stage.
=head2 XS generation process
As mentioned before XS code is generated in the I<WrapXS> directory
either during C<perl Makefile.PL> via xs_generate() if
C<MP_GENERATE_XS=1> is used (which is the default) or explicitly via:
% make xs_generate
In addition it creates a number of files in the I<xs/> directory:
modperl_xs_sv_convert.h
modperl_xs_typedefs.h
=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
C<MPXS_> functions are final C<XSUBs> and always accept:
aTHX_ I32 items, SP **sp, SV **MARK
as their arguments. Whereas C<mpxs_> functions are either intermediate
thin wrappers for the existing C functions or functions that do
something by themselves. C<MPXS_> functions also can be used for
writing thin wrappers for C macros.
=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/TestApache2/coredemo.pm
----------------------------------------
package TestApache2::coredemo;
use strict;
use warnings FATAL => 'all';
use Apache2::Const -compile => 'OK';
use Apache::Test;
use Apache::TestUtil;
use Apache2::CoreDemo;
sub handler {
my $r = shift;
plan $r, tests => 7;
my $a = 7;
my $b = 3;
my ($add, $subst);
$add = Apache2::CoreDemo::print($a, $b);
t_debug "print";
ok !$add;
$add = Apache2::CoreDemo::add($a, $b);
ok t_cmp($a + $b, $add, "add");
$add = Apache2::CoreDemo::add_sv($a, $b);
ok t_cmp($a + $b, $add, "add: return sv");
$add = Apache2::CoreDemo::add_sv_sv($a, $b);
ok t_cmp($a + $b, $add, "add: pass/return svs");
($add, $subst) = @{ Apache2::CoreDemo::add_subst($a, $b) };
ok t_cmp($a + $b, $add, "add_subst: add");
ok t_cmp($a - $b, $subst, "add_subst: subst");
$subst = Apache2::CoreDemo::subst_sp($a, $b);
ok t_cmp($a - $b, $subst, "subst via SP");
Apache2::Const::OK;
}
1;
The first case is the simplest: pass two integer arguments, print
these to the STDERR stream and return nothing:
file:xs/Apache2/CoreDemo/Apache2__CoreDemo.h
----------------------------------------------
static MP_INLINE
void mpxs_Apache2__CoreDemo_print(int a, int b)
{
fprintf(stderr, "%d, %d\n", a, b);
}
file:xs/maps/modperl_functions.map
----------------------------------
MODULE=Apache2::CoreDemo
mpxs_Apache2__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
don't need to change the code, but simply adjust the map file to be:
file:xs/maps/modperl_functions.map
----------------------------------
MODULE=Apache2::CoreDemo
mpxs_Apache2__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
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 = Apache2::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/Apache2/CoreDemo/Apache2__CoreDemo.h
----------------------------------------------
static MP_INLINE
int mpxs_Apache2__CoreDemo_add(int a, int b)
{
return a + b;
}
file:xs/maps/modperl_functions.map
----------------------------------
MODULE=Apache2::CoreDemo
mpxs_Apache2__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/Apache2/CoreDemo/Apache2__CoreDemo.h
----------------------------------------------
static MP_INLINE
SV *mpxs_Apache2__CoreDemo_add_sv(pTHX_ int a, int b)
{
return newSViv(a + b);
}
file:xs/maps/modperl_functions.map
----------------------------------
MODULE=Apache2::CoreDemo
mpxs_Apache2__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
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/Apache2/CoreDemo/Apache2__CoreDemo.h
----------------------------------------------
static MP_INLINE
SV *mpxs_Apache2__CoreDemo_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=Apache2::CoreDemo
mpxs_Apache2__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
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/Apache2/CoreDemo/Apache2__CoreDemo.h
----------------------------------------------
static MP_INLINE
SV *mpxs_Apache2__CoreDemo_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=Apache2::CoreDemo
mpxs_Apache2__CoreDemo_add_subst
If you look at the corresponding testing code:
($add, $subst) = @{ Apache2::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<Apache2::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
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/Apache2/CoreDemo/Apache2__CoreDemo.h
----------------------------------------------
static MP_INLINE
int mpxs_Apache2__CoreDemo_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=Apache2::CoreDemo
mpxs_Apache2__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
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 its return
mechanism.
Let's rewrite the function C<mpxs_Apache2__CoreDemo_add_subst> from
the earlier section to return two results instead of a reference to a
list:
file:xs/Apache2/CoreDemo/Apache2__CoreDemo.h
----------------------------------------------
static XS(MPXS_Apache2__CoreDemo_add_subst_sp)
{
dXSARGS;
int a, b;
if (items != 2) {
Perl_croak(aTHX_ "usage: Apache2::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=Apache2::CoreDemo
DEFINE_add_subst_sp | MPXS_Apache2__CoreDemo_add_subst_sp | ...
The C<mpxs_> functions declare in the third column the arguments that
they expect to receive (and optionally the default values). The
C<MPXS> functions are the real C<XSUBs> and therefore they always
accept:
aTHX_ I32 items, SP **sp, SV **MARK
as their arguments. Therefore it doesn't matter what is placed in this
column when the C<MPXS_> function is declared. Usually for
documentation the Perl side arguments are listed. For example you can
say:
DEFINE_add_subst_sp | MPXS_Apache2__CoreDemo_add_subst_sp | x, y
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:
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/TestApache2/coredemo.pm
----------------------------------------
...
my $a = 7;
my $b = 3;
my ($add, $subst);
# list context
($add, $subst) = Apache2::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 = Apache2::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/Apache2/CoreDemo/Apache2__CoreDemo.h
----------------------------------------------
#define mpxs_Apache2__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=Apache2::CoreDemo
double:DEFINE_power | | double:x, double:y
This works very similar to the C<MPXS_Apache2__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/TestApache2/coredemo.pm
----------------------------------------
...
my $a = 7;
my $b = 3;
my $power = Apache2::CoreDemo::power($a, $b);
ok t_cmp($a ** $b, $power, "power macro");
...
=head2 Passing aTHX for DEFINE map entries
Let's say you have a function or a C macro which you want to provide a
Perl interface for, and you don't need to write a wrapper since C
arguments are the same as Perl arguments. For example:
char *foo(aTHX_ int bar);
The map entry will look like:
MODULE=Apache2::CoreDemo
char *:DEFINE_foo | | int:bar
But there is no way to pass C<aTHX_> since this is a macro and it's an
empty string with non-threaded Perls. Another macro comes to help:
file:xs/Apache2/CoreDemo/Apache2__CoreDemo.h
----------------------------------------------
#define mpxs_Apache2__CoreDemo_foo(x, y) foo(aTHX_ x, y)
=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_Apache2__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 for C<MPXS_> 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, but here we use C<x> and C<y> to
remind us that these two arguments are passed from Perl.
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_modperl_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=Apache2::CoreDemo
modperl_power | MPXS_ | x, y | mypower
and the Perl function will be called C<Apache2::CoreDemo::mypower>.
The C<MPXS_> wrapper's implementation is similar to
L<MPXS_Apache2__CoreDemo_add_subst_sp
|/Functions_Returning_Variable_Number_of_Values>.
=head1 MP_INLINE vs C Macros vs Normal Functions
To make the code maintainable and reusable functions and macros are
used in when programming in C (and other languages :).
When function is marked as I<inlined> it's merely a hint to the
compiler to replace the call to a function with the code inside this
function (i.e. inlined). Not every function can be inlined. Some
typical reasons why inlining is sometimes not done include:
=over
=item *
the function calls itself, that is, is recursive
=item *
the function contains loops such as C<for(;;)> or C<while()>
=item *
the function size is too large
=back
Most of the advantage of inline functions comes from avoiding the
overhead of calling an actual function. Such overhead includes saving
registers, setting up stack frames, etc. But with large functions the
overhead becomes less important.
Use the C<MP_INLINE> keyword in the declaration of the functions that
are to be inlined. The functions should be inlined when:
=over
=item *
Only ever called once (the I<wrappers> that are called from I<.xs>
files), no matter what the size of code is.
=item *
Short bodies of code called in a I<hot> code (like
I<modperl_env_hv_store>, which is called many times inside of a loop),
where it is cleaner to see the code in function form rather than macro
with lots of C<\>'s. Remember that an inline function takes much more
space than a normal functions if called from many places in the code.
=back
Of course C macros are a bit faster then inlined functions, since
there is not even I<short jump> to be made, the code is literally
copied into the place it's called from. However using macros comes at
a price:
=over
=item *
Also unlike macros, in functions argument types are checked, and
necessary conversions are performed correctly. With macros it's
possible that weird things will happen if the caller has passed
arguments of the wrong type when calling a macro.
=item *
One should be careful to pass only absolute values as I<"arguments">
to macros. Consider a macro that returns an absolute value of the
passed argument:
#define ABS(v) ( (v) >= 0 ? (v) : -(v) )
In our example if you happen to pass a function it will be called
twice:
abs_val = ABS(f());
Since it'll be extended as:
abs_val = f() >= 0 ? f() : -f();
You cannot do simple operation like increment--in our example it will
be called twice:
abs_val = ABS(i++);
Because it becomes:
abs_val = i++ >= 0 ? i++ : -i++;
=item *
It's dangerous to use the if() condition without enclosing the code in
C<{}>, since the macro may be called from inside another if-else
condition, which may cause the else part called if the if() part from
the macro fails.
But we always use C<{}> for the code inside the if-else condition, so
it's not a problem here.
=item *
A multi-line macro can cause problems if someone uses the macro in a
context that demands a single statement.
while (foo) MYMACRO(bar);
But again, we always enclose any code in conditional with C<{}>, so
it's not a problem for us.
=item *
Inline functions present a problem for debuggers and profilers,
because the function is expanded at the point of call and loses its
identity. This makes the debugging process a nightmare.
A compiler will typically have some option available to disable
inlining.
=back
In all other cases use normal functions.
=head1 Adding New Interfaces
=head2 Adding Typemaps for new C Data Types
Sometimes when a new interface is added it may include C data types
for which we don't have corresponding XS typemaps yet. In such a case,
the first thing to do is to provide the required typemaps.
Let's add a prototype for the I<typedef struct scoreboard> data type
defined in I<httpd-2.0/include/scoreboard.h>.
First we include the relevant header files in
I<src/modules/perl/modperl_apache_includes.h>:
#include "scoreboard.h"
If you want to specify your own type and don't have a header file for
it (e.g. if you extend some existing datatype within mod_perl) you may
add the I<typedef> to I<src/modules/perl/modperl_types.h>.
After deciding that C<Apache::Scoreboard> is the Perl class will be
used for manipulating C I<scoreboard> data structures, we map the
I<scoreboard> data structure to the C<Apache::Scoreboard>
class. Therefore we add to I<xs/maps/apache_types.map>:
struct scoreboard | Apache::Scoreboard
Since we want the I<scoreboard> data structure to be an opaque object
on the perl side, we simply let mod_perl use the default C<T_PTROBJ>
typemap. After running C<make xs_generate> you can check the assigned
typemap in the autogenerated I<WrapXS/typemap> file.
If you need to do some special handling while converting from C to
Perl and back, you need to add the conversion functions to the
I<xs/typemap> file. For example the C<Apache2::RequestRec> objects need
special handling, so you can see the special C<INPUT> and C<OUTPUT>
typemappings for the corresponding C<T_APACHEOBJ> object type.
Now we run C<make xs_generate> and find the following definitions in
the autogenerated files:
file:xs/modperl_xs_typedefs.h
-----------------------------
typedef scoreboard * Apache__Scoreboard;
file:xs/modperl_xs_sv_convert.h
-------------------------------
#define mp_xs_sv2_Apache__Scoreboard(sv) \
((SvROK(sv) && (SvTYPE(SvRV(sv)) == SVt_PVMG)) \
|| (Perl_croak(aTHX_ "argument is not a blessed reference \
(expecting an Apache::Scoreboard derived object)"),0) ? \
(scoreboard *)SvIV((SV*)SvRV(sv)) : (scoreboard *)NULL)
#define mp_xs_Apache__Scoreboard_2obj(ptr) \
sv_setref_pv(sv_newmortal(), "Apache::Scoreboard", (void*)ptr)
The file I<xs/modperl_xs_typedefs.h> declares the typemapping from C
to Perl and equivalent to the C<TYPEMAP> section of the XS's
I<typemap> file. The second file I<xs/modperl_xs_sv_convert.h>
generates two macros. The first macro is used to convert from Perl to
C datatype and equivalent to the I<typemap> file's C<INPUT>
section. The second macro is used to convert from C to Perl datatype
and equivalent to the I<typemap>'s C<OUTPUT> section.
Now proceed on adding the glue code for the new interface.
=head2 Importing Constants and Enums into Perl API
To I<import> httpd and APR constants and enums into Perl API, edit
I<lib/Apache2/ParseSource.pm>. To add a new type of C<DEFINE> constants
adjust the C<%defines_wanted> variable, for C<enums> modify
C<%enums_wanted>.
For example to import all C<DEFINE>s starting with C<APR_FLOCK_> add:
my %defines_wanted = (
...
APR => {
...
flock => [qw{APR_FLOCK_}],
...
},
);
When deciding which constants are to be exported, the regular
expression will be used, so in our example all matches
C</^APR_FLOCK_/> will be imported into the Perl API.
For example to import an I<read_type_e> C<enum> for APR, add:
my %enums_wanted = (
APR => { map { $_, 1 } qw(apr_read_type) },
);
Notice that I<_e> part at the end of the enum name has gone.
in case of Apache constants remove the leading C<ap_> and terminating
C</_(t|e)$/>. For example I<ap_conn_keepalive_e> needs to be added as:
my %enums_wanted = (
Apache2 => { map { $_, 1 } qw(conn_keepalive) },
);
After adding/modifying the datastructures make sure to run C<make
source_scan> or C<perl build/source_scan.pl> and verify that the
wanted constant or enum were picked by the source scanning
process. Simply grep I<xs/tables/current> for the wanted string. For
example after adding I<apr_read_type_e> enum we can check:
% more xs/tables/current/Apache2/ConstantsTable.pm
...
'read_type' => [
'APR_BLOCK_READ',
'APR_NONBLOCK_READ'
],
Of course the newly added constant or enum's typemap should be
declared in the appropriate I<xs/maps/*_types.map> files, so the XS
conversion of arguments will be performed correctly. For example
I<apr_read_type> is an APR enum so it's declared in
I<xs/maps/apr_types.map>:
apr_read_type | IV
C<IV> is used as a typemap, Since enum is just an integer. In more
complex cases the typemap can be different. (META: examples)
=head1 Maintainers
Maintainer is the person(s) you should contact with updates,
corrections and patches.
Stas Bekman [http://stason.org/]
=head1 Authors
=over
=item *
Stas Bekman [http://stason.org/]
=back
Only the major authors are listed above. For contributors see the
Changes file.
=cut
|