1=head1 NAME
2
3InlineX Cookbook
4
5=head1 BASIC EXAMPLE
6
7As of version 0.13 of this module, it's possible to provide the C/CPP
8code to InlineX::C2XS/InlineX::CPP2XS by supplying either the CODE or
9SRC_LOCATION arguments as key/value pairs in the hashref that forms the
10the final argument given to c2xs()/cpp2xs(). This documentation was
11first written prior to version 0.13 - I'll leave the first parts of it
12as is, but bear in mind that the CPP source can now be stored anywhere
13in any file and you just provide the name and location (either fully
14qualified or relative) of that file using the
15SRC_LOCATION=>'./location/file.ext' key/value pair ... or you can just
16store the code as one big string in a variable (say, $code) in which
17case you supply the code using the CODE=>$code key/value pair. For a
18demo of these capabilities that became available in 0.13, see the
19section "UPDATE for 0.13 and later" at the end of this document.
20
21For even more recent additions, see the section "UPDATE for 0.20 and
22later".
23
24Create a separate directory to work through this demo (let's call it
25'test' - though you can call it whatever you like), then 'cd' to the
26newly created 'test' directory, and create 2 folders in it - 'src' and
27'My-Mod-0.01'. The 'src' folder is where the CPP code gets placed (and
28the folder must be named 'src'). The 'My-Mod-0.01' folder is where the
29various files that InlineX can create will be written. (That folder does
30not have to be named 'My-Mod-0.01' - call it whatever you want. For the
31purpose of this exercise, I'm assuming you *have* named it'My-Mod-0.01').
32
33In the 'src' folder place a CPP file named Mod.cpp that contains:
34
35
36    int plus (int x, int y) {
37        return x + y;
38    }
39    int minus (int x, int y) {
40        return x - y;
41    }
42
43
44Since the module we are building in this particular exercise is named
45My::Mod, the CPP file must be named 'Mod.cpp'. That is, the .cpp file must
46have a '.cpp' extension, and it must have the same name as the .pm file.
47
48To create Mod.xs, place the following file (which I've named 'create.pl')
49in the 'test' directory:
50
51
52    use warnings;
53    use strict;
54    use InlineX::CPP2XS qw(cpp2xs);
55    my $mod = 'My::Mod';
56    my $pkg = $mod;
57    my $build_dir = './My-Mod-0.01';
58    cpp2xs($mod, $pkg, $build_dir);
59
60
61Then run create.pl.
62
63You should now find Mod.xs in test/My-Mod-0.01. We've taken care of
64one of the files that we'll need to build My::Mod. Note that INLINE.h
65was not created. That's because the CPP code in Mod.cpp does not need
66INLINE.h. Let's change that by adding the following to the bottom of
67Mod.cpp:
68
69
70    void plus_minus(int x, int y) {
71         Inline_Stack_Vars;
72         Inline_Stack_Reset;
73         Inline_Stack_Push(sv_2mortal(newSViv(x + y)));
74         Inline_Stack_Push(sv_2mortal(newSViv(x - y)));
75         Inline_Stack_Done;
76         Inline_Stack_Return(2);
77    }
78
79
80Again run create.pl
81
82This time INLINE.h is needed and you should find it, along with Mod.xs,
83in test/My-Mod-0.01.
84
85But, of course, to build the My::Mod module, you'll also need a
86Makefile.PL and a Mod.pm. So let's autogenerate them, too. It's just
87a matter of providing an extra argument to cpp2xs(). Amend create.pl to:
88
89
90    use warnings;
91    use strict;
92    use InlineX::CPP2XS qw(cpp2xs);
93    my $mod = 'My::Mod';
94    my $pkg = $mod;
95    my $build_dir = './My-Mod-0.01';
96    my $hashref = {VERSION =>0.01,
97                   WRITE_MAKEFILE_PL => 1,
98                   WRITE_PM => 1};
99    cpp2xs($mod, $pkg, $build_dir, $hashref);
100
101
102Now when you run create.pl you'll find that Makefile.PL and Mod.pm
103are also created in test/My-Mod-0.01.(Mod.pm doesn't contain any pod
104documentation - and the Makefile.PL might contain some
105machine-specific specifications that should be cleaned up before
106sending the files off to CPAN. But that's all fairly basic and
107straightforward stuff.)
108If you want all of the XSubs except those starting with a single
109underscore (but not multiple underscores) to be placed into Mod.pm's
110@EXPORT then set EXPORT_ALL=>1 in $hashref. Otherwise
111@My::Mod::EXPORT will be empty.
112If you want all of the XSubs except those starting with a single
113underscore (but not multiple underscores) to be placed into Mod.pm's
114@EXPORT_OK then set EXPORT_OK_ALL=>1 in $hashref. Otherwise
115@My::Mod::EXPORT_OK will be empty.
116And if you want to create an export tag (named, eg 'all') that includes
117all of the XSubs except those starting with a single underscore (but
118not multiple underscores) then set EXPORT_TAGS_ALL=>'all' in
119$hashref.
120
121You may also want to provide a test script, a README, a MANIFEST and
122whatever other files you like. These additional files cannot be
123autogenerated by InlineX. For a test script that works with the
124autogenerated Mod.pm, just create a file named 'test.t' in
125test/My-Mod-0.01/t (you'll need to manually create that 't'
126directory). Assuming that we haven't set either EXPORT_OK_ALL or
127EXPORT_ALL (see above), 'test.t' could look like this:
128
129    use warnings;
130    use strict;
131    use My::Mod;
132    print "1..1\n";
133    my $x = 16;
134    my $y = 12;
135    my @z = My::Mod::plus_minus($x, $y);
136    my $ok = '';
137    if(My::Mod::plus($x, $y) == 28) {$ok .= 'a'}
138    if(My::Mod::minus($x, $y) == 4) {$ok .= 'b'}
139    if($z[0] == 28 && $z[1] == 4)   {$ok .= 'c'}
140    if($ok eq 'abc') {print "ok 1\n"}
141    else {print "not ok 1 $ok\n"}
142
143If we have set EXPORT_OK_ALL, then 'test.t' could look like this:
144
145    use warnings;
146    use strict;
147    use My::Mod qw(plus minus plus_minus);
148    print "1..1\n";
149    my $x = 16;
150    my $y = 12;
151    my @z = plus_minus($x, $y);
152    my $ok = '';
153    if(plus($x, $y) == 28) {$ok .= 'a'}
154    if(minus($x, $y) == 4) {$ok .= 'b'}
155    if($z[0] == 28 && $z[1] == 4)   {$ok .= 'c'}
156    if($ok eq 'abc') {print "ok 1\n"}
157    else {print "not ok 1 $ok\n"}
158
159And if we have instead set EXPORT_ALL, then in 'test.t' we
160could replace "use My::Mod qw(plus minus plus_minus);" with
161simply "use My::Mod;"
162
163Now you can build My::Mod in the usual way (in the
164test/My-Mod-0.01 folder) by running:
165
166    perl Makefile.PL
167    make test
168
169You could even install it by running 'make install' if you want.
170
171There are other examples to be found in the demos folder of the
172InlineX-C2XS and InlineX-CPP2XS source distributions from CPAN.
173
174Some other build options are given below. (For a complete list, see
175the InlineX-C2XS/InlineX-CPP2xs documentation.) In each case it's just
176a matter of adding a key/value pair to $hashref in create.pl. No other
177changes to create.pl are necessary.
178
179**********************************************************************
180
181=head1 AUTO_INCLUDE
182
183You may need to include additional headers into the XS file. Do so by
184inserting the following key/value assignment into create.pl's $hashref:
185
186    AUTO_INCLUDE => "#include <x.h>\n#include \"y.h\"",
187
188Also, if the AUTOWRAP feature needs to parse and use these headers,
189then AUTO_INCLUDE is the way to satisfy that requirement.
190
191**********************************************************************
192
193=head1 BUILD_NOISY
194
195You'll note that during the running of create.pl there's some progress
196reports being generated by Inline::CPP. If you don't want to see those
197reports, insert the following key/value assignment into create.pl's
198$hashfef:
199
200    BUILD_NOISY => 0,
201
202**********************************************************************
203
204=head1 USING
205
206Specify an alternative parser to parse the CPP code. There are
207currently no alternative parsers available to Inline::CPP.
208
209   USING => ['ParseRegExp'], # fails on Inline::CPP & InlineX::CPP2XS
210
211**********************************************************************
212
213=head1 TYPEMAPS
214
215Let's do another demo here, based upon the "Object Oriented Inline"
216example in the (excellent) Inline::C-Cookbook - to demonstrate the
217use of typemaps as much as anything else. Back in the 'test' folder,
218create another folder named '/My-Soldier-1.02' ... so we've now got
219folders named test/src, test/My-Mod-0.01 and test/My-Soldier-1.02.
220
221Create a file named 'Soldier.cpp' as follows (and place it in test/src):
222
223  typedef struct {
224    char* name;
225    char* rank;
226    long  serial;
227  } Soldier;
228
229  Soldier * new(char* class, char* name, char* rank, long serial) {
230      Soldier* soldier;
231      New(42, soldier, 1, Soldier);
232
233      soldier->name = savepv(name);
234      soldier->rank = savepv(rank);
235      soldier->serial = serial;
236
237      return soldier;
238  }
239
240  char* get_name(Soldier * obj) {
241        return obj->name;
242  }
243
244  char* get_rank(Soldier * obj) {
245        return obj->rank;
246  }
247
248  long get_serial(Soldier * obj) {
249       return obj->serial;
250  }
251
252  void DESTROY(Soldier* obj) {
253       Safefree(obj->name);
254       Safefree(obj->rank);
255       Safefree(obj);
256  }
257
258
259Note that new() returns a Soldier*. Perl doesn't know anything about
260the Soldier* type ... so we need to provide a typemap with an 'OUTPUT'
261section that tells perl how to deal with this type.
262Note also that the other functions take (as their argument) a Soldier*.
263Again, since perl doesn't know anything about the Soldier* type, we
264need to provide a typemap with an 'INPUT' section that tells perl
265what to do. Here's one such typemap that satisfies both requirements:
266
267
268 Soldier *    SOLDIER
269
270 INPUT
271 SOLDIER
272 	$var = INT2PTR($type, SvIV(SvRV($arg)))
273
274 OUTPUT
275 SOLDIER
276 	$arg     = newSViv(0);
277 	$arg     = newSViv(0);
278	sv_setiv(newSVrv($arg, \"Soldier\"), (IV)$var);
279	SvREADONLY_on(SvRV($arg));
280	$arg;
281
282
283Save that file as test/My-Soldier-1.02/typemap and place a copy of it
284in 'test'. Note that, in addition to our typemap being needed when we
285compile the My::Soldier module, the cpp2xs function also needs it in
286order to write a correct XS file. That's why we've placed a copy of
287the typemap in both 'test' and 'test/My-Soldier-1.02' - so that both
288processes will be able to find it. A better solution is to have the
289TYPEMAPS entry in $hashref provide a fully qualified (absolute) path
290to the file, rather than a relative path. (Unfortunately, I don't
291know what that absolute path will be on your machine.)
292
293I guess we'll also need a test file to test our new Soldier module.
294So save the following as test/My-Soldier-1.02/t/test.t:
295
296    use warnings;
297    use strict;
298    use My::Soldier;
299    print "1..1'\n";
300    my $obj1 = My::Soldier->new('Benjamin', 'Private', 11111);
301    my $obj2 = My::Soldier->new('Sanders', 'Colonel', 22222);
302    my $obj3 = My::Soldier->new('Matt', 'Sergeant', 33333);
303    my $ok = '';
304    if($obj1->My::Soldier::get_name() eq 'Benjamin') {$ok .= 'a'}
305    if($obj2->My::Soldier::get_name() eq 'Sanders') {$ok .= 'b'}
306    if($obj3->My::Soldier::get_name() eq 'Matt') {$ok .= 'c'}
307    if($obj1->My::Soldier::get_rank() eq 'Private') {$ok .= 'd'}
308    if($obj2->My::Soldier::get_rank() eq 'Colonel') {$ok .= 'e'}
309    if($obj3->My::Soldier::get_rank() eq 'Sergeant') {$ok .= 'f'}
310    if($obj1->My::Soldier::get_serial() == 11111) {$ok .= 'g'}
311    if($obj2->My::Soldier::get_serial() == 22222) {$ok .= 'h'}
312    if($obj3->My::Soldier::get_serial() == 33333) {$ok .= 'i'}
313    if($ok eq 'abcdefghi') {print "ok 1\n"}
314    else {print "not ok 1 $ok\n"}
315
316Now we just need to rewrite test/create.pl appropriately:
317
318    use warnings;
319    use strict;
320    use InlineX::CPP2XS qw(cpp2xs);
321    my $mod = 'My::Soldier';
322    my $pkg = $mod;
323    my $build_dir = './My-Soldier-1.02';
324    my $hashref = {VERSION =>1.02,
325                   WRITE_MAKEFILE_PL => 1,
326                   TYPEMAPS => ['./typemap'],
327                   WRITE_PM => 1};
328    cpp2xs($mod, $pkg, $build_dir, $hashref);
329
330So now ... it's just a matter of running create.pl - then 'cd' to
331test/My-Soldier-1.02 and run:
332
333  perl Makefile.PL
334  make test
335
336(and 'make install' if you actually want to install this module.)
337
338**********************************************************************
339
340=head1 UPDATE for 0.13 and later
341
342Returning to the first example code we used at the beginning of this
343cookbook, with version 0.13 or later, we don't need to have the source
344in ./src/Mod.cpp. (We can still do it that way, but we don't *have*
345to.)
346
347We could, for example, put the code that was written into './src/Mod.cpp'
348into '/home/me/file.ext' instead. Then, in order to create Mod.xs,
349Mod.pm, and Makefile.PL in './My-Mod-0.01', we need 'create.pl' to
350look like this:
351
352
353    use warnings;
354    use strict;
355    use InlineX::CPP2XS qw(cpp2xs);
356    my $mod = 'My::Mod';
357    my $pkg = $mod;
358    my $build_dir = './My-Mod-0.01';
359    my $hashref = {VERSION =>0.01,
360                   WRITE_MAKEFILE_PL => 1,
361                   WRITE_PM => 1,
362                   SRC_LOCATION => '/home/me/file.ext'};
363    cpp2xs($mod, $pkg, $build_dir, $hashref);
364
365
366Another alternative is to put the code in a variable - in which
367case 'create.pl' is as follows:
368
369
370    use warnings;
371    use strict;
372    use InlineX::CPP2XS qw(cpp2xs);
373    my $mod = 'My::Mod';
374    my $pkg = $mod;
375    my $build_dir = './My-Mod-0.01';
376    my $code = "
377    int plus (int x, int y) {
378        return x + y;
379    }
380    int minus (int x, int y) {
381        return x - y;
382    }
383
384    void plus_minus(int x, int y) {
385         Inline_Stack_Vars;
386         Inline_Stack_Reset;
387         Inline_Stack_Push(sv_2mortal(newSViv(x + y)));
388         Inline_Stack_Push(sv_2mortal(newSViv(x - y)));
389         Inline_Stack_Done;
390         Inline_Stack_Return(2);
391    }
392    ";
393    my $hashref = {VERSION =>0.01,
394                   WRITE_MAKEFILE_PL => 1,
395                   WRITE_PM => 1,
396                   CODE => $code};
397    cpp2xs($mod, $pkg, $build_dir, $hashref);
398
399
400**********************************************************************
401
402=head1 "UPDATE for 0.20 and later"
403
404With version 0.20, a 'cpp2xs' utility was provided - it provides a less
405cumbersome method of accessing the cpp2xs functionality.
406(See 'cpp2xs --help')
407
408Version 0.21 adds the capability of writing a portable Makefile.PL (and
409xs file) via the config option WRITE_MAKEFILE_PL=>'p'.
410
411And, in the source distro, there's now a nice demo featuring proper C++
412code from David Oswald's Math::Prime::FastSieve module(as opposed to the
413C code that populates this cookbook). See demos/cpp2xs_utility/README in
414the 0.21 (and later) source distro.
415
416
417**********************************************************************
418
419=cut
420
421
422
423