1FFI::Platypus::Bundle(3U)ser Contributed Perl DocumentatiFoFnI::Platypus::Bundle(3)
2
3
4

NAME

6       FFI::Platypus::Bundle - Bundle foreign code with your Perl module
7

VERSION

9       version 1.56
10

SYNOPSIS

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        {
25          (void)class_name;
26          foo_t *self = malloc( sizeof( foo_t ) );
27          self->name = strdup(name);
28          self->value = value;
29          return self;
30        }
31
32        const char *
33        foo__name(foo_t *self)
34        {
35          return self->name;
36        }
37
38        int
39        foo__value(foo_t *self)
40        {
41          return self->value;
42        }
43
44        void
45        foo__DESTROY(foo_t *self)
46        {
47          free(self->name);
48          free(self);
49        }
50
51       "lib/Foo.pm":
52
53        package Foo;
54
55        use strict;
56        use warnings;
57        use FFI::Platypus 1.00;
58
59        {
60          my $ffi = FFI::Platypus->new( api => 1 );
61
62          $ffi->type('object(Foo)' => 'foo_t');
63          $ffi->mangler(sub {
64            my $name = shift;
65            $name =~ s/^/foo__/;
66            $name;
67          });
68
69          $ffi->bundle;
70
71          $ffi->attach( new =>     [ 'string', 'string', 'int' ] => 'foo_t'  );
72          $ffi->attach( name =>    [ 'foo_t' ]                   => 'string' );
73          $ffi->attach( value =>   [ 'foo_t' ]                   => 'int'    );
74          $ffi->attach( DESTROY => [ 'foo_t' ]                   => 'void'   );
75        }
76
77        1;
78
79       "t/foo.t"
80
81        use Test2::V0;
82        use Foo;
83
84        my $foo = Foo->new("platypus", 10);
85        isa_ok $foo, 'Foo';
86        is $foo->name, "platypus";
87        is $foo->value, 10;
88
89        done_testing;
90
91       "Makefile.PL":
92
93        use ExtUtils::MakeMaker;
94        use FFI::Build::MM;
95        my $fbmm = FFI::Build::MM->new;
96        WriteMakefile(
97          $fbmm->mm_args(
98            NAME     => 'Foo',
99            DISTNAME => 'Foo',
100            VERSION  => '1.00',
101            # ...
102          )
103        );
104
105        sub MY::postamble
106        {
107          $fbmm->mm_postamble;
108        }
109
110       or "dist.ini":
111
112        name    = Foo
113        version = 0.01
114        ...
115
116        [FFI::Build]
117        version = 1.04
118

DESCRIPTION

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

AUTHOR

602       Author: Graham Ollis <plicease@cpan.org>
603
604       Contributors:
605
606       Bakkiaraj Murugesan (bakkiaraj)
607
608       Dylan Cali (calid)
609
610       pipcet
611
612       Zaki Mughal (zmughal)
613
614       Fitz Elliott (felliott)
615
616       Vickenty Fesunov (vyf)
617
618       Gregor Herrmann (gregoa)
619
620       Shlomi Fish (shlomif)
621
622       Damyan Ivanov
623
624       Ilya Pavlov (Ilya33)
625
626       Petr Písař (ppisar)
627
628       Mohammad S Anwar (MANWAR)
629
630       Håkon Hægland (hakonhagland, HAKONH)
631
632       Meredith (merrilymeredith, MHOWARD)
633
634       Diab Jerius (DJERIUS)
635
636       Eric Brine (IKEGAMI)
637
638       szTheory
639
640       José Joaquín Atria (JJATRIA)
641
642       Pete Houston (openstrike, HOUSTON)
643
645       This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham
646       Ollis.
647
648       This is free software; you can redistribute it and/or modify it under
649       the same terms as the Perl 5 programming language system itself.
650
651
652
653perl v5.34.0                      2021-10-29          FFI::Platypus::Bundle(3)
Impressum