试图与一个 C 库接口,该库采用一个带有一堆指向它在不同点调用的函数的指针的结构。
像这样:
struct callbacks {
int (*foo)(int);
int (*bar)(int);
}
int set_callbacks(callbacks *cbs);
Run Code Online (Sandbox Code Playgroud)
我可以进行回调:
sub foo-callback(int32 --> int32) {...}
sub bar-callback(int32 --> int32) {...}
Run Code Online (Sandbox Code Playgroud)
如果这样做会很酷:
class Callbacks is repr<CStruct>
{
has &.foo (int32 --> int32);
has &.bar (int32 --> int32);
}
Run Code Online (Sandbox Code Playgroud)
但事实并非如此。我正在尝试做一些事情:
class Callbacks is repr<CStruct>
{
has Pointer $.foo;
has Pointer $.bar;
}
Run Code Online (Sandbox Code Playgroud)
并将它们设置为nativecast(Pointer, &foo-callback)或一些,但我似乎无法强迫它们在那里。
除了编写一个小 C 函数来获取所有 Perl 6 函数指针并将它们插入 C 中的结构之外,还有什么方法可以做到这一点?
I still can't find an official way to do this, but I figured out a work-around.
Declare a version of sprintf that takes a function pointer of the right type (so it will set up the calling shim correctly), but cheat and have sprintf just give me back the pointer as a number (%lld) I can stuff in the Pointer.
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);
Run Code Online (Sandbox Code Playgroud)
If you have multiple callbacks with different signatures, just declare a sprintf for each C calling signature you need, e.g.:
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') {}
Run Code Online (Sandbox Code Playgroud)
and call the right sprintf-* for each callback pointer you need.
There is probably an easier way than going back and forth with the pointer to number to string to number to pointer, but this does the trick for now.