-
Notifications
You must be signed in to change notification settings - Fork 23
/
Copy pathPlatypus.pm
2571 lines (1811 loc) · 74.3 KB
/
Platypus.pm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
package FFI::Platypus;
use strict;
use warnings;
use 5.008004;
use Carp qw( croak );
use FFI::Platypus::Function;
use FFI::Platypus::Type;
# ABSTRACT: Write Perl bindings to non-Perl libraries with FFI. No XS required.
# VERSION
# Platypus-Man,
# Platypus-Man,
# Does Whatever A Platypus Can
# Is Mildly Venomous
# Hangs Out In Rivers By Caves
# Look Out!
# Here Comes The Platypus-Man
# From the original FFI::Platypus prototype:
# Kinda like gluing a duckbill to an adorable mammal
=begin stopwords
ØMQ
=end stopwords
=head1 SYNOPSIS
use FFI::Platypus 2.00;
# for all new code you should use api => 2
my $ffi = FFI::Platypus->new(
api => 2,
lib => undef, # search libc
);
# call dynamically
$ffi->function( puts => ['string'] => 'int' )->call("hello world");
# attach as a xsub and call (much faster)
$ffi->attach( puts => ['string'] => 'int' );
puts("hello world");
=head1 DESCRIPTION
Platypus is a library for creating interfaces to machine code libraries
written in languages like C, L<C++|FFI::Platypus::Lang::CPP>,
L<Go|FFI::Platypus::Lang::Go>,
L<Fortran|FFI::Platypus::Lang::Fortran>,
L<Rust|FFI::Platypus::Lang::Rust>,
L<Pascal|FFI::Platypus::Lang::Pascal>. Essentially anything that gets
compiled into machine code. This implementation uses L<libffi|https://sourceware.org/libffi/> to
accomplish this task. L<libffi|https://sourceware.org/libffi/> is battle tested by a number of other
scripting and virtual machine languages, such as Python and Ruby to
serve a similar role. There are a number of reasons why you might want
to write an extension with Platypus instead of XS:
=over 4
=item FFI / Platypus does not require messing with the guts of Perl
XS is less of an API and more of the guts of perl splayed out to do
whatever you want. That may at times be very powerful, but it can also
be a frustrating exercise in hair pulling.
=item FFI / Platypus is portable
Lots of languages have FFI interfaces, and it is subjectively easier to
port an extension written in FFI in Perl or another language to FFI in
another language or Perl. One goal of the Platypus Project is to reduce
common interface specifications to a common format like JSON that could
be shared between different languages.
=item FFI / Platypus could be a bridge to Raku
One of those "other" languages could be Raku and Raku already has an
FFI interface I am told.
=item FFI / Platypus can be reimplemented
In a bright future with multiple implementations of Perl 5, each
interpreter will have its own implementation of Platypus, allowing
extensions to be written once and used on multiple platforms, in much
the same way that Ruby-FFI extensions can be use in Ruby, JRuby and
Rubinius.
=item FFI / Platypus is pure perl (sorta)
One Platypus script or module works on any platform where the libraries
it uses are available. That means you can deploy your Platypus script
in a shared filesystem where they may be run on different platforms. It
also means that Platypus modules do not need to be installed in the
platform specific Perl library path.
=item FFI / Platypus is not C or C++ centric
XS is implemented primarily as a bunch of C macros, which requires at
least some understanding of C, the C pre-processor, and some C++ caveats
(since on some platforms Perl is compiled and linked with a C++
compiler). Platypus on the other hand could be used to call other
compiled languages, like L<Fortran|FFI::Platypus::Lang::Fortran>,
L<Go|FFI::Platypus::Lang::Go>,
L<Rust|FFI::Platypus::Lang::Rust>,
L<Pascal|FFI::Platypus::Lang::Pascal>, L<C++|FFI::Platypus::Lang::CPP>,
or even L<assembly|FFI::Platypus::Lang::ASM>, allowing you to focus
on your strengths.
=item FFI / Platypus does not require a parser
L<Inline> isolates the extension developer from XS to some extent, but
it also requires a parser. The various L<Inline> language bindings are
a great technical achievement, but I think writing a parser for every
language that you want to interface with is a bit of an anti-pattern.
=back
This document consists of an API reference, a set of examples, some
support and development (for contributors) information. If you are new
to Platypus or FFI, you may want to skip down to the
L<EXAMPLES|/EXAMPLES> to get a taste of what you can do with Platypus.
Platypus has extensive documentation of types at L<FFI::Platypus::Type>
and its custom types API at L<FFI::Platypus::API>.
You are B<strongly> encouraged to use API level 2 for all new code.
There are a number of improvements and design fixes that you get
for free. You should even consider updating existing modules to
use API level 2 where feasible. How do I do that you might ask?
Simply pass in the API level to the platypus constructor.
my $ffi = FFI::Platypus->new( api => 2 );
The Platypus documentation has already been updated to assume API
level 1.
=cut
our @CARP_NOT = qw( FFI::Platypus::Declare FFI::Platypus::Record );
require XSLoader;
XSLoader::load(
'FFI::Platypus', $FFI::Platypus::VERSION || 0
);
=head1 CONSTRUCTORS
=head2 new
my $ffi = FFI::Platypus->new( api => 2, %options);
Create a new instance of L<FFI::Platypus>.
Any types defined with this instance will be valid for this instance
only, so you do not need to worry about stepping on the toes of other
CPAN FFI / Platypus Authors.
Any functions found will be out of the list of libraries specified with
the L<lib|/lib> attribute.
=head3 options
=over 4
=item api
[version 0.91]
Sets the API level. The recommended value for all new code is C<2>.
The Platypus documentation assumes API level C<2> except for a few
places that specifically document older versions. You should
only use a lower value for a legacy code base that cannot be migrated to
a newer API level. Legal values are:
=over
=item C<0>
Original API level. See L<FFI::Platypus::TypeParser::Version0> for details
on the differences.
=item C<1>
Enable version 1 API type parser which allows pass-by-value records
and type decoration on basic types.
=item C<2>
Enable version 2 API.
The Platypus documentation assumes this api level is set.
API version 2 is identical to version 1, except:
=over 4
=item Pointer functions that return C<NULL> will return C<undef> instead of empty list
This fixes a long standing design bug in Platypus.
=item Array references may be passed to pointer argument types
This replicates the behavior of array argument types with no size. So the types C<sint8*> and C<sint8[]>
behave identically when an array reference is passed in. They differ in that, as before, you can
pass a scalar reference into type C<sint8*>.
=item The fixed string type can be specified without pointer modifier
That is you can use C<string(10)> instead of C<string(10)*> as you were previously able to
in API 0.
=back
=back
=item lib
Either a pathname (string) or a list of pathnames (array ref of strings)
to pre-populate the L<lib|/lib> attribute. Use C<[undef]> to search the
current process for symbols.
0.48
C<undef> (without the array reference) can be used to search the current
process for symbols.
=item ignore_not_found
[version 0.15]
Set the L<ignore_not_found|/ignore_not_found> attribute.
=item lang
[version 0.18]
Set the L<lang|/lang> attribute.
=back
=cut
sub new
{
my($class, %args) = @_;
my @lib;
if(exists $args{lib})
{
if(!ref($args{lib}))
{
push @lib, $args{lib};
}
elsif(ref($args{lib}) eq 'ARRAY')
{
push @lib, @{$args{lib}};
}
else
{
croak "lib argument must be a scalar or array reference";
}
}
my $api = $args{api} || 0;
my $experimental = $args{experimental} || 0;
if($experimental == 1)
{
Carp::croak("Please do not use the experimental version of api = 1, instead require FFI::Platypus 1.00 or better");
}
elsif($experimental == 2)
{
Carp::croak("Please do not use the experimental version of api = 2, instead require FFI::Platypus 2.00 or better");
}
if(defined $api && $api > 2 && $experimental != $api)
{
Carp::cluck("Enabling development API version $api prior to FFI::Platypus $api.00");
}
my $tp;
if($api == 0)
{
$tp = 'Version0';
}
elsif($api == 1)
{
$tp = 'Version1';
}
elsif($api == 2)
{
$tp = 'Version2';
}
else
{
Carp::croak("API version $api not (yet) implemented");
}
require "FFI/Platypus/TypeParser/$tp.pm";
$tp = "FFI::Platypus::TypeParser::$tp";
my $self = bless {
lib => \@lib,
lang => '',
handles => {},
abi => -1,
api => $api,
tp => $tp->new,
fini => [],
ignore_not_found => defined $args{ignore_not_found} ? $args{ignore_not_found} : 0,
}, $class;
$self->lang($args{lang} || 'C');
$self;
}
sub _lang_class ($)
{
my($lang) = @_;
my $class = $lang =~ m/^=(.*)$/ ? $1 : "FFI::Platypus::Lang::$lang";
unless($class->can('native_type_map'))
{
my $pm = "$class.pm";
$pm =~ s/::/\//g;
require $pm;
}
croak "$class does not provide native_type_map method"
unless $class->can("native_type_map");
$class;
}
=head1 ATTRIBUTES
=head2 lib
$ffi->lib($path1, $path2, ...);
my @paths = $ffi->lib;
The list of libraries to search for symbols in.
The most portable and reliable way to find dynamic libraries is by using
L<FFI::CheckLib>, like this:
use FFI::CheckLib 0.06;
$ffi->lib(find_lib_or_die lib => 'archive');
# finds libarchive.so on Linux
# libarchive.bundle on OS X
# libarchive.dll (or archive.dll) on Windows
# cygarchive-13.dll on Cygwin
# ...
# and will die if it isn't found
L<FFI::CheckLib> has a number of options, such as checking for specific
symbols, etc. You should consult the documentation for that module.
As a special case, if you add C<undef> as a "library" to be searched,
Platypus will also search the current process for symbols. This is
mostly useful for finding functions in the standard C library, without
having to know the name of the standard c library for your platform (as
it turns out it is different just about everywhere!).
You may also use the L</find_lib> method as a shortcut:
$ffi->find_lib( lib => 'archive' );
=cut
sub lib
{
my($self, @new) = @_;
if(@new)
{
push @{ $self->{lib} }, map { ref $_ eq 'CODE' ? $_->() : $_ } @new;
delete $self->{mangler};
}
@{ $self->{lib} };
}
=head2 ignore_not_found
[version 0.15]
$ffi->ignore_not_found(1);
my $ignore_not_found = $ffi->ignore_not_found;
Normally the L<attach|/attach> and L<function|/function> methods will
throw an exception if it cannot find the name of the function you
provide it. This will change the behavior such that
L<function|/function> will return C<undef> when the function is not
found and L<attach|/attach> will ignore functions that are not found.
This is useful when you are writing bindings to a library and have many
optional functions and you do not wish to wrap every call to
L<function|/function> or L<attach|/attach> in an C<eval>.
=cut
sub ignore_not_found
{
my($self, $value) = @_;
if(defined $value)
{
$self->{ignore_not_found} = $value;
}
$self->{ignore_not_found};
}
=head2 lang
[version 0.18]
$ffi->lang($language);
Specifies the foreign language that you will be interfacing with. The
default is C. The foreign language specified with this attribute
changes the default native types (for example, if you specify
L<Rust|FFI::Platypus::Lang::Rust>, you will get C<i32> as an alias for
C<sint32> instead of C<int> as you do with L<C|FFI::Platypus::Lang::C>).
If the foreign language plugin supports it, this will also enable
Platypus to find symbols using the demangled names (for example, if you
specify L<CPP|FFI::Platypus::Lang::CPP> for C++ you can use method names
like C<Foo::get_bar()> with L</attach> or L</function>.
=cut
sub lang
{
my($self, $value) = @_;
if(defined $value && $value ne $self->{lang})
{
$self->{lang} = $value;
my $class = _lang_class($self->{lang});
$self->abi($class->abi) if $class->can('abi');
{
my %type_map;
my $map = $class->native_type_map(
$self->{api} > 0
? (api => $self->{api})
: ()
);
foreach my $key (keys %$map)
{
my $value = $map->{$key};
next unless $self->{tp}->have_type($value);
$type_map{$key} = $value;
}
$type_map{$_} = $_ for grep { $self->{tp}->have_type($_) }
qw( void sint8 uint8 sint16 uint16 sint32 uint32 sint64 uint64 float double string opaque
longdouble complex_float complex_double );
$type_map{pointer} = 'opaque' if $self->{tp}->isa('FFI::Platypus::TypeParser::Version0');
$self->{tp}->type_map(\%type_map);
}
$class->load_custom_types($self) if $class->can('load_custom_types');
}
$self->{lang};
}
=head2 api
[version 1.11]
my $level = $ffi->api;
Returns the API level of the Platypus instance.
=cut
sub api { shift->{api} }
=head1 METHODS
=head2 type
$ffi->type($typename);
$ffi->type($typename => $alias);
Define a type. The first argument is the native or C name of the type.
The second argument (optional) is an alias name that you can use to
refer to this new type. See L<FFI::Platypus::Type> for legal type
definitions.
Examples:
$ffi->type('sint32'); # only checks to see that sint32 is a valid type
$ffi->type('sint32' => 'myint'); # creates an alias myint for sint32
$ffi->type('bogus'); # dies with appropriate diagnostic
=cut
sub type
{
my($self, $name, $alias) = @_;
croak "usage: \$ffi->type(name => alias) (alias is optional)" unless defined $self && defined $name;
$self->{tp}->check_alias($alias) if defined $alias;
my $type = $self->{tp}->parse($name);
$self->{tp}->set_alias($alias, $type) if defined $alias;
$self;
}
=head2 custom_type
$ffi->custom_type($alias => {
native_type => $native_type,
native_to_perl => $coderef,
perl_to_native => $coderef,
perl_to_native_post => $coderef,
});
Define a custom type. See L<FFI::Platypus::Type#Custom-Types> for details.
=cut
sub custom_type
{
my($self, $alias, $cb) = @_;
my $argument_count = $cb->{argument_count} || 1;
croak "argument_count must be >= 1"
unless $argument_count >= 1;
croak "Usage: \$ffi->custom_type(\$alias, { ... })"
unless defined $alias && ref($cb) eq 'HASH';
croak "must define at least one of native_to_perl, perl_to_native, or perl_to_native_post"
unless defined $cb->{native_to_perl} || defined $cb->{perl_to_native} || defined $cb->{perl_to_native_post};
$self->{tp}->check_alias($alias);
my $type = $self->{tp}->create_type_custom(
$cb->{native_type},
$cb->{perl_to_native},
$cb->{native_to_perl},
$cb->{perl_to_native_post},
$argument_count,
);
$self->{tp}->set_alias($alias, $type);
$self;
}
=head2 load_custom_type
$ffi->load_custom_type($name => $alias, @type_args);
Load the custom type defined in the module I<$name>, and make an alias
I<$alias>. If the custom type requires any arguments, they may be passed
in as I<@type_args>. See L<FFI::Platypus::Type#Custom-Types> for
details.
If I<$name> contains C<::> then it will be assumed to be a fully
qualified package name. If not, then C<FFI::Platypus::Type::> will be
prepended to it.
=cut
sub load_custom_type
{
my($self, $name, $alias, @type_args) = @_;
croak "usage: \$ffi->load_custom_type(\$name, \$alias, ...)"
unless defined $name && defined $alias;
$name = "FFI::Platypus::Type$name" if $name =~ /^::/;
$name = "FFI::Platypus::Type::$name" unless $name =~ /::/;
unless($name->can("ffi_custom_type_api_1"))
{
my $pm = "$name.pm";
$pm =~ s/::/\//g;
eval { require $pm };
warn $@ if $@;
}
unless($name->can("ffi_custom_type_api_1"))
{
croak "$name does not appear to conform to the custom type API";
}
my $cb = $name->ffi_custom_type_api_1($self, @type_args);
$self->custom_type($alias => $cb);
$self;
}
=head2 types
my @types = $ffi->types;
my @types = FFI::Platypus->types;
Returns the list of types that FFI knows about. This will include the
native C<libffi> types (example: C<sint32>, C<opaque> and C<double>) and
the normal C types (example: C<unsigned int>, C<uint32_t>), any types
that you have defined using the L<type|/type> method, and custom types.
The list of types that Platypus knows about varies somewhat from
platform to platform, L<FFI::Platypus::Type> includes a list of the core
types that you can always count on having access to.
It can also be called as a class method, in which case, no user defined
or custom types will be included in the list.
=cut
sub types
{
my($self) = @_;
$self = $self->new unless ref $self && eval { $self->isa('FFI::Platypus') };
sort $self->{tp}->list_types;
}
=head2 type_meta
my $meta = $ffi->type_meta($type_name);
my $meta = FFI::Platypus->type_meta($type_name);
Returns a hash reference with the meta information for the given type.
It can also be called as a class method, in which case, you won't be
able to get meta data on user defined types.
The format of the meta data is implementation dependent and subject to
change. It may be useful for display or debugging.
Examples:
my $meta = $ffi->type_meta('int'); # standard int type
my $meta = $ffi->type_meta('int[64]'); # array of 64 ints
$ffi->type('int[128]' => 'myintarray');
my $meta = $ffi->type_meta('myintarray'); # array of 128 ints
=cut
sub type_meta
{
my($self, $name) = @_;
$self = $self->new unless ref $self && eval { $self->isa('FFI::Platypus') };
$self->{tp}->parse($name)->meta;
}
=head2 mangler
$ffi->mangler(\&mangler);
Specify a customer mangler to be used for symbol lookup. This is usually useful
when you are writing bindings for a library where all of the functions have the
same prefix. Example:
$ffi->mangler(sub {
my($symbol) = @_;
return "foo_$symbol";
});
$ffi->function( get_bar => [] => 'int' ); # attaches foo_get_bar
my $f = $ffi->function( set_baz => ['int'] => 'void' );
$f->call(22); # calls foo_set_baz
=cut
sub mangler
{
my($self, $sub) = @_;
$self->{mangler} = $self->{mymangler} = $sub;
}
=head2 function
my $function = $ffi->function($name => \@argument_types => $return_type);
my $function = $ffi->function($address => \@argument_types => $return_type);
my $function = $ffi->function($name => \@argument_types => $return_type, \&wrapper);
my $function = $ffi->function($address => \@argument_types => $return_type, \&wrapper);
Returns an object that is similar to a code reference in that it can be
called like one.
Caveat: many situations require a real code reference, so at the price
of a performance penalty you can get one like this:
my $function = $ffi->function(...);
my $coderef = sub { $function->(@_) };
It may be better, and faster to create a real Perl function using the
L<attach|/attach> method.
In addition to looking up a function by name you can provide the address
of the symbol yourself:
my $address = $ffi->find_symbol('my_function');
my $function = $ffi->function($address => ...);
Under the covers, L<function|/function> uses L<find_symbol|/find_symbol>
when you provide it with a name, but it is useful to keep this in mind
as there are alternative ways of obtaining a functions address.
Example: a C function could return the address of another C function
that you might want to call.
[version 0.76]
If the last argument is a code reference, then it will be used as a
wrapper around the function when called. The first argument to the wrapper
will be the inner function, or if it is later attached an xsub. This can be
used if you need to verify/modify input/output data.
Examples:
my $function = $ffi->function('my_function_name', ['int', 'string'] => 'string');
my $return_string = $function->(1, "hi there");
[version 0.91]
my $function = $ffi->function( $name => \@fixed_argument_types => \@var_argument_types => $return_type);
my $function = $ffi->function( $name => \@fixed_argument_types => \@var_argument_types => $return_type, \&wrapper);
my $function = $ffi->function( $name => \@fixed_argument_types => \@var_argument_types);
my $function = $ffi->function( $name => \@fixed_argument_types => \@var_argument_types => \&wrapper);
Version 0.91 and later allows you to creat functions for c variadic functions
(such as printf, scanf, etc) which can take a variable number of arguments.
The first set of arguments are the fixed set, the second set are the variable
arguments to bind with. The variable argument types must be specified in order
to create a function object, so if you need to call variadic function with
different set of arguments then you will need to create a new function object
each time:
# int printf(const char *fmt, ...);
$ffi->function( printf => ['string'] => ['int'] => 'int' )
->call("print integer %d\n", 42);
$ffi->function( printf => ['string'] => ['string'] => 'int' )
->call("print string %s\n", 'platypus');
Some older versions of libffi and possibly some platforms may not support
variadic functions. If you try to create a one, then an exception will be
thrown.
[version 1.26]
If the return type is omitted then C<void> will be the assumed return type.
=cut
sub function
{
my $wrapper;
$wrapper = pop if ref $_[-1] eq 'CODE';
croak "usage \$ffi->function( \$name, \\\@arguments, [\\\@var_args], [\$return_type])" unless @_ >= 3 && @_ <= 6;
my $self = shift;
my $name = shift;
my $fixed_args = shift;
my $var_args;
$var_args = shift if defined $_[0] && ref($_[0]) eq 'ARRAY';
my $ret = shift;
$ret = 'void' unless defined $ret;
# special case: treat a single void argument type as an empty list of
# arguments, a la olde timey C compilers.
if( (!defined $var_args) && @$fixed_args == 1 && $fixed_args->[0] eq 'void' )
{
$fixed_args = [];
}
my $fixed_arg_count = defined $var_args ? scalar(@$fixed_args) : -1;
my @args = map { $self->{tp}->parse($_) || croak "unknown type: $_" } @$fixed_args;
if($var_args)
{
push @args, map {
my $type = $self->{tp}->parse($_);
# https://github.com/PerlFFI/FFI-Platypus/issues/323
$type->type_code == 67 ? $self->{tp}->parse('double') : $type
} @$var_args;
}
$ret = $self->{tp}->parse($ret) || croak "unknown type: $ret";
my $address = $name =~ /^-?[0-9]+$/ ? $name : $self->find_symbol($name);
croak "unable to find $name" unless defined $address || $self->ignore_not_found;
return unless defined $address;
$address = @args > 0 ? _cast1() : _cast0() if $address == 0;
my $function = FFI::Platypus::Function::Function->new($self, $address, $self->{abi}, $fixed_arg_count, $ret, @args);
$wrapper
? FFI::Platypus::Function::Wrapper->new($function, $wrapper)
: $function;
}
sub _function_meta
{
# NOTE: may be upgraded to a documented function one day,
# but shouldn't be used externally as we will rename it
# if that happens.
my($self, $name, $meta, $args, $ret) = @_;
$args = ['opaque','int',@$args];
$self->function(
$name, $args, $ret, sub {
my $xsub = shift;
$xsub->($meta, scalar(@_), @_);
},
);
}
=head2 attach
$ffi->attach($name => \@argument_types => $return_type);
$ffi->attach([$c_name => $perl_name] => \@argument_types => $return_type);
$ffi->attach([$address => $perl_name] => \@argument_types => $return_type);
$ffi->attach($name => \@argument_types => $return_type, \&wrapper);
$ffi->attach([$c_name => $perl_name] => \@argument_types => $return_type, \&wrapper);
$ffi->attach([$address => $perl_name] => \@argument_types => $return_type, \&wrapper);
Find and attach a C function as a real live Perl xsub. The advantage of
attaching a function over using the L<function|/function> method is that
it is much much much faster since no object resolution needs to be done.
The disadvantage is that it locks the function and the L<FFI::Platypus>
instance into memory permanently, since there is no way to deallocate an
xsub.
If just one I<$name> is given, then the function will be attached in
Perl with the same name as it has in C. The second form allows you to
give the Perl function a different name. You can also provide an
address (the third form), just like with the L<function|/function>
method.
Examples:
$ffi->attach('my_function_name', ['int', 'string'] => 'string');
$ffi->attach(['my_c_function_name' => 'my_perl_function_name'], ['int', 'string'] => 'string');
my $string1 = my_function_name($int);
my $string2 = my_perl_function_name($int);
[version 0.20]
If the last argument is a code reference, then it will be used as a
wrapper around the attached xsub. The first argument to the wrapper
will be the inner xsub. This can be used if you need to verify/modify
input/output data.
Examples:
$ffi->attach('my_function', ['int', 'string'] => 'string', sub {
my($my_function_xsub, $integer, $string) = @_;
$integer++;
$string .= " and another thing";
my $return_string = $my_function_xsub->($integer, $string);
$return_string =~ s/Belgium//; # HHGG remove profanity
$return_string;
});
[version 0.91]
$ffi->attach($name => \@fixed_argument_types => \@var_argument_types, $return_type);
$ffi->attach($name => \@fixed_argument_types => \@var_argument_types, $return_type, \&wrapper);
As of version 0.91 you can attach a variadic functions, if it is supported
by the platform / libffi that you are using. For details see the C<function>
documentation. If not supported by the implementation then an exception
will be thrown.
=cut
sub attach
{
my $wrapper;
$wrapper = pop if ref $_[-1] eq 'CODE';
my $self = shift;
my $name = shift;
my $args = shift;
my $varargs;
$varargs = shift if defined $_[0] && ref($_[0]) eq 'ARRAY';
my $ret = shift;
my $proto = shift;
$ret = 'void' unless defined $ret;
my($c_name, $perl_name) = ref($name) ? @$name : ($name, $name);
croak "you tried to provide a perl name that looks like an address"
if $perl_name =~ /^-?[0-9]+$/;
my $function = $varargs
? $self->function($c_name, $args, $varargs, $ret, $wrapper)
: $self->function($c_name, $args, $ret, $wrapper);
if(defined $function)
{
$function->attach($perl_name, $proto);
}
$self;
}
=head2 closure
my $closure = $ffi->closure($coderef);
my $closure = FFI::Platypus->closure($coderef);
Prepares a code reference so that it can be used as a FFI closure (a
Perl subroutine that can be called from C code). For details on
closures, see L<FFI::Platypus::Type#Closures> and L<FFI::Platypus::Closure>.
=cut
sub closure
{
my($self, $coderef) = @_;
return undef unless defined $coderef;
croak "not a coderef" unless ref $coderef eq 'CODE';
require FFI::Platypus::Closure;
FFI::Platypus::Closure->new($coderef);
}
=head2 cast
my $converted_value = $ffi->cast($original_type, $converted_type, $original_value);
The C<cast> function converts an existing I<$original_value> of type
I<$original_type> into one of type I<$converted_type>. Not all types
are supported, so care must be taken. For example, to get the address
of a string, you can do this:
my $address = $ffi->cast('string' => 'opaque', $string_value);
Something that won't work is trying to cast an array to anything:
my $address = $ffi->cast('int[10]' => 'opaque', \@list); # WRONG
=cut
sub cast
{
$_[0]->function(0 => [$_[1]] => $_[2])->call($_[3]);
}
=head2 attach_cast
$ffi->attach_cast("cast_name", $original_type, $converted_type);
$ffi->attach_cast("cast_name", $original_type, $converted_type, \&wrapper);
my $converted_value = cast_name($original_value);
This function attaches a cast as a permanent xsub. This will make it
faster and may be useful if you are calling a particular cast a lot.
[version 1.26]
A wrapper may be added as the last argument to C<attach_cast> and works
just like the wrapper for C<attach> and C<function> methods.
=cut
sub attach_cast
{
my($self, $name, $type1, $type2, $wrapper) = @_;
my $caller = caller;
$name = join '::', $caller, $name unless $name =~ /::/;
if(defined $wrapper && ref($wrapper) eq 'CODE')
{
$self->attach([0 => $name] => [$type1] => $type2 => '$', $wrapper);
}
else
{
$self->attach([0 => $name] => [$type1] => $type2 => '$');
}
$self;
}
=head2 sizeof
my $size = $ffi->sizeof($type);
my $size = FFI::Platypus->sizeof($type);
Returns the total size of the given type in bytes. For example to get
the size of an integer:
my $intsize = $ffi->sizeof('int'); # usually 4
my $longsize = $ffi->sizeof('long'); # usually 4 or 8 depending on platform
You can also get the size of arrays
my $intarraysize = $ffi->sizeof('int[64]'); # usually 4*64
my $intarraysize = $ffi->sizeof('long[64]'); # usually 4*64 or 8*64
# depending on platform
Keep in mind that "pointer" types will always be the pointer / word size
for the platform that you are using. This includes strings, opaque and
pointers to other types.
This function is not very fast, so you might want to save this value as