Skip site navigation (1)Skip section navigation (2)

FreeBSD Manual Pages

  
 
  

home | help
FFI::Platypus::Bundle(User Contributed Perl DocumentatFFI::Platypus::Bundle(3)

NAME
       FFI::Platypus::Bundle - Bundle foreign code with	your Perl module

VERSION
       version 1.31

SYNOPSIS
       "ffi/foo.c":

	#include <ffi_platypus_bundle.h>
	#include <string.h>

	typedef	struct {
	  char *name;
	  int value;
	} foo_t;

	foo_t*
	foo__new(const char *class_name, const char *name, int value)
	{
	  (void)class_name;
	  foo_t	*self =	malloc(	sizeof(	foo_t )	);
	  self->name = strdup(name);
	  self->value =	value;
	  return self;
	}

	const char *
	foo__name(foo_t	*self)
	{
	  return self->name;
	}

	int
	foo__value(foo_t *self)
	{
	  return self->value;
	}

	void
	foo__DESTROY(foo_t *self)
	{
	  free(self->name);
	  free(self);
	}

       "lib/Foo.pm":

	package	Foo;

	use strict;
	use warnings;
	use FFI::Platypus;

	{
	  my $ffi = FFI::Platypus->new(	api => 1 );

	  $ffi->type('object(Foo)' => 'foo_t');
	  $ffi->mangler(sub {
	    my $name = shift;
	    $name =~ s/^/foo__/;
	    $name;
	  });

	  $ffi->bundle;

	  $ffi->attach(	new =>	   [ 'string', 'string', 'int' ] => 'foo_t'  );
	  $ffi->attach(	name =>	   [ 'foo_t' ]			 => 'string' );
	  $ffi->attach(	value =>   [ 'foo_t' ]			 => 'int'    );
	  $ffi->attach(	DESTROY	=> [ 'foo_t' ]			 => 'void'   );
	}

	1;

       "t/foo.t"

	use Test::More;
	use Foo;

	my $foo	= Foo->new("platypus", 10);
	isa_ok $foo, 'Foo';
	is $foo->name, "platypus";
	is $foo->value,	10;

	done_testing;

       "Makefile.PL":

	use ExtUtils::MakeMaker;
	use FFI::Build::MM;
	my $fbmm = FFI::Build::MM->new;
	WriteMakefile(
	  $fbmm->mm_args(
	    NAME     =>	'Foo',
	    DISTNAME =>	'Foo',
	    VERSION  =>	'1.00',
	    # ...
	  )
	);

	sub MY::postamble
	{
	  $fbmm->mm_postamble;
	}

       or "dist.ini":

	name	= Foo
	version	= 0.01
	...

	[FFI::Build]
	version	= 1.04

DESCRIPTION
       This document serves as a tutorial for using the	new bundling interface
       provided	by FFI::Platypus as of api version 1.  It requires
       FFI::Platypus of	at least 1.00.

       Sometimes when writing FFI bindings you need to include a little	C code
       (or your	favorite compiled language) to finish things off.
       Alternatively, you might	just want to write some	C code (or your
       favorite	compiled language) to include with your	Perl module to make a
       tight loop faster.  The bundling	interface has you covered.

   Basic example
       To illustrate we	will go	through	the files in the synopsis and explain
       how and why they	work.  To start	with we	have some C code which
       emulates	object oriented	code using "foo__" as a	prefix.	 We use	a C
       struct that we call "foo_t" to store our	object data.  On the C level
       the struct acts as a class, when	combined with its functions that act
       as methods.  The	constructor just allocates the memory it needs for the
       "foo_t" instance, fills in the appropriate fields and returns the
       pointer:

	foo_t*
	foo__new(const char *class_name, const char *name, int value)
	{
	  (void) class_name;
	  foo_t	*self =	malloc(	sizeof(	foo_t )	);
	  self->name = strdup(name);
	  self->value =	value;
	  return self;
	}

       We include a class name as the first argument, because Perl will
       include that when calling the constructor, but we do not	use it here.
       An exercise for the reader would	be to add hierarchical inheritance.

       There are also some methods which return	member values.	This class has
       only read only members, but you could have read/write or	other methods
       depending on your needs.

	const char *
	foo__name(foo_t	*self)
	{
	  return self->name;
	}

       We also include a destructor so that the	memory owned by	the object can
       be freed	when it	is no longer needed.

	void
	foo__DESTROY(foo_t *self)
	{
	  free(self->name);
	  free(self);
	}

       This might start	to look	a little like a	Perl module, and when we look
       at the Perl code	that binds to this code, you will see why.  First lets
       prepare the FFI::Platypus instance and specify the correct api version:

	my $ffi	= FFI::Platypus->new( api => 1 );

       The bundle interface is only supported with api version 1, so if	you
       try to use version 0 it will not	work.  Next we define an object	type
       for "foo_t" which will associate	it with	the Perl class "Foo".

	$ffi->type('object(Foo)' => 'foo_t');

       As object type is a blessed reference to	an opaque (default) or integer
       type which can be used as a Perl	object.	 Platypus does the translating
       of Perl object to and from the foo_t pointers that the C	code
       understands.  For more details on Platypus types	see
       FFI::Platypus::Type.

       Next we set the mangler on the Platypus instance	so that	we can refer
       to function names without the "foo__" prefix.  You could	just not use
       the prefix in your C code and skip this step, or	you could refer	to the
       function	names in their full in your Perl code, however,	this saves
       extra typing and	allows you to bundle more than one class with your
       Perl code without having	to worry about name conflicts.

	$ffi->mangler(sub {
	  my $name = shift;
	  $name	=~ s/^/foo__/;
	  $name;
	});

       Finally we let Platypus know that we will be bundling code.

	$ffi->bundle;

       By default, this	searches for the appropriate place for your dynamic
       libraries using the current package.  In	some cases you may need	to
       override	this, for example if your dist is named	"Foo-Bar" but your
       specific	class is named "Foo::Bar::Baz",	you'd want something like
       this:

	package	Foo::Bar::Baz;
	use FFI::Platypus;
	my $ffi	= FFI::Platypus->new( api => 1 );
	$ffi->bundle('Foo::Bar');
	...

       Now, finally we can attach the methods for our class:

	$ffi->attach( new =>	 [ 'string', 'int' ] =>	'foo_t'	 );
	$ffi->attach( name =>	 [ 'foo_t' ]	     =>	'string' );
	$ffi->attach( value =>	 [ 'foo_t' ]	     =>	'int'	 );
	$ffi->attach( DESTROY => [ 'foo_t' ]	     =>	'void'	 );

       Note that we do not have	to include the "foo__" prefix because of the
       way we set up the mangler.  If we hadn't	done that then we could
       instead attach with the full names:

	$ffi->attach( [	'foo__new'  => 'new' ]	=> [ 'string', 'int' ] => 'foo_t'  );
	$ffi->attach( [	'foo__name' => 'name' ]	=> [ 'foo_t' ]	       => 'string' );
	...

       You're done!  You can now use this class.  Lets write a test to make
       sure it works,

	use strict;
	use warnings;
	use Test::More;
	use Foo;

	my $foo	= Foo->new("platypus", 10);
	isa_ok $foo, 'Foo';
	is $foo->name, "platypus";
	is $foo->value,	10;

	done_testing;

       and use "prove" to check	that it	works:

	% prove	-lvm
	t/foo.t	..
	ok 1 - An object of class 'Foo'	isa 'Foo'
	ok 2
	ok 3
	1..3
	ok
	All tests successful.
	Files=1, Tests=3,  0 wallclock secs ( 0.02 usr	0.00 sys +  0.14 cusr  0.03 csys =  0.19 CPU)
	Result:	PASS

       Platypus	automatically compiles and links the dynamic library for you:

	% ls ffi/_build
	foo.c.o	 libFoo.so

       The C code will be rebuilt next time if the source code is newer	than
       the object or dynamic libraries files.  If the source files are not
       changed,	then it	won't be rebuilt to save time.	If you are using the
       code without MakeMaker, or another build	system you are responsible for
       cleaning	up these files.	 This is intended as a convenience to allow
       you to test your	code without having to invoke MakeMaker, or "dzil" or
       whatever	build system you are using.

       When you	distribute your	module though, you will	want the dynamic
       library built just once at build-time and installed correctly so	that
       it can be found at run-time.  You don't need to make any	changes	to
       your C or Perl code, but	you do need to tell MakeMaker to build and
       install the appropriate files using FFI::Build::MM:

	use ExtUtils::MakeMaker;
	use FFI::Build::MM;
	my $fbmm = FFI::Build::MM->new;
	WriteMakefile(
	  $fbmm->mm_args(
	    NAME     =>	'Foo',
	    DISTNAME =>	'Foo',
	    VERSION  =>	'1.00',
	    # ...
	  )
	);

	sub MY::postamble
	{
	  $fbmm->mm_postamble;
	}

       And we can invoke all the normal	MakeMaker style	stuff and our C	code
       will be compiled, linked	and installed at the appropriate steps.

	% perl Makefile.PL
	Generating a Unix-style	Makefile
	Writing	Makefile for Foo
	Writing	MYMETA.yml and MYMETA.json
	% make
	cp lib/Foo.pm blib/lib/Foo.pm
	"/Users/ollisg/perl5/perlbrew/perls/perl-5.30.0/bin/perl" -MFFI::Build::MM=cmd -e fbx_build
	CC ffi/foo.c
	LD blib/lib/auto/share/dist/Foo/lib/libFoo.dylib
	% make test
	"/Users/ollisg/perl5/perlbrew/perls/perl-5.30.0/bin/perl" -MFFI::Build::MM=cmd -e fbx_build
	"/Users/ollisg/perl5/perlbrew/perls/perl-5.30.0/bin/perl" -MFFI::Build::MM=cmd -e fbx_test
	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
	t/foo.t	.. ok
	All tests successful.
	Files=1, Tests=3,  0 wallclock secs ( 0.01 usr	0.00 sys +  0.06 cusr  0.01 csys =  0.08 CPU)
	Result:	PASS

       If the "Makefile.PL" file above looks overly complicated, you can use
       the Dist::Zilla::Plugin::FFI::Build plugin to simplify your life	if you
       are using Dist::Zilla:

	[FFI::Build]
	version	= 1.04

       Specifying version 1.04 will ensure that	any ".o" or ".so" files	are
       pruned from your	build tree and not distributed by mistake.

   Initialization example
       The bundle interface also gives you entry points	which will be called
       automatically when your code is loaded and unloaded if they are found.

       "ffi_pl_bundle_init"
	    void ffi_pl_bundle_init(const char *package, int argc, void	*argv[]);

	   Called when the dynamic library is loaded.  "package" is the	Perl
	   package that	called "bundle"	from Perl space.  "argc" and "argv"
	   represents an array of opaque pointers that can be passed as	an
	   array to bundle as the last argument.  (the count "argc" is a
	   little redundant because "argv" is also NULL	terminated).

       "ffi_pl_bundle_constant"
	    void ffi_pl_bundle_constant(const char *package, ffi_platypus_constant_t *c);

	   Called immediately after "ffi_pl_bundle_init", and is intended to
	   allow you to	set Perl constants from	C space.  For details on how
	   this	works and what methods you can call on the
	   "ffi_platypus_constant_t" instance, see FFI::Platypus::Constant.

       "ffi_pl_bundle_fini"
	    void ffi_pl_bundle_fini(const char *package);

	   Called when the dynamic library is unloaded.	 "package" is the Perl
	   package that	called "bundle"	from Perl space	when the library was
	   loaded.  CAVEAT: if you attach any functions	then this will never
	   be called, because attaching	functions locks	the Platypus instance
	   into	memory along with the libraries	which it is using.

       Here is an example that passes the version and a	callback back into
       Perl space that emulates	the Perl 5.10 "say" feature.

       "ffi/init.c":

	#include <ffi_platypus_bundle.h>

	char buffer[512];
	const char *version;
	void (*say)(const char *);

	void
	ffi_pl_bundle_init(const char *package,	int argc, void *argv[])
	{
	  version = argv[0];
	  say	  = argv[1];

	  say("in init!");

	  snprintf(buffer, 512,	"package = %s, version = %s", package, version);
	  say(buffer);

	  snprintf(buffer, 512,	"args =	%d", argc);
	  say(buffer);
	}

	void
	ffi_pl_bundle_fini(const char *package)
	{
	  say("in fini!");
	}

       "lib/Init.pm":

	package	Init;

	use strict;
	use warnings;
	use FFI::Platypus;

	our $VERSION = '1.00';

	{
	  my $ffi = FFI::Platypus->new(	api => 1 );

	  my $say = $ffi->closure(sub {
	    my $string = shift;
	    print "$string\n";
	  });

	  $ffi->bundle([
	    $ffi->cast(	'string' => 'opaque', $VERSION ),
	    $ffi->cast(	'(string)->void' => 'opaque', $say ),
	  ]);

	  undef	$ffi;
	  undef	$say;
	}

	1;

       The deinitialization order for the $say callback	and the	$ffi instance
       is essential here, so we	do it manually with "undef":

	undef $ffi;
	undef $say;

       First we	deallocate $ffi	which calls "ffi_pl_bundle_fini", which	calls
       $say, so	we want	to make	sure the latter	is still allocated.  Once
       "ffi_pl_bundle_fini" is done, we	can safely deallocate $say.

       If "ffi_pl_bundle_fini" didn't call back	into Perl space	like this then
       we don't	have to	be as careful about deallocating things	in Perl	space.

AUTHOR
       Author: Graham Ollis <plicease@cpan.org>

       Contributors:

       Bakkiaraj Murugesan (bakkiaraj)

       Dylan Cali (calid)

       pipcet

       Zaki Mughal (zmughal)

       Fitz Elliott (felliott)

       Vickenty	Fesunov	(vyf)

       Gregor Herrmann (gregoa)

       Shlomi Fish (shlomif)

       Damyan Ivanov

       Ilya Pavlov (Ilya33)

       Petr Pisar (ppisar)

       Mohammad	S Anwar	(MANWAR)

       HAYkon HA|gland (hakonhagland, HAKONH)

       Meredith	(merrilymeredith, MHOWARD)

       Diab Jerius (DJERIUS)

COPYRIGHT AND LICENSE
       This software is	copyright (c) 2015,2016,2017,2018,2019,2020 by Graham
       Ollis.

       This is free software; you can redistribute it and/or modify it under
       the same	terms as the Perl 5 programming	language system	itself.

perl v5.32.0			  2020-07-01	      FFI::Platypus::Bundle(3)

NAME | VERSION | SYNOPSIS | DESCRIPTION | AUTHOR | COPYRIGHT AND LICENSE

Want to link to this manual page? Use this URL:
<https://www.freebsd.org/cgi/man.cgi?query=FFI::Platypus::Bundle&sektion=3&manpath=FreeBSD+12.2-RELEASE+and+Ports>

home | help