fin*_*fin 7 rakudo nativecall raku
我正在尝试从用 C 编写的共享库返回结构。这是简单的代码,用于测试返回结构和简单的 int32,libstruct.c编译者gcc -shared -Wl,-soname,libstruct.so.1 -o libstruct.so.1 libstruct.c:
#include <stdint.h>
int32_t newint(int32_t arg) {
return arg;
}
struct MyStruct {
int32_t member;
};
struct MyStruct newstruct(int32_t arg) {
struct MyStruct myStruct;
myStruct.member = arg;
return(myStruct);
}
Run Code Online (Sandbox Code Playgroud)
我可以通过简单的 C 程序来使用这个库,该程序usestruct.c由以下代码编译gcc -o usestruct usestruct.c ./libstruct.so.1:
#include <stdio.h>
#include <stdint.h>
struct MyStruct {
int32_t member;
};
extern struct MyStruct newstruct(int32_t);
extern int32_t newint(int32_t);
int main() {
printf("%d\n", newint(42));
struct MyStruct myStruct;
myStruct = newstruct(42);
printf("%d\n", myStruct.member);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我可以使用 启动它LD_LIBRARY_PATH=./ ./usestruct,它工作正常,打印两个值。现在,让我们在 raku 中编写类似的程序usestruct.raku:
#!/bin/env raku
use NativeCall;
sub newint(int32) returns int32 is native('./libstruct.so.1') { * }
say newint(42);
class MyStruct is repr('CStruct') {
has int32 $.member;
}
sub newstruct(int32) returns MyStruct is native('./libstruct.so.1') { * }
say newstruct(42).member;
Run Code Online (Sandbox Code Playgroud)
首先打印42,但随后因分段错误而终止。
在 C 中,这个例子有效,但我不是 C 专家,也许我忘记了一些东西,一些编译选项?或者这是乐道的一个错误?
NativeCall 接口要求使用指针进行 C 结构的事务:
CStruct 对象通过引用传递给本机函数,本机函数也必须通过引用返回 CStruct 对象。
但是,您的 C 函数会按值返回一个新结构。然后,我猜想,这被尝试解释为内存地址,因为它需要一个指针,并尝试从野生内存区域读取/写入,因此出现段错误。
您可以将函数指针化为:
struct MyStruct* newstruct(int32_t val) {
/* dynamically allocating now */
struct MyStruct *stru = malloc(sizeof *stru);
stru->member = val;
return stru;
}
Run Code Online (Sandbox Code Playgroud)
位于#include <stdlib.h>最顶部的malloc. Raku 程序本质上与一些美学上的模数相同:
# prog.raku
use NativeCall;
my constant LIB = "./libstruct.so";
class MyStruct is repr("CStruct") {
has int32 $.member;
}
# C bridge
sub newint(int32) returns int32 is native(LIB) { * }
sub newstruct(int32) returns MyStruct is native(LIB) { * }
say newint(42);
my $s := newstruct(84);
say $s;
say $s.member;
Run Code Online (Sandbox Code Playgroud)
我们构建 lib 并运行 Raku 程序以获得
$ gcc -Wall -Wextra -pedantic -shared -o libstruct.so -fPIC mod_struct.c
$ raku prog.raku
42
MyStruct.new(member => 84)
84
Run Code Online (Sandbox Code Playgroud)
(擅自将C文件重命名为“mod_struct.c”)
看起来不错。但有一个问题:既然进行了动态分配,就产生了将其返还的责任。我们需要自己用 C 桥 Freer 来做:
当基于 CStruct 的类型用作本机函数的返回类型时,GC 不会为您管理内存。
所以
$ gcc -Wall -Wextra -pedantic -shared -o libstruct.so -fPIC mod_struct.c
$ raku prog.raku
42
MyStruct.new(member => 84)
84
Run Code Online (Sandbox Code Playgroud)
请注意,由于结构本身对其成员没有动态分配(因为它只有一个整数),因此我们没有进行进一步的释放。
现在 Raku 程序需要意识到这一点,并使用它:
/* addendum to mod_struct.c */
void free_struct(struct MyStruct* s) {
free(s);
}
Run Code Online (Sandbox Code Playgroud)
输出如下
42
MyStruct.new(member => 84)
84
successfully freed struct
Run Code Online (Sandbox Code Playgroud)
手动跟踪 MyStruct 对象并记住在一段时间后释放它们可能很麻烦;那就是写C!在 Raku 级别中,我们已经有一个代表结构的类;然后我们可以向它添加一个DESTROY子方法,只要垃圾收集器认为有必要,它就会释放自己:
# prog.raku
use NativeCall;
my constant LIB = "./libstruct.so";
class MyStruct is repr("CStruct") {
has int32 $.member;
}
# C bridge
sub newint(int32) returns int32 is native(LIB) { * }
sub newstruct(int32) returns MyStruct is native(LIB) { * }
sub free_struct(MyStruct) is native(LIB) { * }; # <-- new!
say newint(42);
my $s := newstruct(84);
say $s;
say $s.member;
# ... after some time
free_struct($s);
say "successfully freed struct";
Run Code Online (Sandbox Code Playgroud)
通过此添加,不需要手动调用free_struct(事实上,最好不要,因为它可能导致双重释放,这是 C 级别上未定义的行为)。
PS 你的主 C 文件可能会被修改,例如,头文件似乎是有序的,但这超出了范围,或者这只是一个谁知道的说明性示例。无论哪种情况,感谢您提供 MRE 并欢迎访问该网站。
除了很棒的@Mustafa 的回答。
我找到了另一种方法来解决我的问题:我们可以在 raku 中分配结构并将其传递给 C 函数。这是一个示例,文件mod_struct.c:
#include <stdint.h>
struct MyStruct {
int32_t member;
};
void writestruct(struct MyStruct *outputStruct, int32_t arg) {
outputStruct->member = arg;
}
Run Code Online (Sandbox Code Playgroud)
文件usestruct.raku:
#!/bin/env raku
use NativeCall;
class MyStruct is repr('CStruct') {
has int32 $.member;
}
sub writestruct(MyStruct is rw, int32) is native('./libstruct.so') { * }
my $myStruct = MyStruct.new;
writestruct($myStruct, 42);
say $myStruct.member;
Run Code Online (Sandbox Code Playgroud)
编译并运行它:
$ gcc -Wall -Wextra -pedantic -shared -o libstruct.so -fPIC mod_struct.c
$ ./usestruct.raku
42
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
192 次 |
| 最近记录: |