【问题标题】:Passing an inlined CArray in a CStruct to a shared library using NativeCall使用 NativeCall 将 CStruct 中的内联 CArray 传递给共享库
【发布时间】:2018-06-09 19:09:01
【问题描述】:

这是“How to declare native array of fixed size in Perl 6?”的后续问题。

在那个问题中,讨论了如何将固定大小的数组合并到CStruct 中。在this answer 中,建议使用HASCStruct 中内联CArray。当我测试这个想法时,我遇到了一些奇怪的行为,在问题下方的 cmets 部分中无法解决,所以我决定将它写成一个新问题。这是我的 C 测试库代码:

slib.c

#include <stdio.h>

struct myStruct
{
    int A; 
    int B[3];
    int C;
};

void use_struct (struct myStruct *s) {
    printf("sizeof(struct myStruct): %ld\n", sizeof( struct myStruct ));
    printf("sizeof(struct myStruct *): %ld\n", sizeof( struct myStruct *));
    printf("A = %d\n", s->A);
    printf("B[0] = %d\n", s->B[0]);
    printf("B[1] = %d\n", s->B[1]);
    printf("B[2] = %d\n", s->B[2]);
    printf("C = %d\n", s->C);
}

为了生成一个共享库,我使用了:

gcc -c -fpic slib.c
gcc -shared -o libslib.so slib.o

然后,Perl 6 代码:

p.p6

use v6;
use NativeCall;

class myStruct is repr('CStruct') {
    has int32 $.A is rw;
    HAS int32 @.B[3] is CArray is rw;
    has int32 $.C is rw;
}

sub use_struct(myStruct $s) is native("./libslib.so") { * };

my $s = myStruct.new();
$s.A = 1;
$s.B[0] = 2;
$s.B[1] = 3;
$s.B[2] = 4;
$s.C = 5;
say "Expected size of Perl 6 struct: ", (nativesizeof(int32) * 5);
say "Actual size of Perl 6 struct: ", nativesizeof( $s );
say 'Number of elements of $s.B: ', $s.B.elems;
say "B[0] = ", $s.B[0];
say "B[1] = ", $s.B[1];
say "B[2] = ", $s.B[2];
say "Calling library function..";
say "--------------------------";
use_struct( $s );

脚本的输出是:

Expected size of Perl 6 struct: 20
Actual size of Perl 6 struct: 24
Number of elements of $s.B: 3
B[0] = 2
B[1] = 3
B[2] = 4
Calling library function..
--------------------------
sizeof(struct myStruct): 20
sizeof(struct myStruct *): 8
A = 1
B[0] = 0         # <-- Expected 2
B[1] = 653252032 # <-- Expected 3
B[2] = 22030     # <-- Expected 4
C = 5

问题

  • 为什么nativesizeof( $s ) 给出 24(而不是预期值 20)?

  • 为什么从 C 函数打印时结构中的数组 B 的内容与预期不符?

注意

我使用的是 Ubuntu 18.04 和 Perl 6 Rakudo 版本 2018.04.01,但也使用版本 2018.05 进行了测试

【问题讨论】:

  • 这显然是将数据对齐到 64 位,因为删除 $.A 会将结构减少 8 位。
  • @BradGilbert 是的,很好的观察!如果我从结构中删除$.A,则大小会减少 8 个字节,从 24 个字节减少到 16 个字节,而我预计只会减少 4 个字节,因为$.A 是一个uint32
  • 您也可以在$.B 之前添加一个int32,它不会改变大小。我认为添加 HAS … is CArray 时忽略了这一点。
  • @BradGilbert 是的,在 @.B 的声明之前添加 has int32 $.D is rw 不会改变大小。至少似乎在结构的末尾添加了四个额外的对齐字节,因此结构中的字段不会移位。是的,额外的对齐字节似乎与CArray 相关,因为将CArray 替换为3 个int32 给出的nativesizeof( $s ) 为20,而不是CArray 时的24。
  • @jjmerelo 很有趣。修复了该错误的 Tobias(与往常一样进行了测试,因此它不应该倒退)也是编写新代码的人,该代码于 2018.05 登陆以分配内联 CArray。他来来去去,有时一次离开好几个月。我用#perl6-dev 上的链接告诉了他,并轻推了timotimo / others。 colabti.org/irclogger/irclogger_log/…

标签: raku nativecall


【解决方案1】:

您的代码是正确的。我刚刚在 MoarVM 中修复了该错误,并向 rakudo 添加了测试,类似于您的代码:

在 C 中:

typedef struct {
    int a;
    int b[3];
    int c;
} InlinedArrayInStruct;

在 Perl 6 中:

class InlinedArrayInStruct is repr('CStruct') {
    has int32 $.a is rw;
    HAS int32 @.b[3] is CArray;
    has int32 $.c is rw;
}

查看这些补丁: https://github.com/MoarVM/MoarVM/commit/ac3d3c76954fa3c1b1db14ea999bf3248c2eda1c https://github.com/rakudo/rakudo/commit/f8b79306cc1900b7991490eef822480f304a56d9

如果您不是直接从 github 的最新源代码构建 rakudo(以及 NQP 和 MoarVM),您可能需要等待将出现在这里的 2018.08 版本:https://rakudo.org/files

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-20
    相关资源
    最近更新 更多