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