1FFI::Platypus::Bundle(3U)ser Contributed Perl DocumentatiFoFnI::Platypus::Bundle(3)
2
3
4
6 FFI::Platypus::Bundle - Bundle foreign code with your Perl module
7
9 version 2.05
10
12 "ffi/foo.c":
13
14 #include <ffi_platypus_bundle.h>
15 #include <string.h>
16
17 typedef struct {
18 char *name;
19 int value;
20 } foo_t;
21
22 foo_t*
23 foo__new(const char *class_name, const char *name, int value) {
24 (void)class_name;
25 foo_t *self = malloc( sizeof( foo_t ) );
26 self->name = strdup(name);
27 self->value = value;
28 return self;
29 }
30
31 const char *
32 foo__name(foo_t *self) {
33 return self->name;
34 }
35
36 int
37 foo__value(foo_t *self) {
38 return self->value;
39 }
40
41 void
42 foo__DESTROY(foo_t *self) {
43 free(self->name);
44 free(self);
45 }
46
47 "lib/Foo.pm":
48
49 package Foo;
50
51 use strict;
52 use warnings;
53 use FFI::Platypus 2.00;
54
55 my $ffi = FFI::Platypus->new( api => 2 );
56
57 $ffi->type('object(Foo)' => 'foo_t');
58 $ffi->mangler(sub {
59 my $name = shift;
60 $name =~ s/^/foo__/;
61 $name;
62 });
63
64 $ffi->bundle;
65
66 $ffi->attach( new => [ 'string', 'string', 'int' ] => 'foo_t' );
67 $ffi->attach( name => [ 'foo_t' ] => 'string' );
68 $ffi->attach( value => [ 'foo_t' ] => 'int' );
69 $ffi->attach( DESTROY => [ 'foo_t' ] => 'void' );
70
71 1;
72
73 "t/foo.t"
74
75 use Test2::V0;
76 use Foo;
77
78 my $foo = Foo->new("platypus", 10);
79 isa_ok $foo, 'Foo';
80 is $foo->name, "platypus";
81 is $foo->value, 10;
82
83 done_testing;
84
85 "Makefile.PL":
86
87 use ExtUtils::MakeMaker;
88 use FFI::Build::MM;
89 my $fbmm = FFI::Build::MM->new;
90 WriteMakefile(
91 $fbmm->mm_args(
92 NAME => 'Foo',
93 DISTNAME => 'Foo',
94 VERSION => '1.00',
95 # ...
96 )
97 );
98
99 sub MY::postamble
100 {
101 $fbmm->mm_postamble;
102 }
103
104 or "dist.ini":
105
106 name = Foo
107 version = 0.01
108 ...
109
110 [FFI::Build]
111 version = 1.04
112
114 This document serves as a tutorial for using the new bundling interface
115 provided by FFI::Platypus as of api version 1. It requires
116 FFI::Platypus of at least 1.00.
117
118 Sometimes when writing FFI bindings you need to include a little C code
119 (or your favorite compiled language) to finish things off.
120 Alternatively, you might just want to write some C code (or your
121 favorite compiled language) to include with your Perl module to make a
122 tight loop faster. The bundling interface has you covered.
123
124 Basic example
125 To illustrate we will go through the files in the synopsis and explain
126 how and why they work. To start with we have some C code which
127 emulates object oriented code using "foo__" as a prefix. We use a C
128 struct that we call "foo_t" to store our object data. On the C level
129 the struct acts as a class, when combined with its functions that act
130 as methods. The constructor just allocates the memory it needs for the
131 "foo_t" instance, fills in the appropriate fields and returns the
132 pointer:
133
134 foo_t*
135 foo__new(const char *class_name, const char *name, int value)
136 {
137 (void) class_name;
138 foo_t *self = malloc( sizeof( foo_t ) );
139 self->name = strdup(name);
140 self->value = value;
141 return self;
142 }
143
144 We include a class name as the first argument, because Perl will
145 include that when calling the constructor, but we do not use it here.
146 An exercise for the reader would be to add hierarchical inheritance.
147
148 There are also some methods which return member values. This class has
149 only read only members, but you could have read/write or other methods
150 depending on your needs.
151
152 const char *
153 foo__name(foo_t *self)
154 {
155 return self->name;
156 }
157
158 We also include a destructor so that the memory owned by the object can
159 be freed when it is no longer needed.
160
161 void
162 foo__DESTROY(foo_t *self)
163 {
164 free(self->name);
165 free(self);
166 }
167
168 This might start to look a little like a Perl module, and when we look
169 at the Perl code that binds to this code, you will see why. First lets
170 prepare the FFI::Platypus instance and specify the correct api version:
171
172 my $ffi = FFI::Platypus->new( api => 2 );
173
174 The bundle interface is only supported with api version 1, so if you
175 try to use version 0 it will not work. Next we define an object type
176 for "foo_t" which will associate it with the Perl class "Foo".
177
178 $ffi->type('object(Foo)' => 'foo_t');
179
180 As object type is a blessed reference to an opaque (default) or integer
181 type which can be used as a Perl object. Platypus does the translating
182 of Perl object to and from the foo_t pointers that the C code
183 understands. For more details on Platypus types see
184 FFI::Platypus::Type.
185
186 Next we set the mangler on the Platypus instance so that we can refer
187 to function names without the "foo__" prefix. You could just not use
188 the prefix in your C code and skip this step, or you could refer to the
189 function names in their full in your Perl code, however, this saves
190 extra typing and allows you to bundle more than one class with your
191 Perl code without having to worry about name conflicts.
192
193 $ffi->mangler(sub {
194 my $name = shift;
195 $name =~ s/^/foo__/;
196 $name;
197 });
198
199 Finally we let Platypus know that we will be bundling code.
200
201 $ffi->bundle;
202
203 By default, this searches for the appropriate place for your dynamic
204 libraries using the current package. In some cases you may need to
205 override this, for example if your dist is named "Foo-Bar" but your
206 specific class is named "Foo::Bar::Baz", you'd want something like
207 this:
208
209 package Foo::Bar::Baz;
210 use FFI::Platypus 2.00;
211 my $ffi = FFI::Platypus->new( api => 2 );
212 $ffi->bundle('Foo::Bar');
213 ...
214
215 Now, finally we can attach the methods for our class:
216
217 $ffi->attach( new => [ 'string', 'int' ] => 'foo_t' );
218 $ffi->attach( name => [ 'foo_t' ] => 'string' );
219 $ffi->attach( value => [ 'foo_t' ] => 'int' );
220 $ffi->attach( DESTROY => [ 'foo_t' ] => 'void' );
221
222 Note that we do not have to include the "foo__" prefix because of the
223 way we set up the mangler. If we hadn't done that then we could
224 instead attach with the full names:
225
226 $ffi->attach( [ 'foo__new' => 'new' ] => [ 'string', 'int' ] => 'foo_t' );
227 $ffi->attach( [ 'foo__name' => 'name' ] => [ 'foo_t' ] => 'string' );
228 ...
229
230 You're done! You can now use this class. Lets write a test to make
231 sure it works,
232
233 use Test2::V0;
234 use Foo;
235
236 my $foo = Foo->new("platypus", 10);
237 isa_ok $foo, 'Foo';
238 is $foo->name, "platypus";
239 is $foo->value, 10;
240
241 done_testing;
242
243 and use "prove" to check that it works:
244
245 % prove -lvm
246 t/foo.t ..
247 ok 1 - An object of class 'Foo' isa 'Foo'
248 ok 2
249 ok 3
250 1..3
251 ok
252 All tests successful.
253 Files=1, Tests=3, 0 wallclock secs ( 0.02 usr 0.00 sys + 0.14 cusr 0.03 csys = 0.19 CPU)
254 Result: PASS
255
256 Platypus automatically compiles and links the dynamic library for you:
257
258 % ls ffi/_build
259 foo.c.o libFoo.so
260
261 The C code will be rebuilt next time if the source code is newer than
262 the object or dynamic libraries files. If the source files are not
263 changed, then it won't be rebuilt to save time. If you are using the
264 code without MakeMaker, or another build system you are responsible for
265 cleaning up these files. This is intended as a convenience to allow
266 you to test your code without having to invoke MakeMaker, or "dzil" or
267 whatever build system you are using.
268
269 When you distribute your module though, you will want the dynamic
270 library built just once at build-time and installed correctly so that
271 it can be found at run-time. You don't need to make any changes to
272 your C or Perl code, but you do need to tell MakeMaker to build and
273 install the appropriate files using FFI::Build::MM:
274
275 use ExtUtils::MakeMaker;
276 use FFI::Build::MM;
277 my $fbmm = FFI::Build::MM->new;
278 WriteMakefile(
279 $fbmm->mm_args(
280 NAME => 'Foo',
281 DISTNAME => 'Foo',
282 VERSION => '1.00',
283 # ...
284 )
285 );
286
287 sub MY::postamble
288 {
289 $fbmm->mm_postamble;
290 }
291
292 And we can invoke all the normal MakeMaker style stuff and our C code
293 will be compiled, linked and installed at the appropriate steps.
294
295 % perl Makefile.PL
296 Generating a Unix-style Makefile
297 Writing Makefile for Foo
298 Writing MYMETA.yml and MYMETA.json
299 % make
300 cp lib/Foo.pm blib/lib/Foo.pm
301 "/Users/ollisg/perl5/perlbrew/perls/perl-5.30.0/bin/perl" -MFFI::Build::MM=cmd -e fbx_build
302 CC ffi/foo.c
303 LD blib/lib/auto/share/dist/Foo/lib/libFoo.dylib
304 % make test
305 "/Users/ollisg/perl5/perlbrew/perls/perl-5.30.0/bin/perl" -MFFI::Build::MM=cmd -e fbx_build
306 "/Users/ollisg/perl5/perlbrew/perls/perl-5.30.0/bin/perl" -MFFI::Build::MM=cmd -e fbx_test
307 PERL_DL_NONLAZY=1 "/Users/ollisg/perl5/perlbrew/perls/perl-5.30.0/bin/perl" "-MExtUtils::Command::MM" "-MTest::Harness" "-e" "undef *Test::Harness::Switches; test_harness(0, 'blib/lib', 'blib/arch')" t/*.t
308 t/foo.t .. ok
309 All tests successful.
310 Files=1, Tests=3, 0 wallclock secs ( 0.01 usr 0.00 sys + 0.06 cusr 0.01 csys = 0.08 CPU)
311 Result: PASS
312
313 If the "Makefile.PL" file above looks overly complicated, you can use
314 the Dist::Zilla::Plugin::FFI::Build plugin to simplify your life if you
315 are using Dist::Zilla:
316
317 [FFI::Build]
318 version = 1.04
319
320 Specifying version 1.04 will ensure that any ".o" or ".so" files are
321 pruned from your build tree and not distributed by mistake.
322
323 Initialization example
324 The bundle interface also gives you entry points which will be called
325 automatically when your code is loaded and unloaded if they are found.
326
327 "ffi_pl_bundle_init"
328 void ffi_pl_bundle_init(const char *package, int argc, void *argv[]);
329
330 Called when the dynamic library is loaded. "package" is the Perl
331 package that called "bundle" from Perl space. "argc" and "argv"
332 represents an array of opaque pointers that can be passed as an
333 array to bundle as the last argument. (the count "argc" is a
334 little redundant because "argv" is also NULL terminated).
335
336 "ffi_pl_bundle_constant"
337 void ffi_pl_bundle_constant(const char *package, ffi_platypus_constant_t *c);
338
339 Called immediately after "ffi_pl_bundle_init", and is intended to
340 allow you to set Perl constants from C space. For details on how
341 this works and what methods you can call on the
342 "ffi_platypus_constant_t" instance, see FFI::Platypus::Constant.
343
344 "ffi_pl_bundle_fini"
345 void ffi_pl_bundle_fini(const char *package);
346
347 Called when the dynamic library is unloaded. "package" is the Perl
348 package that called "bundle" from Perl space when the library was
349 loaded. CAVEAT: if you attach any functions then this will never
350 be called, because attaching functions locks the Platypus instance
351 into memory along with the libraries which it is using.
352
353 Here is an example that passes the version and a callback back into
354 Perl space that emulates the Perl 5.10 "say" feature.
355
356 "ffi/init.c":
357
358 #include <ffi_platypus_bundle.h>
359
360 char buffer[512];
361 const char *version;
362 void (*say)(const char *);
363
364 void
365 ffi_pl_bundle_init(const char *package, int argc, void *argv[])
366 {
367 version = argv[0];
368 say = argv[1];
369
370 say("in init!");
371
372 snprintf(buffer, 512, "package = %s, version = %s", package, version);
373 say(buffer);
374
375 snprintf(buffer, 512, "args = %d", argc);
376 say(buffer);
377 }
378
379 void
380 ffi_pl_bundle_fini(const char *package)
381 {
382 say("in fini!");
383 }
384
385 "lib/Init.pm":
386
387 package Init;
388
389 use strict;
390 use warnings;
391 use FFI::Platypus 2.00;
392
393 our $VERSION = '1.00';
394
395 {
396 my $ffi = FFI::Platypus->new( api => 2 );
397
398 my $say = $ffi->closure(sub {
399 my $string = shift;
400 print "$string\n";
401 });
402
403 $ffi->bundle([
404 $ffi->cast( 'string' => 'opaque', $VERSION ),
405 $ffi->cast( '(string)->void' => 'opaque', $say ),
406 ]);
407
408 undef $ffi;
409 undef $say;
410 }
411
412 1;
413
414 The deinitialization order for the $say callback and the $ffi instance
415 is essential here, so we do it manually with "undef":
416
417 undef $ffi;
418 undef $say;
419
420 First we deallocate $ffi which calls "ffi_pl_bundle_fini", which calls
421 $say, so we want to make sure the latter is still allocated. Once
422 "ffi_pl_bundle_fini" is done, we can safely deallocate $say.
423
424 If "ffi_pl_bundle_fini" didn't call back into Perl space like this then
425 we don't have to be as careful about deallocating things in Perl space.
426
427 Compiler or linker flags example
428 There are times when you will want to specify your own compiler and
429 linker flags for the C code that you are bundling. The "TL;DR" is that
430 you can put a ".fbx" file in your "ffi" directory. This is a Perl
431 script that returns a hash reference that is passed into the FFI::Build
432 constructor. This allows you to set a number of options, including
433 compiler and linker flags. A more detailed example follows:
434
435 You may want or need to set compiler and linker flags for your bundled
436 C code. For example, say we have a header file, but instead of putting
437 it in the "ffi" directory we want to put it in a separate directory
438 called "include".
439
440 "include/answer.h":
441
442 #ifndef ANSWER_H
443 #define ANSWER_H
444
445 int answer(void);
446
447 #endif
448
449 "ffi/answer.c":
450
451 int
452 answer(void)
453 {
454 /* the answer to life the universe and everything */
455 return 42;
456 }
457
458 "lib/Answer.pm":
459
460 package Answer;
461
462 use strict;
463 use warnings;
464 use FFI::Platypus 2.00;
465 use Exporter qw( import );
466
467 our @EXPORT = qw( answer );
468
469 my $ffi = FFI::Platypus->new( api => 2 );
470 $ffi->bundle;
471 $ffi->attach( answer => [] => 'int' );
472
473 1;
474
475 If you try to use this module just as-is you will get an error, about
476 not being able to find the header file. Probably something like this:
477
478 ffi/answer.c:1:10: fatal error: 'answer.h' file not found
479
480 So we put a "answer.fbx" file in the "ffi" directory. (In case you are
481 wondering FBX stands for "Ffi Build and file eXtensions should whenever
482 possible be three characters long"). The name of the file can be
483 anything so long as it ends in ".fbx", we just choose "answer" here
484 because that is the name of the project.
485
486 "ffi/answer.fbx":
487
488 our $DIR;
489
490 return {
491 cflags => "-I/include",
492 source => "$DIR/*.c",
493 }
494
495 The $DIR variable is provided by the builder code. It is the root of
496 the distribution, and is helpful if you need a fully qualified path.
497 In this case you could have also used "ffi/*.c".
498
499 The script returns a hash reference which is passed into the FFI::Build
500 constructor, so you can use any of the options supported by that class.
501 Now we should be able to use our bundled module:
502
503 % perl -Ilib -MAnswer=answer -E 'say answer'
504 42
505
506 Using bundled code with Alien.
507 A useful technique is to use Platypus with Alien technology. The Alien
508 namespace is reserved for providing external non-Perl dependencies for
509 CPAN modules. The nominal Alien module when installed looks for the
510 library locally, and if it can't be found it fetches it from the
511 internet, builds it, and installs it in a private directory so that it
512 can be used by other CPAN modules. For Aliens that provide shared
513 libraries, and that have simple interfaces that do not require
514 additional C code you can easily just pass the shared libraries to
515 Platypus directly. For modules that require some bundled C code and an
516 Alien you have to link the Alien library with your bundled code. If
517 the Alien uses the Alien::Base interface then all you have to do is
518 give the name of the Alien to FFI::Build.
519
520 For example, the "bzip2" library provides an interface that requires
521 the caller to allocate a C "struct" and then pass it to its various
522 functions. The "struct" is actually pretty simple and you could use
523 FFI::C or FFI::Platypus::Record, but here is an example of how you
524 would connect bundled C code with an Alien.
525
526 "ffi/compress.c":
527
528 #include <bzlib.h>
529 #include <stdlib.h>
530
531 int
532 bzip2__new(bz_stream **stream, int blockSize100k, int verbosity, int workFactor )
533 {
534 *stream = malloc(sizeof(bz_stream));
535 (*stream)->bzalloc = NULL;
536 (*stream)->bzfree = NULL;
537 (*stream)->opaque = NULL;
538
539 return BZ2_bzCompressInit(*stream, blockSize100k, verbosity, workFactor );
540 }
541
542 "lib/Bzip2.pm":
543
544 package Bzip2;
545
546 use strict;
547 use warnings;
548 use FFI::Platypus 2.00;
549 use FFI::Platypus::Memory qw( free );
550
551 my $ffi = FFI::Platypus->new( api => 2 );
552 $ffi->bundle;
553
554 $ffi->mangler(sub {
555 my $name = shift;
556 $name =~ s/^/bzip2__/ unless $name =~ /^BZ2_/;
557 $name;
558 });
559
560 =head2 new
561
562 my $bzip2 = Bzip2->new($block_size_100k, $verbosity, $work_flow);
563
564 =cut
565
566 $ffi->attach( new => ['opaque*', 'int', 'int', 'int'] => 'int' => sub {
567 my $xsub = shift;
568 my $class = shift;
569 my $ptr;
570 my $ret = $xsub->(\$ptr, @_);
571 return bless \$ptr, $class;
572 });
573
574 $ffi->attach( [ BZ2_bzCompressEnd => 'DESTROY' ] => ['opaque'] => 'int' => sub {
575 my $xsub = shift;
576 my $self = shift;
577 my $ret = $xsub->($$self);
578 free $$self;
579 });
580
581 1;
582
583 The ".fbx" file that goes with this to make it work with Alien::Libbz2
584 is now pretty trivial:
585
586 "ffi/bz2.fbx":
587
588 {
589 alien => ['Alien::Libbz2'],
590 source => ['ffi/*.c'],
591 };
592
594 Author: Graham Ollis <plicease@cpan.org>
595
596 Contributors:
597
598 Bakkiaraj Murugesan (bakkiaraj)
599
600 Dylan Cali (calid)
601
602 pipcet
603
604 Zaki Mughal (zmughal)
605
606 Fitz Elliott (felliott)
607
608 Vickenty Fesunov (vyf)
609
610 Gregor Herrmann (gregoa)
611
612 Shlomi Fish (shlomif)
613
614 Damyan Ivanov
615
616 Ilya Pavlov (Ilya33)
617
618 Petr Písař (ppisar)
619
620 Mohammad S Anwar (MANWAR)
621
622 Håkon Hægland (hakonhagland, HAKONH)
623
624 Meredith (merrilymeredith, MHOWARD)
625
626 Diab Jerius (DJERIUS)
627
628 Eric Brine (IKEGAMI)
629
630 szTheory
631
632 José Joaquín Atria (JJATRIA)
633
634 Pete Houston (openstrike, HOUSTON)
635
637 This software is copyright (c) 2015-2022 by Graham Ollis.
638
639 This is free software; you can redistribute it and/or modify it under
640 the same terms as the Perl 5 programming language system itself.
641
642
643
644perl v5.36.0 2022-11-18 FFI::Platypus::Bundle(3)