-
Notifications
You must be signed in to change notification settings - Fork 23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for record with callbacks? #177
Comments
yeah, atm closures aren't supported as record members. However, you can use the opaque type instead and cast the closure value to opaque: $fdf->f($ffi->cast( '(double,opaque)->double' => 'opaque', $f_func)); It would be nice to have broader support for these and other types for the record type though, so I'll keep this open so that it can be worked out when there is time. |
Keep in mind that you need to make sure that |
Thanks! The cast seems to work fine for $fdf->fdf(
$ffi->cast( '(double,opaque,opaque*,opaque*)->double' => 'opaque', $fdf_func)
); But this gives error:
|
right closures have a limited support for types. Some types like record couldn't be supported the way they are currently implemented. Others are tricky because of memory ownership (returning a string from a closure probably won't ever be supported). Pointer types probably could be supported in the future, but it hasn't been a priority so far. Most of these thing can be dealt with using a cast inside the closure though. # closure that takes an opaque* as an argument
$ffi->closure(sub {
my($a, $b, $c, $d) = @_;
$c = $ffi->cast('opaque' => 'opaque*', $c);
...
});
# closure that returns a string
$ffi->closure(sub {
...
state $ret; # cannot be my
$ret = ...;
return $ffi->cast('string' => 'opaque', $ret);
}); |
Interesting! Ok so if I try: my $fdf_func = $ffi->closure(
sub {
my( $x, $params, $y, $dy ) = @_;
$y = $ffi->cast('opaque' => 'opaque*', $y);
$dy = $ffi->cast('opaque' => 'opaque*', $dy);
$$y = $x * $x * $x;
$$dy = 3.0 * $x * $x;
}
); I now get error:
where line no 58 is this: $$y = $x * $x * $x; |
What you want is to write a double to an arbitrary location of memory which Perl doesn't allow, but you can use the C memcpy function. Something like: (not tested) # (double, opaque, opaque, opaque)->void
my $fdf_func = $ffi->closure(
sub {
my( $x, $params, $y, $dy ) = @_;
my $memcpy = $ffi->function( memcpy => [ 'opaque', 'double*', 'size_t']=> 'opaque');
my $size = $ffi->sizeof('double');
$memcpy->($y, \($x*$x*$x), $size);
$memcpy->($dy, \(3.0*$x*$x), $size);
}
); (of course if you expect this to be called frequently, which presumably it will, you will want to attach memcpy and save the sizeof value. |
Wow! This actually works now: package GSL::RootSolver::GslFunctionFDF;
use FFI::Platypus::Record;
record_layout(
qw [
opaque f
opaque df
opaque fdf
opaque params
]
);
package main;
use feature qw(say);
use strict;
use warnings;
use Data::Printer;
use FFI::Platypus;
use FFI::CheckLib;
my $ffi = FFI::Platypus->new();
$ffi->lib( find_lib_or_exit( lib => 'gsl' ) );
$ffi->type('opaque' => 'gsl_root_fdfsolver_type_ptr');
$ffi->type('opaque' => 'gsl_root_fdfsolver');
$ffi->attach(
'gsl_root_fdfsolver_alloc',
[ 'gsl_root_fdfsolver_type_ptr' ] => 'gsl_root_fdfsolver'
);
my $solver_type = $ffi->cast(
'opaque' => 'opaque*',
$ffi->find_symbol('gsl_root_fdfsolver_newton')
);
my $solver = gsl_root_fdfsolver_alloc( $$solver_type );
my $fdf = GSL::RootSolver::GslFunctionFDF->new();
my $f_func = $ffi->closure(
sub {
my( $x, $params ) = @_;
return $x*$x*$x;
}
);
$fdf->f($ffi->cast( '(double,opaque)->double' => 'opaque', $f_func) );
my $df_func = $ffi->closure(
sub {
my( $x, $params ) = @_;
return 3 * $x * $x;
}
);
$fdf->df($ffi->cast( '(double,opaque)->double' => 'opaque', $df_func) );
my $fdf_func = $ffi->closure(
sub {
my( $x, $params, $y, $dy ) = @_;
my $memcpy = $ffi->function(
memcpy => [ 'opaque', 'double*', 'size_t']=> 'opaque'
);
my $size = $ffi->sizeof('double');
$memcpy->($y, \($x*$x*$x), $size);
$memcpy->($dy, \(3.0*$x*$x), $size);
}
);
$fdf->fdf(
$ffi->cast( '(double,opaque,opaque,opaque)->double' => 'opaque', $fdf_func)
);
$fdf->params( undef );
my $initial_guess = 5.0;
$ffi->type("record(GSL::RootSolver::GslFunctionFDF)" => 'gsl_function_fdf');
$ffi->attach(
'gsl_root_fdfsolver_set',
[ 'gsl_root_fdfsolver', 'gsl_function_fdf', 'double' ] => 'gsl_root_fdfsolver'
);
$ffi->attach(
'gsl_root_fdfsolver_iterate', [ 'gsl_root_fdfsolver' ] => 'int'
);
$ffi->attach(
'gsl_root_fdfsolver_root', [ 'gsl_root_fdfsolver' ] => 'double'
);
$ffi->attach(
'gsl_root_fdfsolver_free', [ 'gsl_root_fdfsolver' ] => 'void'
);
gsl_root_fdfsolver_set ($solver, $fdf, $initial_guess);
for my $i (1..20) {
gsl_root_fdfsolver_iterate ($solver);
my $root = gsl_root_fdfsolver_root ($solver);
printf "%.8f\n", $root;
}
gsl_root_fdfsolver_free ($solver);
Output
|
I am pretty sure we can support record pass-by-value into a closure, but not pointer to a record (at least based on the current record implementation). |
Revisiting some old tickets here and using structs from
|
#312 adds support for passing a record into a closure. Passing a pointer into a closure already worked, but due to limitations in the record interface, that is actually a copy too, so records can only be passed into a closure by-value, and they are read-only. For passing a pointer to a record into a closure it makes more sense for the closure to take a opaque and cast that into a |
I have this C program using the GNU scientific library (the library can be installed on Ubuntu from package
libgsl-dev
):The above can be compiled using
gcc -Wall -o solve solve.c -lgsl -lgslcblas -lm
and produces output:I started implementing this in Perl using
FFI::Platypus
:When I run this:
So callbacks in records is not implemented yet? Would it be difficult to add support for callbacks passed in records like this?
The text was updated successfully, but these errors were encountered: