【问题标题】:How to pass user data to a callback function如何将用户数据传递给回调函数
【发布时间】:2018-05-02 07:46:34
【问题描述】:

我正在开发一个 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)

【问题讨论】:

  • @raiph 我不知道我为什么会有这种印象。注意到并改变了。谢谢!

标签: c callback raku nativecall


【解决方案1】:

我不确定如何将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 );

【讨论】:

  • 您的解决方案使用 int32 工作,但我想让模块用户传递任何类型的数据,所以我更喜欢像 Buf 或 Blob 这样的东西。在其他地方,当 C 声明类型为 void * 时,我成功使用了 Buf,因此这是我在这里尝试的第一件事。
【解决方案2】:

我意识到这是一个老问题,您可能很久以前就实施了解决方法,但为了让其他人遇到类似问题,我现在将发布我的答案。

对于不同的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));           

【讨论】:

  • 有趣的模块!虽然在我的情况下,我必须传递的是 Perl6 Str,而不是本机值,所以如果我理解正确,我不能使用你的模块。
  • 应该没问题 -- 只需 cb.store() 与您的 C 对象指针关联的 Str,然后使用该指针的 cb.id() 注册回调。然后在回调中调用cb.lookup(),它会返回Str。
  • 在我的例子中,我没有 C 对象指针:我调用的 C 函数有三个参数:第一个参数将在内部进行扫描,一个回调函数在扫描仪发现某些内容时调用,以及一个附加的指针。该函数将“按原样”将该指针传递给回调。我想将一个 Str 变量传递给函数,该函数又将它传递给回调。 [继续]
  • 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.store() 调用的cb.id() 方法,该方法试图调用nativecast(int64, $thing) 沿Str 传递。
  • 嗯.. 我总是想把回调关联起来。然后在回调中,它使用那个东西来查找数据。通常是我与 Perl CStruct 或 CPointer 相关联的东西,回调用来区分多个实例的某种句柄。
猜你喜欢
  • 1970-01-01
  • 2013-06-06
  • 2013-02-07
  • 1970-01-01
  • 2022-01-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多