如何为返回整个结构的 C 函数编写 Raku 声明?
How to write a Raku declaration for a C function returning a whole struct?
我有这个 C 代码:
typedef struct {
double dat[2];
} gsl_complex;
gsl_complex gsl_poly_complex_eval(const double c[], const int len, const gsl_complex z);
C 函数 returns 一个完整的结构,而不仅仅是一个指针,所以我不能将 Raku 声明写成:
sub gsl_poly_complex_eval(CArray[num64] $c, int32 $len, gsl_complex $z --> gsl_complex)
is native(LIB) is export { * }
有什么建议吗?
为此,您需要一个包含更详细示例的 CStruct. The P5localtime 模块。
问题
一些 C API 使用三阶段方法处理结构,通过引用传递结构,如下所示:
struct mystruct *init_mystruct(arguments, ...);
double compute(struct mystruct *);
void clean_mystruct(struct mystruct *);
这种实现隐藏了数据结构,但这是有代价的:最终用户必须跟踪他们的指针并记得自己清理,否则程序将泄漏内存。
另一种方法是我正在连接的库使用的方法:return 堆栈上的数据,因此可以将其分配给 auto
变量并在超出范围时自动丢弃。
在这种情况下,API 被建模为两阶段操作:
struct mystruct init_mystruct(arguments, ...);
double compute(struct mystruct);
结构按值在堆栈上传递,之后无需清理。
但是 Raku 的 NativeCall 接口只能使用通过引用传递它们的 C 结构,因此出现了问题。
解决方案
这不是一个干净的解决方案,因为它回到了所描述的第一种方法,即三相方法,但这是迄今为止我能够设计出的唯一方法。
在这里,我考虑了库 API 中的两个 C 函数:第一个创建一个复数作为结构,第二个将两个数字相加。
首先我写了一个小的C代码接口,文件complex.c:
#include <gsl/gsl_complex.h>
#include <gsl/gsl_complex_math.h>
#include <stdlib.h>
gsl_complex *alloc_gsl_complex(void)
{
gsl_complex *c = malloc(sizeof(gsl_complex));
return c;
}
void free_gsl_complex(gsl_complex *c)
{
free(c);
}
void mgsl_complex_rect(double x, double y, gsl_complex *res)
{
gsl_complex ret = gsl_complex_rect(x, y);
*res = ret;
}
void mgsl_complex_add(gsl_complex *a, gsl_complex *b, gsl_complex *res)
{
*res = gsl_complex_add(*a, *b);
}
我是这样编译的:
gcc -c -fPIC complex.c
gcc -shared -o libcomplex.so complex.o -lgsl
请注意最后的 -lgsl
用于 link 我正在连接的 libgsl C 库。
然后我写了Raku底层接口:
#!/usr/bin/env raku
use NativeCall;
constant LIB = ('/mydir/libcomplex.so');
class gsl_complex is repr('CStruct') {
HAS num64 @.dat[2] is CArray;
}
sub mgsl_complex_rect(num64 $x, num64 $y, gsl_complex $c) is native(LIB) { * }
sub mgsl_complex_add(gsl_complex $a, gsl_complex $b, gsl_complex $res) is native(LIB) { * }
sub alloc_gsl_complex(--> gsl_complex) is native(LIB) { * }
sub free_gsl_complex(gsl_complex $c) is native(LIB) { * }
my gsl_complex $c1 = alloc_gsl_complex;
mgsl_complex_rect(1e0, 2e0, $c1);
say "{$c1.dat[0], $c1.dat[1]}"; # output: 1 2
my gsl_complex $c2 = alloc_gsl_complex;
mgsl_complex_rect(1e0, 2e0, $c2);
say "{$c2.dat[0], $c2.dat[1]}"; # output: 1 2
my gsl_complex $res = alloc_gsl_complex;
mgsl_complex_add($c1, $c2, $res);
say "{$res.dat[0], $res.dat[1]}"; # output: 2 4
free_gsl_complex($c1);
free_gsl_complex($c2);
free_gsl_complex($res);
请注意,我必须显式释放我创建的三个数据结构,破坏了原始 C API 精心设计。
我有这个 C 代码:
typedef struct {
double dat[2];
} gsl_complex;
gsl_complex gsl_poly_complex_eval(const double c[], const int len, const gsl_complex z);
C 函数 returns 一个完整的结构,而不仅仅是一个指针,所以我不能将 Raku 声明写成:
sub gsl_poly_complex_eval(CArray[num64] $c, int32 $len, gsl_complex $z --> gsl_complex)
is native(LIB) is export { * }
有什么建议吗?
为此,您需要一个包含更详细示例的 CStruct. The P5localtime 模块。
问题
一些 C API 使用三阶段方法处理结构,通过引用传递结构,如下所示:
struct mystruct *init_mystruct(arguments, ...);
double compute(struct mystruct *);
void clean_mystruct(struct mystruct *);
这种实现隐藏了数据结构,但这是有代价的:最终用户必须跟踪他们的指针并记得自己清理,否则程序将泄漏内存。
另一种方法是我正在连接的库使用的方法:return 堆栈上的数据,因此可以将其分配给 auto
变量并在超出范围时自动丢弃。
在这种情况下,API 被建模为两阶段操作:
struct mystruct init_mystruct(arguments, ...);
double compute(struct mystruct);
结构按值在堆栈上传递,之后无需清理。
但是 Raku 的 NativeCall 接口只能使用通过引用传递它们的 C 结构,因此出现了问题。
解决方案
这不是一个干净的解决方案,因为它回到了所描述的第一种方法,即三相方法,但这是迄今为止我能够设计出的唯一方法。
在这里,我考虑了库 API 中的两个 C 函数:第一个创建一个复数作为结构,第二个将两个数字相加。
首先我写了一个小的C代码接口,文件complex.c:
#include <gsl/gsl_complex.h>
#include <gsl/gsl_complex_math.h>
#include <stdlib.h>
gsl_complex *alloc_gsl_complex(void)
{
gsl_complex *c = malloc(sizeof(gsl_complex));
return c;
}
void free_gsl_complex(gsl_complex *c)
{
free(c);
}
void mgsl_complex_rect(double x, double y, gsl_complex *res)
{
gsl_complex ret = gsl_complex_rect(x, y);
*res = ret;
}
void mgsl_complex_add(gsl_complex *a, gsl_complex *b, gsl_complex *res)
{
*res = gsl_complex_add(*a, *b);
}
我是这样编译的:
gcc -c -fPIC complex.c
gcc -shared -o libcomplex.so complex.o -lgsl
请注意最后的 -lgsl
用于 link 我正在连接的 libgsl C 库。
然后我写了Raku底层接口:
#!/usr/bin/env raku
use NativeCall;
constant LIB = ('/mydir/libcomplex.so');
class gsl_complex is repr('CStruct') {
HAS num64 @.dat[2] is CArray;
}
sub mgsl_complex_rect(num64 $x, num64 $y, gsl_complex $c) is native(LIB) { * }
sub mgsl_complex_add(gsl_complex $a, gsl_complex $b, gsl_complex $res) is native(LIB) { * }
sub alloc_gsl_complex(--> gsl_complex) is native(LIB) { * }
sub free_gsl_complex(gsl_complex $c) is native(LIB) { * }
my gsl_complex $c1 = alloc_gsl_complex;
mgsl_complex_rect(1e0, 2e0, $c1);
say "{$c1.dat[0], $c1.dat[1]}"; # output: 1 2
my gsl_complex $c2 = alloc_gsl_complex;
mgsl_complex_rect(1e0, 2e0, $c2);
say "{$c2.dat[0], $c2.dat[1]}"; # output: 1 2
my gsl_complex $res = alloc_gsl_complex;
mgsl_complex_add($c1, $c2, $res);
say "{$res.dat[0], $res.dat[1]}"; # output: 2 4
free_gsl_complex($c1);
free_gsl_complex($c2);
free_gsl_complex($res);
请注意,我必须显式释放我创建的三个数据结构,破坏了原始 C API 精心设计。