Как передать пользовательские данные в функцию обратного вызова - PullRequest
0 голосов
/ 02 мая 2018

Я работаю над интерфейсом NativeCall; есть функция C, которая принимает обратный вызов, определенный как:

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

Мой первый дубль был:

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

но при вызове этой функции выдается ошибка:

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

Если я игнорирую аргумент user_data, все работает, поэтому остальная часть объявления в порядке: я просто не могу передать какие-либо дополнительные данные в функцию обратного вызова.

В других случаях я использовал Buf для передачи блока (возможно) двоичных данных в функцию C, и это работало; Разница здесь заключается в функции обратного вызова. Есть идеи, как решить эту проблему?

(с использованием perl6 2018.03)

Ответы [ 2 ]

0 голосов
/ 05 апреля 2019

Я понимаю, что это старый вопрос, и вы, вероятно, давно использовали обходные пути, но в интересах поиска других людей с похожей проблемой, я опубликую свой ответ сейчас.

Мне приходилось делать это несколько раз для разных NativeCall библиотечных интерфейсов, поэтому я решил упаковать его в NativeHelpers :: Callback модуль.

Он предоставляет несколько простых методов, чтобы связать объект perl с CPointer и легко найти его из функции обратного вызова.

Это все не проверено, но что-то вроде этого должно работать для вашего случая:

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));           
0 голосов
/ 02 мая 2018

Я не уверен, как передать Buf в качестве пользовательских данных, поскольку Buf не является нативным типом. Но вместо этого вы можете использовать, например, CStruct:

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

Тогда объявление будет:

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

И обратный вызов может быть объявлен и определен, например:

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

Редактировать :

Вот обходной путь для передачи типа Perl 6, такого как Buf (т.е. не нативный тип), в обратный вызов с использованием замыкания. Например:

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

Затем объявите реальный обратный вызов my-callback следующим образом:

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

И вызвать библиотечную функцию следующим образом:

exif_content_foreach_entry( $content, &$callback, $data );
...