diff --git a/Dockerfile b/Dockerfile index c07d02f..47c727d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,6 +26,8 @@ RUN \ python3.8 \ ruby2.7 \ ruby2.7-dev \ + perl \ + cpanminus \ \ && rm -rf /var/lib/apt/lists/* diff --git a/examples/.gitignore b/examples/.gitignore index 27d5459..a9b1b6c 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -19,3 +19,7 @@ test-output/ # C# */csharp-example + +# Perl +.perl-dependencies +/local diff --git a/examples/Makefile b/examples/Makefile index f6ad625..b2bc1c9 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -49,6 +49,7 @@ TEST_HASKELL := tests_haskell.mk TEST_NODEJS := tests_nodejs.mk TEST_CSHARP := tests_csharp.mk TEST_JULIA := tests_julia.mk +TEST_PERL := tests_perl.mk TEST_ALL_LANGUAGES := tests_all.mk dir := integers @@ -66,7 +67,7 @@ include ${dir}/rules.mk dir := vector_return include ${dir}/rules.mk -all: c ruby python haskell nodejs csharp julia +all: c ruby python haskell nodejs csharp julia perl .PHONY: all # Test only a single language @@ -81,5 +82,6 @@ haskell: nodejs: csharp: julia: +perl: -.PHONY: c ruby python haskell nodejs csharp julia +.PHONY: c ruby python haskell nodejs csharp julia perl diff --git a/examples/cpanfile b/examples/cpanfile new file mode 100644 index 0000000..71fc6aa --- /dev/null +++ b/examples/cpanfile @@ -0,0 +1,3 @@ +requires 'FFI::Platypus', '1.00'; +requires 'FFI::Platypus::Lang::Rust'; +requires 'FFI::Platypus::Type::PtrObject'; diff --git a/examples/integers/src/main.pl b/examples/integers/src/main.pl new file mode 100644 index 0000000..ff58dd4 --- /dev/null +++ b/examples/integers/src/main.pl @@ -0,0 +1,13 @@ +use strict; +use warnings; +use 5.010; +use FFI::Platypus 1.00; + +my $ffi = FFI::Platypus->new( + api => 1, + lang => 'Rust', +); +$ffi->find_lib( lib => 'integers'); +$ffi->attach( addition => ['u32','u32'] => 'u32' ); + +say addition(1,2); diff --git a/examples/objects/src/main.pl b/examples/objects/src/main.pl new file mode 100644 index 0000000..10284d2 --- /dev/null +++ b/examples/objects/src/main.pl @@ -0,0 +1,40 @@ +use strict; +use warnings; +use 5.014; +use FFI::Platypus 1.00; + +my $ffi = FFI::Platypus->new( + api => 1, + lang => 'Rust', +); +$ffi->mangler(sub { "zip_code_database_$_[0]" }); +$ffi->find_lib( lib => 'objects' ); +$ffi->load_custom_type('::PtrObject' => 'ZipCodeDatabase' => 'ZipCodeDatabase' ); + +package ZipCodeDatabase { + + $ffi->attach( new => [] => 'opaque' => sub { + my($xsub, $class) = @_; + my $ptr = $xsub->(); + bless { ptr => $ptr }, $class; + }); + + $ffi->attach( populate => ['ZipCodeDatabase']); + $ffi->attach( population_of => ['ZipCodeDatabase','string'] => 'u32'); + + $ffi->attach( free => ['opaque'] => sub { + my($xsub, $self) = @_; + if(my $ptr = delete $self->{ptr}) { + $xsub->($ptr); + } + }); + +} + +my $database = ZipCodeDatabase->new; + +$database->populate; +my $pop1 = $database->population_of("90210"); +my $pop2 = $database->population_of("20500"); + +say $pop1 - $pop2; diff --git a/examples/slice_arguments/src/main.pl b/examples/slice_arguments/src/main.pl new file mode 100644 index 0000000..2a39f39 --- /dev/null +++ b/examples/slice_arguments/src/main.pl @@ -0,0 +1,17 @@ +use strict; +use warnings; +use 5.010; +use FFI::Platypus 1.00; + +my $ffi = FFI::Platypus->new( + api => 1, + lang => 'Rust', +); +$ffi->find_lib( lib => 'slice_arguments' ); +$ffi->attach( sum_of_even => [ 'u32[]', 'isize' ] => 'u32' => sub { + my($xsub, $n) = @_; + my $len = @$n; + return $xsub->($n, $len); +}); + +say sum_of_even([1,2,3,4,5,6]); diff --git a/examples/string_arguments/src/main.pl b/examples/string_arguments/src/main.pl new file mode 100644 index 0000000..f9fed09 --- /dev/null +++ b/examples/string_arguments/src/main.pl @@ -0,0 +1,13 @@ +use strict; +use warnings; +use 5.010; +use FFI::Platypus 1.00; + +my $ffi = FFI::Platypus->new( + api => 1, + lang => 'Rust', +); +$ffi->find_lib( lib => 'string_arguments' ); +$ffi->attach( how_many_characters => [ 'string' ] => 'u32' ); + +say how_many_characters("göes to élevên"); diff --git a/examples/string_return/src/main.pl b/examples/string_return/src/main.pl new file mode 100644 index 0000000..2c1723c --- /dev/null +++ b/examples/string_return/src/main.pl @@ -0,0 +1,23 @@ +use strict; +use warnings; +use 5.010; +use FFI::Platypus 1.00; + +my $ffi = FFI::Platypus->new( + api => 1, + lang => 'Rust', +); +$ffi->find_lib( lib => 'string_return' ); + +$ffi->attach_cast( opaque_to_string => 'opaque' => 'string'); +$ffi->attach( theme_song_free => ['opaque'] ); +$ffi->attach( theme_song_generate => ['u8'] => 'opaque' => sub { + my($xsub, $length) = @_; + my $ptr = $xsub->($length); + my $str = opaque_to_string($ptr); + utf8::decode($str); + theme_song_free($ptr); + return $str; +}); + +say theme_song_generate(5); diff --git a/examples/tests_all.mk b/examples/tests_all.mk index c66befe..a943cbe 100644 --- a/examples/tests_all.mk +++ b/examples/tests_all.mk @@ -5,3 +5,4 @@ include ${TEST_HASKELL} include ${TEST_NODEJS} include ${TEST_CSHARP} include ${TEST_JULIA} +include ${TEST_PERL} diff --git a/examples/tests_perl.mk b/examples/tests_perl.mk new file mode 100644 index 0000000..e603eb8 --- /dev/null +++ b/examples/tests_perl.mk @@ -0,0 +1,26 @@ +include ${COMMON_TEST_RULES} + +# System-wide packages need to be installed. We run the appropriate +# package manager and leave a small cookie file to indicate +# success. The execution depends on the cookie file. + +ifndef PERL_DEPENDENCIES + +PERL_DEPENDENCIES := .perl-dependencies + +${PERL_DEPENDENCIES}: cpanfile + cpanm --installdeps -L local . + touch $@ + +endif + +${TEST_DIR_${d}}/perl-test: LIB_DIR := ${LIB_DIR_${d}} +${TEST_DIR_${d}}/perl-test: ${d}/src/main.pl ${TEST_DIR_${d}} ${LIB_${d}} ${PERL_DEPENDENCIES} + LD_LIBRARY_PATH=${LIB_DIR} perl -Ilocal/lib $< > $@ + +.PHONY: perl-test_${d} +perl-test_${d}: EXPECTED := ${d}/expected-output +perl-test_${d}: ${TEST_DIR_${d}}/perl-test + diff -q ${EXPECTED} $< + +perl: perl-test_${d} diff --git a/examples/tuples/rules.mk b/examples/tuples/rules.mk index 0e15965..90ab4a8 100644 --- a/examples/tuples/rules.mk +++ b/examples/tuples/rules.mk @@ -8,6 +8,7 @@ include ${TEST_RUBY} include ${TEST_PYTHON} include ${TEST_NODEJS} include ${TEST_CSHARP} +include ${TEST_PERL} d := ${dirstack_${sp}} sp := ${basename ${sp}} diff --git a/examples/tuples/src/main.pl b/examples/tuples/src/main.pl new file mode 100644 index 0000000..c2a480b --- /dev/null +++ b/examples/tuples/src/main.pl @@ -0,0 +1,35 @@ +use strict; +use warnings; +use 5.014; +use FFI::Platypus 1.00; + +my $ffi = FFI::Platypus->new( + api => 1, + lang => 'Rust', +); +$ffi->find_lib( lib => 'tuples' ); + +package Tuple { + use FFI::Platypus::Record; + + use overload + '""' => sub { shift->to_string }, + bool => sub { 1 }, fallback => 1; + + record_layout_1( + $ffi, + u32 => 'x', + u32 => 'y', + ); + + sub to_string { + my($self) = @_; + sprintf "(%d,%d)", $self->x, $self->y; + } +} + +$ffi->attach( flip_things_around => ['record(Tuple)'] => 'record(Tuple)' ); + +my $tup = Tuple->new( x => 10, y => 20 ); + +say flip_things_around($tup); diff --git a/site/integers/index.md b/site/integers/index.md index 5c29d8b..1ff916c 100644 --- a/site/integers/index.md +++ b/site/integers/index.md @@ -115,3 +115,9 @@ Windows by copying `target\debug\integers.dll` to the current directory and running `julia src\main.jl`. [ccall]: https://docs.julialang.org/en/v1/base/c/#ccall + +## Perl + +{% example src/main.pl %} + +TODO diff --git a/site/objects/index.md b/site/objects/index.md index 916b6c8..48403cb 100644 --- a/site/objects/index.md +++ b/site/objects/index.md @@ -166,3 +166,9 @@ creating and freeing the object. With the `do` syntax, the user code becomes similar to one using Python's `with` syntax. Alternatively, the programmer can use the other constructor and call the method `close` when it is no longer needed. + +## Perl + +{% example src/main.pl %} + +TODO diff --git a/site/slice_arguments/index.md b/site/slice_arguments/index.md index 5154faa..80e1fd2 100644 --- a/site/slice_arguments/index.md +++ b/site/slice_arguments/index.md @@ -116,3 +116,9 @@ to the rule, and should be passed with a plain `Ptr` and length. [julia-Ptr]: https://docs.julialang.org/en/v1/base/c/#Core.Ptr [julia-Ref]: https://docs.julialang.org/en/v1/base/c/#Core.Ref [julia-refptr]: https://docs.julialang.org/en/v1/manual/calling-c-and-fortran-code/#When-to-use-T,-Ptr{T}-and-Ref{T}-1 + +## Perl + +{% example src/main.pl %} + +TODO diff --git a/site/string_arguments/index.md b/site/string_arguments/index.md index 11839e5..a83185d 100644 --- a/site/string_arguments/index.md +++ b/site/string_arguments/index.md @@ -97,3 +97,9 @@ Julia strings (of base type `AbstractString`) are automatically converted to C strings. The `Cstring` type from Julia is compatible with the Rust type `CStr`, as it also assumes a `NUL` terminator byte and does not allow `NUL` bytes embedded in the string. + +## Perl + +{% example src/perl.pl %} + +TODO diff --git a/site/string_return/index.md b/site/string_return/index.md index d5fbb6a..b2d169b 100644 --- a/site/string_return/index.md +++ b/site/string_return/index.md @@ -96,3 +96,9 @@ by Julia, and transfers the Rust string back afterwards. The resource is kept alive in Julia. [julia-objects]: ../objects#julia + +## Perl + +{% example src/main.pl %} + +TODO diff --git a/site/tuples/index.md b/site/tuples/index.md index dd1bf99..f07ce7e 100644 --- a/site/tuples/index.md +++ b/site/tuples/index.md @@ -81,3 +81,9 @@ will store each member inline and will be passed to the native function by value. [julia-isbits]: https://docs.julialang.org/en/v1/base/base/#Base.isbits + +## Perl + +{% example src/main.pl %} + +TODO