4

试图与一个 C 库接口,该库采用一个结构,该结构带有一堆指向它在各个点调用的函数的指针。

像这样的东西:

struct callbacks {
    int (*foo)(int);
    int (*bar)(int);
}

int set_callbacks(callbacks *cbs);

我可以进行回调:

sub foo-callback(int32 --> int32) {...}
sub bar-callback(int32 --> int32) {...}

如果这可行,那就太酷了:

class Callbacks is repr<CStruct>
{
    has &.foo (int32 --> int32);
    has &.bar (int32 --> int32);
}

但事实并非如此。我正在尝试做一些事情:

class Callbacks is repr<CStruct>
{
    has Pointer $.foo;
    has Pointer $.bar;
}

并将它们设置为nativecast(Pointer, &foo-callback)或类似,但我似乎无法强迫它们在那里。

除了编写一个小 C 函数来获取所有 Perl 6 函数指针并将它们插入 C 中的结构之外,还有什么方法可以做到这一点?

4

1 回答 1

5

我仍然找不到正式的方法来做到这一点,但我想出了一个解决方法。

声明一个sprintf使用正确类型的函数指针的版本(因此它将正确设置调用 shim),但作弊并让 sprintf 将指针作为数字(%lld)返回给我,我可以将其填充到指针中。

class Callbacks is repr<CStruct>
{
    has Pointer $.foo;
    has Pointer $.bar;

    sub sprintf(Blob, Str, & (int32 --> int32) --> int32) is native {}

    submethod BUILD(:&foo, :&bar)
    {
        my $buf = buf8.allocate(20);
        my $len = sprintf($buf, "%lld", &foo);
        $!foo := Pointer.new($buf.subbuf(^$len).decode.Int);
        $len = sprintf($buf, "%lld", &bar);
        $!bar := Pointer.new($buf.subbuf(^$len).decode.Int);
    }
}

sub foo-callback(int32 $x --> int32) {...}
sub bar-callback(int32 $x --> int32) {...}

my $callbacks = Callbacks.new(foo => &foo-callback,
                              bar => &bar-callback);

如果您有多个具有不同签名的回调,只需sprintf为您需要的每个 C 调用签名声明一个,例如:

sub sprintf-ii(Blob, Str, & (int32 --> int32) --> int32)
    is native is symbol('sprintf') {}

sub sprintf-isi(Blob, Str, & (int32, Str --> int32) --> int32)
    is native is symbol('sprintf') {}

sprintf-*为您需要的每个回调指针调用正确的。

可能有一种比使用指向数字的指针到字符串到数字到指针的指针更简单的方法,但现在这可以解决问题。

于 2018-02-15T00:41:29.397 回答