使用 C 接口为具有动态数据和自定义 postcircumfix 的结构获取随机和错误的值

fre*_*ddy 7 nativecall raku

我有一个 C 文件和一个 Raku 文件用于连接。有时我会得到随机且错误的值。文件如下

\n

C 文件:它有一个名为 CField 的结构,Raku 代码将与该结构交互。重要的是它有一个动态分配的属性,即data. 所以我在 中进行了 2 个 malloc make_field,并在 中进行了 2 个 free free_field

\n
// field.c\n#include <stdio.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n\nstruct CField {\n    void* data;\n    size_t size;\n};\n\nvoid* checker_malloc(size_t n) {\n    // malloc but checks if allocated fine\n    void* p = malloc(n);\n    if (!p) { fprintf(stderr, "can\'t allocate %zu bytes", n); exit(1); }\n    return p;\n}\n\nstruct CField* make_field(void* data, size_t data_size) {\n    // malloc for all struct\n    struct CField* new_field = checker_malloc(sizeof *new_field);\n\n    // malloc for .data\n    size_t num_bytes = data_size * sizeof(int32_t);\n    new_field->data = checker_malloc(num_bytes);\n    memcpy(new_field->data, data, num_bytes);\n\n    new_field->size = data_size;\n    \n    return new_field;\n}\n\nvoid free_field(struct CField* v) {\n    free(v->data);\n    free(v);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

Raku 文件:它有一个围绕该结构的包装器。我[]在这里为“无论传递什么”定义运算符,Wrapper.new(...)[*]即做一些特殊的事情。它本质上是根据当前数据创建一个新的包装器。

\n
## Field.rakumod\nuse NativeCall;\n\n# C side\nmy constant LIB = "./libfield.so";\nclass CField is repr("CStruct") {...}\n\nsub make_field(Pointer[void], size_t --> CField) is native(LIB) { * };\nsub free_field(CField) is native(LIB) { * };\n\nclass CField {\n    has Pointer $.data;\n    has size_t  $.size;\n\n    submethod DESTROY {\n        free_field(self);\n    }\n}\n\n# Raku side\nclass Wrapper is export {\n    has CField $.field;\n    has Int $.size;\n\n    # New from Raku data\n    multi method new(@data) {\n        self.bless:\n            field => make_field(nativecast(Pointer, CArray[int32].new(@data)), @data.elems),\n            size => @data.elems;\n    }\n\n    # New from already Wrapper\n    multi method new(Wrapper $other) {\n        self.bless:\n            field => make_field($other.field.data, $other.size),\n            size => $other.size;\n    }\n\n    method as-list {\n        # to see what is inside\n        my $nat = nativecast(CArray[int32], $!field.data);\n        $nat[^$!size].List\n    }\n\n    multi postcircumfix:<[ ]>(Wrapper:D $w, Whatever) is export {\n        # new Wrapper object around $w, essentially copies it\n        Wrapper.new($w)\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

现在重现失败:

\n
$ tree\n.\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Field.rakumod\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 field.c\n\n$ gcc -std=c99 -Wall -fPIC -shared -olibfield.so field.c\n\n$ raku -I. -MField -e\'for ^1_000 { my $a = Wrapper.new([1, 2, 3]); my $b = $a[*].as-list; die "$_: {$b.raku}" unless $b eqv (1, 2, 3) }; say "no error"\'\nno error\n\n$ raku -I. -MField -e\'for ^1_000 { my $a = Wrapper.new([1, 2, 3]); my $b = $a[*].as-list; die "$_: {$b.raku}" unless $b eqv (1, 2, 3) }; say "no error"\'\n446: $(38717712, 0, 3)\n  in block <unit> at -e line 1\n
Run Code Online (Sandbox Code Playgroud)\n

有时它会像预期的那样以“无错误”结束。但有时它会像我们看到的那样死去。有趣的是,如果我DESTROY在 CField 中注释方法,它总是“没有错误”。但内存泄漏对吗?那么我做错了什么以及如何解决它?

\n
$ raku -v  # Ubuntu 20.04\nWelcome to Rakudo\xe2\x84\xa2 v2023.09.\nImplementing the Raku\xc2\xae Programming Language v6.d.\nBuilt on MoarVM version 2023.09.\n
Run Code Online (Sandbox Code Playgroud)\n