【问题标题】:Passing an array of structures to a Perl 6 NativeCall function将结构数组传递给 Perl 6 NativeCall 函数
【发布时间】:2017-04-21 14:12:54
【问题描述】:

我正在尝试使用 NativeCall 与一些 C 函数进行交互。

我有一个简单的 C 结构和一个需要它们的数组的函数。

struct foo {
    int x;
    char *s;
};

struct foo foo_array[3];

foo_array[0].x = 12;
foo_array[0].s = "foo";
foo_array[1].x = 27;
foo_array[1].s = "bar";
foo_array[2].x = -1;

void somefunc(foo_array);

我尝试了很多方法,但似乎无法完全正确。

class foo is repr('CStruct') {
    has int32 $.x;
    has Str $.s
};

sub somefunc(CArray[foo]) is native { * }

my @foo-array := CArray[foo].new;

@foo-array[0] = ???
@foo-array[1] = ???
@foo-array[2] = ???

somefunc(@foo-array);

如何正确创建类 foo 的对象并设置它们的值, 以及如何使它们的数组适合传递?

【问题讨论】:

    标签: raku nativecall


    【解决方案1】:

    据我所知,没有内置的方法可以做到这一点。但是,有足够的绳索上吊自己 建立一个解决方法

    role StructArray[Mu:U \T where .REPR eq 'CStruct'] does Positional[T] {
        has $.bytes;
        has $.elems;
    
        method new(UInt \n) {
            self.bless(bytes => buf8.allocate(n * nativesizeof T), elems => n);
        }
    
        method AT-POS(UInt \i where ^$!elems) {
            nativecast(T, Pointer.new(nativecast(Pointer, $!bytes) + i * nativesizeof T));
        }
    
        method pointer {
            nativecast(Pointer[T], $!bytes);
        }
    }
    

    这应该允许以下工作:

    my @foo-array := StructArray[foo].new(10); # 'array' with 10 elements
    @foo-array[0].x = 42;
    

    通过将@foo-array.pointer 传递给Pointer[foo] 类型的参数,可以与C 函数进行交互。由于结构也是通过指针传递的,因此您也可以将@foo-array[0] 传递给foo 类型的参数以获得相同的效果。

    【讨论】:

    • 这让我可以使用@foo-array[0].x = 42 分配给我的 int32,但我得到了 Str 的“无法修改不可变的 Str”。
    • @CurtTilmes:至少有两种方法可以解决这个问题:(1)编写一个执行赋值的 C 函数(2)使用指针大小的整数作为您分配的指针替代+nativecast(Pointer, "???".encode);在任何一种情况下,都必须小心,以便 gc 在 C 端使用对象时不会收集它们
    • 我将它从 'Str' 更改为 'size_t' 并将其设置为 nativecast(Pointer, CArray[uint8].new($mystring.encode)).Int 这似乎有效。我会在没有 CArray 的情况下尝试它。
    • +nativecast(Pointer, $mystring.encode) 不会终止字符串。 +nativecast(Pointer, CArray[uint8].new($mystring.encode)) 有效。
    • 您可以手动终止字符串,例如"$mystring\0".encode 应该这样做;如果您使用您的版本(或我的encode 返回的缓冲区),您还应该将CArray 存储在某个变量中,以便在您需要时保持它的生命
    【解决方案2】:

    以下代码显示了如何将指向 byte 的指针和指向 wchar 的指针传递给 windows api 函数。

    我还没有需要传入结构数组的案例,但我不明白为什么相同的技术不适用。最重要的是:您必须确保为数据分配内存!

    use NativeCall;
    
    constant UINT    := uint32;
    constant BOOL    := uint32;
    constant BYTE    := uint8;
    constant WCHAR   := uint16;
    constant int     := int32;
    
    constant LPWTSTR      := CArray[WCHAR];
    constant PBYTE        := CArray[BYTE];
    
    my $virtual-keycode = 0xBC; # comma
    
    sub SetConsoleCP(UINT) is native('Kernel32') returns BOOL { * };
    sub SetConsoleOutputCP(UINT) is native('Kernel32') returns BOOL { * };
    
    # winapi: int ToUnicode( UINT wVirtKey, UINT wScanCode, const PBYTE lpKeyState, LPWSTR pwszBuff, int cchBuff, UINT wFlags );
    sub ToUnicode(UINT, UINT, PBYTE is rw, LPWTSTR is rw, int32, UINT) is native("User32") returns int32 { * };
    
    my @kbs := CArray[BYTE].new(0 xx 256);
    my @buf := CArray[WCHAR].new(0 xx 2);
    
    say "Can't set Codepage" unless SetConsoleCP(65001) && SetConsoleOutputCP(65001);
    
    say "Got Unicode" ~ @buf[0] ~ " -> " ~ @buf[0].chr
        if ToUnicode( $virtual-keycode, 0, @kbs, @buf, 2 ,0);   
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-02-24
      • 1970-01-01
      • 2011-04-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多