在XS中为C库注册多个Perl子引用

Ada*_*ott 5 perl xs

无论perlcall(在"策略存储回调上下文信息"一节),并延伸和嵌入Perl(以下简称"回调"一节)列出了3点不同的方式来处理从XS/C调用的Perl子例程:

  1. 马上:XS打电话
  2. 延迟:将子ref保存为SV*以供日后使用
  3. 多个:保存n个子参考以供日后使用

上面针对#3列出的示例和细节使用XS中的散列来将子ref与特定C函数相关联,但是它们预定义了固定数量的C函数,这是不合适的.

我正在使用一个XS接口到一个C库,它使用带有可选参数的回调/函数指针,例如:

  blah(custom_type *o, void (*func) (void *data, int more_data), const void * data);
Run Code Online (Sandbox Code Playgroud)

这个库中的C blah将最终调用传递给它的函数以及传入的数据.

如果可能的话,我想将C API的一对一映射到Perl.例如

  blah($o, \&func, $data);
Run Code Online (Sandbox Code Playgroud)

目前,我上面有#2,但是另一次调用blah()会覆盖已保存的SV*.

我将如何实施上述#3?

Ada*_*ott 1

这是我想出的解决方案:

此 C 库中的大多数回调将采用用户提供的 void * 并将其作为第一个参数传递。所以我将 SV * 和用户提供的数据保存在一个结构中:

typedef struct __saved_callback {
    SV   *func;
    void *data;
} _saved_callback;
Run Code Online (Sandbox Code Playgroud)

我的 XS 函数将分配一个 _saved_callback 结构,并将其作为第一个参数传递给 call_perl_sub() 以及 Perl 子引用和用户假设的数据。

void
blah(obj, func, data)
    whatever *obj
    void *func
    void *data
    CODE:
        _saved_callback *sc = NULL;
        Newx(sc, 1, _saved_callback);
        sc->func = (SV *)func;
        sc->data = data;
        blah(obj, call_perl_sub, sc);
Run Code Online (Sandbox Code Playgroud)

然后调用 Perl 子引用(我省略了用户提供的数据参数的堆​​栈操作):

void call_perl_sub(void *data) {
    dSP;
    int count;
    _saved_callback *perl_saved_cb = data;

    count = call_sv(perl_saved_cb->func, G_DISCARD);
    if ( count != 0 )
        croak("Expected 0 value got %d\n", count);
}
Run Code Online (Sandbox Code Playgroud)