Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass user data to a callback function

I'm working on a NativeCall interface; there's a C function which accepts a callback, defined as:

typedef void (* ExifContentForeachEntryFunc) (ExifEntry *, void *user_data);
void exif_content_foreach_entry (ExifContent *content, ExifContentForeachEntryFunc func, void *user_data);

My first take has been:

sub exif_content_foreach_entry(ExifContent $exifcontent, &func (ExifEntry $entry, Buf $data), Buf $user_data) is native(LIB) is export { * }

but when invoked this function generates an error:

Internal error: unhandled dyncall callback argument type
  in method CALL-ME at /opt/rakudo-pkg/share/perl6/sources/24DD121B5B4774C04A7084827BFAD92199756E03 (NativeCall) line 588

If I ignore the user_data argument, everything works, so the rest of the declaration is fine: I just don't get to pass any additional data to the callback function.

On other occasions I used a Buf to pass a block of (possibly) binary data to a C function and it worked; the difference here is the callback function. Any idea how to solve this problem?

(using perl6 2018.03)

like image 267
Fernando Santagata Avatar asked May 02 '18 07:05

Fernando Santagata


2 Answers

I am not sure how to pass a Buf as user data since Buf is not a native type. But you could use for instance a CStruct instead:

class UserData is repr('CStruct') {
    has int32 $.dummy;
}

Then the declaration would be:

sub exif_content_foreach_entry(
    ExifContent $exifcontent, 
    &func (ExifEntry $entry, UserData $data),
    UserData $user_data) is native(LIB) is export { * }

And the callback can be declared and defined as for example:

sub my-callback (ExifEntry $entry, UserData $data) {
    say "In callback";
    say "Value of data: ", $data.dummy;
}

Edit:

Here is a workaround to pass a Perl 6 type like Buf (i.e not a native type) to the callback using a closure. For example:

my $buf = Buf.new( 1, 2, 3);
my $callback = my sub (ExifEntry $entry, UserData $data) {
    my-callback( $entry, $buf);
}

Then declare the real callback my-callback like this:

sub my-callback (ExifEntry $entry, Buf $data) {
    say "In callback";
    say "Value of data: ", $data;
}

And call the library function like this:

exif_content_foreach_entry( $content, &$callback, $data );
like image 65
Håkon Hægland Avatar answered Sep 29 '22 00:09

Håkon Hægland


I realize this is an old question and you've probably implemented workarounds long ago, but in the interest of catching others with a similar issue, I'll post my answer now.

I've had to do this several times for different NativeCall library interfaces, so I decided to package it up into a NativeHelpers::Callback module.

It provides some simple methods to associate a perl object with an CPointer and easily look it up from within a callback function.

This is all untested, but something like this should work for your case:

use NativeHelpers::Callback :cb;                                                

class ExifEntry is repr('CPointer') { ... }                                     

sub exif_content_foreach_entry(ExifContent $exifcontent,                        
    &func (ExifEntry $entry, int64), int64) is native(LIB) is export { * }      

class MyPerlObject {                                                            
    has $.entry;                                                                
    has Buf $.buf;                                                              
    ...                                                                         
}                                                                               

sub MyCallBack(ExifEntry $entry, int64 $id) {                                   
    my MyPerlObject $object = cb.lookup($id);
    ... do stuff with $object ...                                   
}                                                                               

my ExifEntry $entry = ...;                                                      

my MyPerlObject $object = MyPerlObject.new(:$entry, buf => ...);                
cb.store($object, $entry);                                                      
exif_content_foreach_entry($exifcontent, &MyCallBack, cb.id($entry));           
like image 29
Curt Tilmes Avatar answered Sep 29 '22 00:09

Curt Tilmes