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 2.05
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          (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

DESCRIPTION

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

AUTHOR

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)
Impressum