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.43
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 Test::More;
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 strict;
240        use warnings;
241        use Test::More;
242        use Foo;
243
244        my $foo = Foo->new("platypus", 10);
245        isa_ok $foo, 'Foo';
246        is $foo->name, "platypus";
247        is $foo->value, 10;
248
249        done_testing;
250
251       and use "prove" to check that it works:
252
253        % prove -lvm
254        t/foo.t ..
255        ok 1 - An object of class 'Foo' isa 'Foo'
256        ok 2
257        ok 3
258        1..3
259        ok
260        All tests successful.
261        Files=1, Tests=3,  0 wallclock secs ( 0.02 usr  0.00 sys +  0.14 cusr  0.03 csys =  0.19 CPU)
262        Result: PASS
263
264       Platypus automatically compiles and links the dynamic library for you:
265
266        % ls ffi/_build
267        foo.c.o  libFoo.so
268
269       The C code will be rebuilt next time if the source code is newer than
270       the object or dynamic libraries files.  If the source files are not
271       changed, then it won't be rebuilt to save time.  If you are using the
272       code without MakeMaker, or another build system you are responsible for
273       cleaning up these files.  This is intended as a convenience to allow
274       you to test your code without having to invoke MakeMaker, or "dzil" or
275       whatever build system you are using.
276
277       When you distribute your module though, you will want the dynamic
278       library built just once at build-time and installed correctly so that
279       it can be found at run-time.  You don't need to make any changes to
280       your C or Perl code, but you do need to tell MakeMaker to build and
281       install the appropriate files using FFI::Build::MM:
282
283        use ExtUtils::MakeMaker;
284        use FFI::Build::MM;
285        my $fbmm = FFI::Build::MM->new;
286        WriteMakefile(
287          $fbmm->mm_args(
288            NAME     => 'Foo',
289            DISTNAME => 'Foo',
290            VERSION  => '1.00',
291            # ...
292          )
293        );
294
295        sub MY::postamble
296        {
297          $fbmm->mm_postamble;
298        }
299
300       And we can invoke all the normal MakeMaker style stuff and our C code
301       will be compiled, linked and installed at the appropriate steps.
302
303        % perl Makefile.PL
304        Generating a Unix-style Makefile
305        Writing Makefile for Foo
306        Writing MYMETA.yml and MYMETA.json
307        % make
308        cp lib/Foo.pm blib/lib/Foo.pm
309        "/Users/ollisg/perl5/perlbrew/perls/perl-5.30.0/bin/perl" -MFFI::Build::MM=cmd -e fbx_build
310        CC ffi/foo.c
311        LD blib/lib/auto/share/dist/Foo/lib/libFoo.dylib
312        % make test
313        "/Users/ollisg/perl5/perlbrew/perls/perl-5.30.0/bin/perl" -MFFI::Build::MM=cmd -e fbx_build
314        "/Users/ollisg/perl5/perlbrew/perls/perl-5.30.0/bin/perl" -MFFI::Build::MM=cmd -e fbx_test
315        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
316        t/foo.t .. ok
317        All tests successful.
318        Files=1, Tests=3,  0 wallclock secs ( 0.01 usr  0.00 sys +  0.06 cusr  0.01 csys =  0.08 CPU)
319        Result: PASS
320
321       If the "Makefile.PL" file above looks overly complicated, you can use
322       the Dist::Zilla::Plugin::FFI::Build plugin to simplify your life if you
323       are using Dist::Zilla:
324
325        [FFI::Build]
326        version = 1.04
327
328       Specifying version 1.04 will ensure that any ".o" or ".so" files are
329       pruned from your build tree and not distributed by mistake.
330
331   Initialization example
332       The bundle interface also gives you entry points which will be called
333       automatically when your code is loaded and unloaded if they are found.
334
335       "ffi_pl_bundle_init"
336            void ffi_pl_bundle_init(const char *package, int argc, void *argv[]);
337
338           Called when the dynamic library is loaded.  "package" is the Perl
339           package that called "bundle" from Perl space.  "argc" and "argv"
340           represents an array of opaque pointers that can be passed as an
341           array to bundle as the last argument.  (the count "argc" is a
342           little redundant because "argv" is also NULL terminated).
343
344       "ffi_pl_bundle_constant"
345            void ffi_pl_bundle_constant(const char *package, ffi_platypus_constant_t *c);
346
347           Called immediately after "ffi_pl_bundle_init", and is intended to
348           allow you to set Perl constants from C space.  For details on how
349           this works and what methods you can call on the
350           "ffi_platypus_constant_t" instance, see FFI::Platypus::Constant.
351
352       "ffi_pl_bundle_fini"
353            void ffi_pl_bundle_fini(const char *package);
354
355           Called when the dynamic library is unloaded.  "package" is the Perl
356           package that called "bundle" from Perl space when the library was
357           loaded.  CAVEAT: if you attach any functions then this will never
358           be called, because attaching functions locks the Platypus instance
359           into memory along with the libraries which it is using.
360
361       Here is an example that passes the version and a callback back into
362       Perl space that emulates the Perl 5.10 "say" feature.
363
364       "ffi/init.c":
365
366        #include <ffi_platypus_bundle.h>
367
368        char buffer[512];
369        const char *version;
370        void (*say)(const char *);
371
372        void
373        ffi_pl_bundle_init(const char *package, int argc, void *argv[])
374        {
375          version = argv[0];
376          say     = argv[1];
377
378          say("in init!");
379
380          snprintf(buffer, 512, "package = %s, version = %s", package, version);
381          say(buffer);
382
383          snprintf(buffer, 512, "args = %d", argc);
384          say(buffer);
385        }
386
387        void
388        ffi_pl_bundle_fini(const char *package)
389        {
390          say("in fini!");
391        }
392
393       "lib/Init.pm":
394
395        package Init;
396
397        use strict;
398        use warnings;
399        use FFI::Platypus 1.00;
400
401        our $VERSION = '1.00';
402
403        {
404          my $ffi = FFI::Platypus->new( api => 1 );
405
406          my $say = $ffi->closure(sub {
407            my $string = shift;
408            print "$string\n";
409          });
410
411          $ffi->bundle([
412            $ffi->cast( 'string' => 'opaque', $VERSION ),
413            $ffi->cast( '(string)->void' => 'opaque', $say ),
414          ]);
415
416          undef $ffi;
417          undef $say;
418        }
419
420        1;
421
422       The deinitialization order for the $say callback and the $ffi instance
423       is essential here, so we do it manually with "undef":
424
425        undef $ffi;
426        undef $say;
427
428       First we deallocate $ffi which calls "ffi_pl_bundle_fini", which calls
429       $say, so we want to make sure the latter is still allocated.  Once
430       "ffi_pl_bundle_fini" is done, we can safely deallocate $say.
431
432       If "ffi_pl_bundle_fini" didn't call back into Perl space like this then
433       we don't have to be as careful about deallocating things in Perl space.
434
435   Compiler or linker flags example
436       There are times when you will want to specify your own compiler and
437       linker flags for the C code that you are bundling.  The "TL;DR" is that
438       you can put a ".fbx" file in your "ffi" directory.  This is a Perl
439       script that returns a hash reference that is passed into the FFI::Build
440       constructor.  This allows you to set a number of options, including
441       compiler and linker flags.  A more detailed example follows:
442
443       You may want or need to set compiler and linker flags for your bundled
444       C code.  For example, say we have a header file, but instead of putting
445       it in the "ffi" directory we want to put it in a separate directory
446       called "include".
447
448       "include/answer.h":
449
450        #ifndef ANSWER_H
451        #define ANSWER_H
452
453        int answer(void);
454
455        #endif
456
457       "ffi/answer.c":
458
459        #include <answer.h>
460
461        int
462        answer(void)
463        {
464          /* the answer to life the universe and everything */
465          return 42;
466        }
467
468       "lib/Answer.pm":
469
470        package Answer;
471
472        use strict;
473        use warnings;
474        use FFI::Platypus 1.00;
475        use base qw( Exporter );
476
477        our @EXPORT = qw( answer );
478
479        my $ffi = FFI::Platypus->new( api => 1 );
480        $ffi->bundle;
481        $ffi->attach( answer => [] => 'int' );
482
483        1;
484
485       If you try to use this module just as-is you will get an error, about
486       not being able to find the header file.  Probably something like this:
487
488        ffi/answer.c:1:10: fatal error: 'answer.h' file not found
489
490       So we put a "answer.fbx" file in the "ffi" directory.  (In case you are
491       wondering FBX stands for "Ffi Build and file eXtensions should whenever
492       possible be three characters long").  The name of the file can be
493       anything so long as it ends in ".fbx", we just choose "answer" here
494       because that is the name of the project.
495
496       "ffi/answer.fbx":
497
498        our $DIR;
499
500        return {
501          cflags => "-I/include",
502          source => "$DIR/*.c",
503        }
504
505       The $DIR variable is provided by the builder code.  It is the root of
506       the distribution, and is helpful if you need a fully qualified path.
507       In this case you could have also used "ffi/*.c".
508
509       The script returns a hash reference which is passed into the FFI::Build
510       constructor, so you can use any of the options supported by that class.
511       Now we should be able to use our bundled module:
512
513        % perl -Ilib -MAnswer=answer -E 'say answer'
514        42
515
516   Using bundled code with Alien.
517       A useful technique is to use Platypus with Alien technology.  The Alien
518       namespace is reserved for providing external non-Perl dependencies for
519       CPAN modules.  The nominal Alien module when installed looks for the
520       library locally, and if it can't be found it fetches it from the
521       internet, builds it, and installs it in a private directory so that it
522       can be used by other CPAN modules.  For Aliens that provide shared
523       libraries, and that have simple interfaces that do not require
524       additional C code you can easily just pass the shared libraries to
525       Platypus directly.  For modules that require some bundled C code and an
526       Alien you have to link the Alien library with your bundled code.  If
527       the Alien uses the Alien::Base interface then all you have to do is
528       give the name of the Alien to FFI::Build.
529
530       For example, the "bzip2" library provides an interface that requires
531       the caller to allocate a C "struct" and then pass it to its various
532       functions.  The "struct" is actually pretty simple and you could use
533       FFI::C or FFI::Platypus::Record, but here is an example of how you
534       would connect bundled C code with an Alien.
535
536       "ffi/compress.c":
537
538        #include <bzlib.h>
539        #include <stdlib.h>
540
541        int
542        bzip2__new(bz_stream **stream, int blockSize100k, int verbosity, int workFactor )
543        {
544          *stream = malloc(sizeof(bz_stream));
545          (*stream)->bzalloc = NULL;
546          (*stream)->bzfree  = NULL;
547          (*stream)->opaque  = NULL;
548
549          return BZ2_bzCompressInit(*stream, blockSize100k, verbosity, workFactor );
550        }
551
552       "lib/Bzip2.pm":
553
554        package Bzip2;
555
556        use strict;
557        use warnings;
558        use FFI::Platypus 1.00;
559        use FFI::Platypus::Memory qw( free );
560
561        my $ffi = FFI::Platypus->new( api => 1 );
562        $ffi->bundle;
563
564        $ffi->mangler(sub {
565          my $name = shift;
566          $name =~ s/^/bzip2__/ unless $name =~ /^BZ2_/;
567          $name;
568        });
569
570        =head2 new
571
572         my $bzip2 = Bzip2->new($block_size_100k, $verbosity, $work_flow);
573
574        =cut
575
576        $ffi->attach( new => ['opaque*', 'int', 'int', 'int'] => 'int' => sub {
577          my $xsub = shift;
578          my $class = shift;
579          my $ptr;
580          my $ret = $xsub->(\$ptr, @_);
581          return bless \$ptr, $class;
582        });
583
584        $ffi->attach( [ BZ2_bzCompressEnd => 'DESTROY' ] => ['opaque'] => 'int' => sub {
585          my $xsub = shift;
586          my $self = shift;
587          my $ret = $xsub->($$self);
588          free $$self;
589        });
590
591        1;
592
593       The ".fbx" file that goes with this to make it work with Alien::Libbz2
594       is now pretty trivial:
595
596       "ffi/bz2.fbx":
597
598        {
599          alien => ['Alien::Libbz2'],
600          source => ['ffi/*.c'],
601        };
602

AUTHOR

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