Как передать пользовательские данные в функцию обратного вызова

Я работаю над интерфейсом 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)


person Fernando Santagata    schedule 02.05.2018    source источник
comment
@raiph Я не знаю, почему у меня сложилось такое впечатление. Заметил и изменил. Спасибо!   -  person jjmerelo    schedule 02.05.2018


Ответы (2)


Я не уверен, как передать 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 );
person Håkon Hægland    schedule 02.05.2018
comment
Ваше решение работает с использованием int32, но я хотел разрешить пользователям модулей передавать любые данные, поэтому я предпочитаю что-то вроде Buf или Blob. В другом месте я успешно использовал Buf, когда тип объявления C был void *, следовательно, это было первое, что я попробовал здесь. - person Fernando Santagata; 03.05.2018

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

Мне приходилось делать это несколько раз для разных интерфейсов библиотеки 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));           
person Curt Tilmes    schedule 05.04.2019
comment
Интересный модуль! Хотя в моем случае я должен передать Perl6 Str, а не собственное значение, поэтому, если я правильно понял, я не могу использовать ваш модуль. - person Fernando Santagata; 09.04.2019
comment
Это должно быть хорошо - просто cb.store() Str, связанная с вашим указателем объекта C, затем зарегистрируйте обратный вызов с cb.id() этого указателя. Затем вызовите cb.lookup() в обратном вызове, и он вернет Str. - person Curt Tilmes; 09.04.2019
comment
В моем случае у меня нет указателя объекта C: функция C, которую я вызываю, имеет три аргумента: первый аргумент, который будет сканироваться внутри, функция обратного вызова, которая вызывается, когда сканер что-то находит, и дополнительный указатель. Функция передаст этот указатель обратному вызову как есть. Я хочу передать в функцию переменную Str, которая, в свою очередь, передаст ее обратному вызову. [продолжает] - person Fernando Santagata; 09.04.2019
comment
cb.store() ожидает два аргумента, но у меня есть только одна переменная Str; если я попробую что-то вроде cb.store($var, $var), я получу ошибку: Native call expected return type with CPointer, CStruct, CArray, or VMArray representation, but got a P6opaque (Str). Это происходит от метода cb.id(), вызываемого cb.store(), который пытается вызвать nativecast(int64, $thing), передавая Str. - person Fernando Santagata; 09.04.2019
comment
Хм... У меня всегда есть какая-то вещь C, с которой я хочу связать обратный вызов. Затем в обратном вызове он использует эту штуку для поиска данных. Обычно я связываю что-то с Perl CStruct или CPointer, какой-то дескриптор, который обратный вызов получает, чтобы различать несколько экземпляров. - person Curt Tilmes; 09.04.2019