Codebase list libffi-platypus-perl / 7836a81
rm ::Declare Graham Ollis authored 3 years ago Graham✈️✈️ committed 3 years ago
5 changed file(s) with 3 addition(s) and 673 deletion(s). Raw diff Collapse all Expand all
00 Revision history for {{$dist->name}}
11
22 {{$NEXT}}
3 - FFI::Platypus::Declare is no longer part of this distribution. It is
4 no available from FFI-Platypus-Declare instead. It has already
5 been discouraged for quite some time. (gh#285)
36 - Fix bug where bundled .so name could incorrectly get double colons (::)
47 in the name in development mode only. This is probably only a problem
58 on Windows. (gh#284)
4343 "lib/FFI/Platypus/Closure.pm" => "\$(INST_LIB)/FFI/Platypus/Closure.pm",
4444 "lib/FFI/Platypus/Constant.pm" => "\$(INST_LIB)/FFI/Platypus/Constant.pm",
4545 "lib/FFI/Platypus/DL.pm" => "\$(INST_LIB)/FFI/Platypus/DL.pm",
46 "lib/FFI/Platypus/Declare.pm" => "\$(INST_LIB)/FFI/Platypus/Declare.pm",
4746 "lib/FFI/Platypus/Function.pm" => "\$(INST_LIB)/FFI/Platypus/Function.pm",
4847 "lib/FFI/Platypus/Internal.pm" => "\$(INST_LIB)/FFI/Platypus/Internal.pm",
4948 "lib/FFI/Platypus/Lang.pm" => "\$(INST_LIB)/FFI/Platypus/Lang.pm",
+0
-428
lib/FFI/Platypus/Declare.pm less more
0 package FFI::Platypus::Declare;
1
2 use strict;
3 use warnings;
4 use 5.008004;
5 use Carp ();
6 use FFI::Platypus;
7
8 # ABSTRACT: Declarative interface to FFI::Platypus
9 # VERSION
10
11 =head1 SYNOPSIS
12
13 use FFI::Platypus::Declare 'string', 'int';
14
15 lib undef; # use libc
16 attach puts => [string] => int;
17
18 puts("hello world");
19
20 =head1 DESCRIPTION
21
22 This module is officially B<discouraged>. The idea was to provide a
23 simpler declarative interface without the need of (directly) creating
24 an L<FFI::Platypus> instance. In practice it is almost as complicated
25 and makes it difficult to upgrade to the proper OO interface if the
26 need arises. I have stopped using it mainly for this reason. It will
27 remain as part of the Platypus core distribution to keep old code working,
28 but you are encouraged to write new code using the OO interface.
29 Alternatively, you can try the Perl 6 inspired L<NativeCall>, which
30 provides most of the goals this module was intended for (that is
31 a simple interface at the cost of some power), without much of the
32 complexity. The remainder of this document describes the interface.
33
34 This module provides a declarative interface to L<FFI::Platypus>. It
35 provides a more concise interface at the cost of a little less power,
36 and a little more namespace pollution.
37
38 Any strings passed into the C<use> line will be declared as types and
39 exported as constants into your namespace, so that you can use them
40 without quotation marks.
41
42 Aliases can be declared using a list reference:
43
44 use FFI::Platypus [ 'int[48]' => 'my_integer_array' ];
45
46 Custom types can also be declared as a list reference (the type name
47 must include a ::):
48
49 use FFI::Platypus [ '::StringPointer' => 'my_string_pointer' ];
50 # short for FFI::Platypus::Type::StringPointer
51
52 =cut
53
54 our $ffi = {};
55 our $types = {};
56
57 sub _ffi_object
58 {
59 my($package, $filename) = caller(1);
60 $ffi->{$package} ||= FFI::Platypus->new->package($package,$filename);
61 }
62
63 =head1 FUNCTIONS
64
65 All functions are exported into your namespace. If you do not want that,
66 then use the OO interface (see L<FFI::Platypus>).
67
68 =head2 lib
69
70 lib $libpath;
71
72 Specify one or more dynamic libraries to search for symbols. If you are
73 unsure of the location / version of the library then you can use
74 L<FFI::CheckLib#find_lib>.
75
76 =cut
77
78 sub lib (@)
79 {
80 _ffi_object->lib(@_);
81 }
82
83 =head2 type
84
85 type $type;
86 type $type = $alias;
87
88 Declare the given type.
89
90 Examples:
91
92 type 'uint8'; # only really checks that uint8 is a valid type
93 type 'uint8' => 'my_unsigned_int_8';
94
95 =cut
96
97 sub type ($;$)
98 {
99 _ffi_object->type(@_);
100 }
101
102 =head2 custom_type
103
104 custom_type $alias => \%args;
105
106 Declare the given custom type. See L<FFI::Platypus::Type#Custom-Types>
107 for details.
108
109 =cut
110
111 sub custom_type ($$)
112 {
113 _ffi_object->custom_type(@_);
114 }
115
116 =head2 load_custom_type
117
118 load_custom_type $name => $alias, @type_args;
119
120 Load the custom type defined in the module I<$name>, and make an alias
121 with the name I<$alias>. If the custom type requires any arguments, they
122 may be passed in as I<@type_args>. See L<FFI::Platypus::Type#Custom-Types>
123 for details.
124
125 If I<$name> contains C<::> then it will be assumed to be a fully
126 qualified package name. If not, then C<FFI::Platypus::Type::> will be
127 prepended to it.
128
129 =cut
130
131 sub load_custom_type ($$;@)
132 {
133 _ffi_object->load_custom_type(@_);
134 }
135
136 =head2 type_meta
137
138 my $meta = type_meta $type;
139
140 Get the type meta data for the given type.
141
142 Example:
143
144 my $meta = type_meta 'int';
145
146 =cut
147
148 sub type_meta($)
149 {
150 _ffi_object->type_meta(@_);
151 }
152
153 =head2 attach
154
155 attach $name => \@argument_types => $return_type;
156 attach [$c_name => $perl_name] => \@argument_types => $return_type;
157 attach [$address => $perl_name] => \@argument_types => $return_type;
158
159 Find and attach a C function as a Perl function as a real live xsub.
160
161 If just one I<$name> is given, then the function will be attached in
162 Perl with the same name as it has in C. The second form allows you to
163 give the Perl function a different name. You can also provide a memory
164 address (the third form) of a function to attach.
165
166 Examples:
167
168 attach 'my_function', ['uint8'] => 'string';
169 attach ['my_c_function_name' => 'my_perl_function_name'], ['uint8'] => 'string';
170 my $string1 = my_function($int);
171 my $string2 = my_perl_function_name($int);
172
173 =cut
174
175 my $inner_counter = 0;
176
177 sub attach ($$$;$$)
178 {
179 my $wrapper;
180 $wrapper = pop if ref($_[-1]) eq 'CODE';
181 my($name, $args, $ret, $proto) = @_;
182
183 my($symbol_name, $perl_name) = ref $name ? (@$name) : ($name, $name);
184 my $function = _ffi_object->function($symbol_name, $args, $ret, $wrapper);
185 $function->attach($perl_name, $proto);
186 ();
187 }
188
189 =head2 closure
190
191 my $closure = closure $codeblock;
192
193 Create a closure that can be passed into a C function. For details on closures, see L<FFI::Platypus::Type#Closures>.
194
195 Example:
196
197 my $closure1 = closure { return $_[0] * 2 };
198 my $closure2 = closure sub { return $_[0] * 4 };
199
200 =cut
201
202 sub closure (&)
203 {
204 my($coderef) = @_;
205 require FFI::Platypus::Closure;
206 FFI::Platypus::Closure->new($coderef);
207 }
208
209 =head2 sticky
210
211 my $closure = sticky closure $codeblock;
212
213 Keyword to indicate the closure should not be deallocated for the life
214 of the current process.
215
216 If you pass a closure into a C function without saving a reference to it
217 like this:
218
219 foo(closure { ... }); # BAD
220
221 Perl will not see any references to it and try to free it immediately.
222 (this has to do with the way Perl and C handle responsibilities for
223 memory allocation differently). One fix for this is to make sure the
224 closure remains in scope using either C<my> or C<our>. If you know the
225 closure will need to remain in existence for the life of the process (or
226 if you do not care about leaking memory), then you can add the sticky
227 keyword to tell L<FFI::Platypus> to keep the thing in memory.
228
229 foo(sticky closure { ... }); # OKAY
230
231 =cut
232
233 sub sticky ($)
234 {
235 my($closure) = @_;
236 Carp::croak("usage: sticky \$closure")
237 unless defined $closure && ref($closure) eq 'FFI::Platypus::Closure';
238 $closure->sticky;
239 $closure;
240 }
241
242 =head2 cast
243
244 my $converted_value = cast $original_type, $converted_type, $original_value;
245
246 The C<cast> function converts an existing I<$original_value> of type
247 I<$original_type> into one of type I<$converted_type>. Not all types
248 are supported, so care must be taken. For example, to get the address
249 of a string, you can do this:
250
251 my $address = cast 'string' => 'opaque', $string_value;
252
253 =cut
254
255 sub cast ($$$)
256 {
257 _ffi_object->cast(@_);
258 }
259
260 =head2 attach_cast
261
262 attach_cast "cast_name", $original_type, $converted_type;
263 my $converted_value = cast_name($original_value);
264
265 This function creates a subroutine which can be used to convert
266 variables just like the L<cast|FFI::Platypus::Declare#cast> function
267 above. The above synopsis is roughly equivalent to this:
268
269 sub cast_name { cast($original_type, $converted_type, $_[0]) }
270 my $converted_value = cast_name($original_value);
271
272 Except that the L<attach_cast|FFI::Platypus::Declare#attach_cast>
273 variant will be much faster if called multiple times since the cast does
274 not need to be dynamically allocated on each instance.
275
276 =cut
277
278 sub attach_cast ($$$)
279 {
280 my($name, $type1, $type2) = @_;
281 my $caller = caller;
282 $name = join '::', $caller, $name;
283 _ffi_object->attach_cast($name, $type1, $type2);
284 }
285
286 =head2 sizeof
287
288 my $size = sizeof $type;
289
290 Returns the total size of the given type. For example to get the size
291 of an integer:
292
293 my $intsize = sizeof 'int'; # usually 4 or 8 depending on platform
294
295 You can also get the size of arrays
296
297 my $intarraysize = sizeof 'int[64]';
298
299 Keep in mind that "pointer" types will always be the pointer / word size
300 for the platform that you are using. This includes strings, opaque and
301 pointers to other types.
302
303 This function is not very fast, so you might want to save this value as
304 a constant, particularly if you need the size in a loop with many
305 iterations.
306
307 =cut
308
309 sub sizeof ($)
310 {
311 _ffi_object->sizeof($_[0]);
312 }
313
314 =head2 lang
315
316 lang $language;
317
318 Specifies the foreign language that you will be interfacing with. The
319 default is C. The foreign language specified with this attribute
320 changes the default native types (for example, if you specify
321 L<Rust|FFI::Platypus::Lang::Rust>, you will get C<i32> as an alias for
322 C<sint32> instead of C<int> as you do with L<C|FFI::Platypus::Lang::C>).
323
324 In the future this may attribute may offer hints when doing demangling
325 of languages that require it like L<C++|FFI::Platypus::Lang::CPP>.
326
327 =cut
328
329 sub lang ($)
330 {
331 _ffi_object->lang($_[0]);
332 }
333
334 =head2 abi
335
336 abi $abi;
337
338 Set the ABI or calling convention for use in subsequent calls
339 to L</attach>. May be either a string name or integer value
340 from L<FFI::Platypus#abis>.
341
342 =cut
343
344 sub abi ($)
345 {
346 _ffi_object->abi($_[0]);
347 }
348
349 sub import
350 {
351 my $caller = caller;
352 shift; # class
353
354 foreach my $arg (@_)
355 {
356 if(ref $arg)
357 {
358 if($arg->[0] =~ /::/)
359 {
360 _ffi_object->load_custom_type(@$arg);
361 no strict 'refs';
362 *{join '::', $caller, $arg->[1]} = sub () { $arg->[1] };
363 }
364 else
365 {
366 _ffi_object->type(@$arg);
367 no strict 'refs';
368 *{join '::', $caller, $arg->[1]} = sub () { $arg->[0] };
369 }
370 }
371 else
372 {
373 _ffi_object->type($arg);
374 no strict 'refs';
375 *{join '::', $caller, $arg} = sub () { $arg };
376 }
377 }
378
379 no strict 'refs';
380 *{join '::', $caller, 'lib'} = \&lib;
381 *{join '::', $caller, 'type'} = \&type;
382 *{join '::', $caller, 'type_meta'} = \&type_meta;
383 *{join '::', $caller, 'custom_type'} = \&custom_type;
384 *{join '::', $caller, 'load_custom_type'} = \&load_custom_type;
385 *{join '::', $caller, 'attach'} = \&attach;
386 *{join '::', $caller, 'closure'} = \&closure;
387 *{join '::', $caller, 'sticky'} = \&sticky;
388 *{join '::', $caller, 'cast'} = \&cast;
389 *{join '::', $caller, 'attach_cast'} = \&attach_cast;
390 *{join '::', $caller, 'sizeof'} = \&sizeof;
391 *{join '::', $caller, 'lang'} = \&lang;
392 *{join '::', $caller, 'abi'} = \&abi;
393 }
394
395 1;
396
397 =head1 SEE ALSO
398
399 =over 4
400
401 =item L<FFI::Platypus>
402
403 Object oriented interface to Platypus.
404
405 =item L<FFI::Platypus::Type>
406
407 Type definitions for Platypus.
408
409 =item L<FFI::Platypus::API>
410
411 Custom types API for Platypus.
412
413 =item L<FFI::Platypus::Memory>
414
415 memory functions for FFI.
416
417 =item L<FFI::CheckLib>
418
419 Find dynamic libraries in a portable way.
420
421 =item L<FFI::TinyCC>
422
423 JIT compiler for FFI.
424
425 =back
426
427 =cut
1616 require_ok 'FFI::Platypus::Closure';
1717 require_ok 'FFI::Platypus::Constant';
1818 require_ok 'FFI::Platypus::DL';
19 require_ok 'FFI::Platypus::Declare';
2019 require_ok 'FFI::Platypus::Function';
2120 require_ok 'FFI::Platypus::Internal';
2221 require_ok 'FFI::Platypus::Lang';
5655 ok -f 't/ffi_platypus_closure.t', 'test for FFI::Platypus::Closure';
5756 ok -f 't/ffi_platypus_constant.t', 'test for FFI::Platypus::Constant';
5857 ok -f 't/ffi_platypus_dl.t', 'test for FFI::Platypus::DL';
59 ok -f 't/ffi_platypus_declare.t', 'test for FFI::Platypus::Declare';
6058 ok -f 't/ffi_platypus_function.t', 'test for FFI::Platypus::Function';
6159 ok -f 't/ffi_platypus_internal.t', 'test for FFI::Platypus::Internal';
6260 ok -f 't/ffi_platypus_lang.t', 'test for FFI::Platypus::Lang';
+0
-242
t/ffi_platypus_declare.t less more
0 use strict;
1 use warnings;
2 use Test::More;
3 use FFI::CheckLib;
4 use FFI::Platypus::Declare;
5
6 my $libtest = find_lib lib => 'test', symbol => 'f0', libpath => 't/ffi';
7
8 subtest normal => sub {
9
10 { package Normal;
11
12 use FFI::Platypus::Declare;
13
14 lib $libtest;
15 attach 'f0', ['uint8'] => 'uint8';
16 attach [f0 => 'f1'], ['uint8'] => 'uint8';
17
18 attach [f0 => 'f0_wrap'] => ['uint8'] => 'uint8' => sub {
19 my($inner, $value) = @_;
20 $inner->($value+1)+2;
21 };
22
23 attach [f0 => 'f0_wrap2'] => ['uint8'] => 'uint8' => '$' => sub {
24 my($inner, $value) = @_;
25 $inner->($value+1)+2;
26 };
27 }
28
29 is Normal::f0(22), 22, 'f0(22) = 22';
30 is Normal::f1(22), 22, 'f1(22) = 22';
31 is Normal::f0_wrap(22), 25, 'f0_wrap(22) = 25';
32 is Normal::f0_wrap2(22), 25, 'f0_wrap2(22) = 25';
33 };
34
35 subtest prototype => sub {
36
37 my $value = eval q{
38 package ProtoType;
39
40 use FFI::Platypus::Declare;
41
42 BEGIN {
43 lib $libtest;
44 attach(f0 => ['uint8'] => 'uint8' => '$');
45 }
46
47 f0 22;
48 };
49
50 is $@, '', 'no compile error';
51 is $value, 22, 'f(22) = 22';
52
53 };
54
55 subtest 'with type aliases' => sub {
56
57 { package WithTypeAliases;
58
59 use FFI::Platypus::Declare
60 'string',
61 [int => 'myint'];
62
63 lib $libtest;
64 attach [my_atoi=>'atoi'], [string] => myint;
65 }
66
67 is WithTypeAliases::atoi("42"), 42, 'atoi("42") = 42';
68 };
69
70 subtest 'simple closure test' => sub {
71
72 { package ClosureSimple;
73
74 use FFI::Platypus::Declare;
75
76 our $closure = closure { $_[0]+1 };
77 }
78
79 isa_ok $ClosureSimple::closure, 'FFI::Platypus::Closure';
80 is $ClosureSimple::closure->(1), 2, 'closure.(1) = 2';
81 };
82
83 subtest 'abis' => sub {
84
85 my %abis = %{ FFI::Platypus->abis };
86
87 ok defined $abis{default_abi}, 'has a default ABI';
88
89 foreach my $abi (keys %abis)
90 {
91 subtest $abi => sub {
92 eval { abi $abi };
93 is $@, '', 'string';
94 eval { abi $abis{$abi} };
95 is $@, '', 'integer';
96 };
97 }
98
99 subtest 'bogus' => sub {
100 eval { abi 'bogus' };
101 like $@, qr{no such ABI: bogus}, 'string';
102 eval { abi 999999 };
103 like $@, qr{no such ABI: 999999}, 'integer';
104 };
105 };
106
107 subtest 'lang' => sub {
108
109 subtest C => sub {
110
111 package
112 Test1;
113
114 use Test::More;
115 use FFI::Platypus::Declare;
116
117 eval { type 'int' };
118 is $@, '', 'int is an okay type';
119 eval { type 'foo_t' };
120 isnt $@, '', 'foo_t is not an okay type';
121 note $@;
122 eval { type 'sint16' };
123 is $@, '', 'sint16 is an okay type';
124
125 };
126
127 subtest 'Foo constructor' => sub {
128
129 package
130 FFI::Platypus::Lang::Foo;
131
132 sub native_type_map
133 {
134 {
135 foo_t => 'sint16',
136 bar_t => 'uint32',
137 }
138 }
139
140 package
141 Test2;
142
143 use Test::More;
144 use FFI::Platypus::Declare;
145
146 lang 'Foo';
147
148 eval { type 'int' };
149 isnt $@, '', 'int is not an okay type';
150 note $@;
151 eval { type 'foo_t' };
152 is $@, '', 'foo_t is an okay type';
153 eval { type 'sint16' };
154 is $@, '', 'sint16 is an okay type';
155
156 is sizeof('foo_t'), 2, 'sizeof foo_t = 2';
157 is sizeof('bar_t'), 4, 'sizeof foo_t = 4';
158
159 };
160 };
161
162 subtest 'sizeof' => sub {
163 is sizeof 'uint32', 4, 'sizeof uint32 = 4';
164 is sizeof 'uint32[2]', 8, 'sizeof uint32[2] = 8';
165 };
166
167 subtest 'sticky' => sub {
168 package Foo;
169
170 use Test::More;
171 use FFI::Platypus::Declare
172 qw( uint8 void ),
173 ['(uint8)->uint8' => 'closure_t'];
174
175 lib $libtest;
176
177 attach [uint8_set_closure => 'set_closure'] => [closure_t] => void;
178 attach [uint8_call_closure => 'call_closure'] => [uint8] => uint8;
179
180 set_closure(sticky closure { $_[0] * 2 });
181 is call_closure(2), 4, 'call_closure(2) = 4';
182 };
183
184 subtest 'cast' => sub {
185 package Bar;
186
187 use Test::More;
188 use FFI::Platypus::Declare;
189
190 lib $libtest;
191
192 attach string_matches_foobarbaz => ['opaque'] => 'int';
193 attach string_return_foobarbaz => [] => 'opaque';
194 attach string_set_closure => ['opaque'] => 'void';
195 attach string_call_closure => ['string'] => 'void';
196
197 subtest 'cast from string to pointer' => sub {
198 my $string = "foobarbaz";
199 my $pointer = cast string => opaque => $string;
200
201 is string_matches_foobarbaz($pointer), 1, 'dynamic';
202
203 attach_cast cast1 => string => 'opaque';
204 my $pointer2 = cast1($string);
205
206 is string_matches_foobarbaz($pointer2), 1, 'static';
207
208 };
209
210 subtest 'cast from pointer to string' => sub {
211 my $pointer = string_return_foobarbaz();
212 my $string = cast opaque => string => $pointer;
213
214 is $string, "foobarbaz", "dynamic";
215
216 attach_cast cast2 => pointer => 'string';
217 my $string2 = cast2($pointer);
218
219 is $string2, "foobarbaz", "static";
220
221 };
222
223 subtest 'cast closure to opaque' => sub {
224 my $testname = 'dynamic';
225
226 my $closure = closure { is $_[0], "testvalue", $testname };
227 my $pointer = cast '(string)->void' => opaque => $closure;
228
229 string_set_closure($pointer);
230 string_call_closure("testvalue");
231
232 attach_cast 'cast3', '(string)->void' => 'opaque';
233 my $pointer2 = cast3($closure);
234
235 $testname = 'static';
236 string_set_closure($pointer2);
237 string_call_closure("testvalue");
238 };
239 };
240
241 done_testing;