【问题标题】:How to define fixed-length strings in a Perl6 NativeCall struct?如何在 Perl6 NativeCall 结构中定义固定长度的字符串?
【发布时间】:2017-09-29 03:08:16
【问题描述】:

我有一个第三方 C 库,它定义了一个类似的结构:

struct myStruct {
  int a;
  int b;
  char str1[32];
  char str2[32];
};

还有一个函数,它接受一个指向这个结构的指针并填充它。我需要我的 Perl6 本机调用来提供该结构,然后读取结果。

到目前为止,我在 Perl6 中将结构定义为:

class myStruct is repr('CStruct') {
  has int32 $.a;
  has int32 $.b;
  has Str $.str1; # Option A: This won't work as Perl won't know what length to allocate
  has CArray[uint8] $.str2; # Option B: This makes more sense, but again how to define length?  
                     # Also, would this allocate the array in place, or 
                     #    reference an array that is separately allocated (and therefore not valid)?
}

还有一个像这样的原生调用:

sub fillStruct(myStruct) is native('test_lib') { ... }
我的 $struct = myStruct.new();
填充结构($结构); # 到目前为止我尝试过的任何变体都会出现 seg 错误

我怎样才能做到这一点?

【问题讨论】:

  • 您尝试过文档中的代码吗? docs.perl6.org/language/nativecall#Structs 我们可以推断结构将动态调整大小,同时考虑到CArray 的当前大小。文档演示了更改 TWEAK() 中的元素。所以尝试传递一个完整的数组而不是一个空的数组。 (如果你想让它为空,请填写 0。)
  • 但是 perl 在技术上不需要计算大小。它只需要可用的内存。所以填充数组应该可以解决问题。 (如果我记得我的 C++ 时代,结构并不总是相同的大小,因为您可以将可变长度缓冲区作为最后一个元素。)
  • C/C++ 结构总是以固定大小定义,尽管它们可以映射/转换到更大的内存区域,并且可以通过指针操作访问。
  • 我也尝试了推荐的 TWEAK 函数方法,将每个 uint8 数组显式初始化为正确的大小,但是当我运行它时仍然出现 seg 错误。我怀疑 Perl 是将指向字符串数组的指针插入到结构中,而不是根据需要直接在结构中定义数组。
  • 遗憾的是,rakudo 仍然缺少您需要的功能;您可以从 nativehelpers 模块中得到一些缓解:modules.perl6.org/search/?q=nativehelpers - 希望对您有所帮助

标签: raku nativecall


【解决方案1】:

正如其他人所说,目前似乎没有任何方法可以实现这一目标。

我已经求助于定义一个新的 C 函数作为解决方法。该函数有效地充当访问器方法,仅返回我需要的字段作为离散的 NativeCall 友好指针。

希望社区能够在某个时候为这个案例提供适当的支持。

【讨论】:

    【解决方案2】:

    在撰写本文时,这似乎没有得到处理。
    作为一种解决方法,我的做法是让宏生成 32 个int8,充分放置字段名称。

    【讨论】:

    • 谢谢。您能否举一个定义变量的宏的示例,如果可能的话,是一个具有编程选择名称的变量?宏目前完全没有文档记录。
    • 其实不需要使用宏,使用元对象协议创建类即可。它会让你创建一个类型,添加属性,然后组成类。您可能可以查看我的 ADT 模块以获取灵感:github.com/timo/adt
    【解决方案3】:

    截至 2020 年 7 月,您应该能够做到以下几点:

    sub setCharArray($a, $s, $l, $c is rw) {
      die 'Too many chars!' unless $s.chars <= $l;;
    
      $c = $s if $c;
      my $chars = $a.encode;
      $a[$_] = $chars[$_] for ^$chars.elems;
      $a[$chars.elems] = 0 unless $s.elems == 128;
    }
    
    class A repr<CStruct> is export {
      HAS uint8 @!myString[128] is CArray;
    
      state Str $cached;
    
      method myString is rw {
        Proxy.new:
          FETCH => sub($) {
            return if $cached;
            $cached = Buf.new(@!myString).decode
         },
    
         STORE => $, Str $s is raw {
           setCharArray(@!myString, $s, 128, $cached);
         }
      }
      
    }
    

    让我解释一下:

    用于定义静态尺寸元素的“HAS”声明器已经在 NativeCall 中使用了一段时间,所以这不是实验部分。棘手的部分是“方法 myString”。它允许 A 类的使用者设置和获取 @!myString 属性,就好像它是一个正确的属性,而不是一个数组。

    如果@!myString 中的数据首先从C 中读取,那么$cache 状态变量将为空。然后通过解码的 Buf 创建 Str 对象并返回。希望对用户隐藏对象实现中看到的复杂性,以便:

    my $a = c_function_that_returns_A();
    $a.myString;
    

    ...将按预期工作,并且类似:

    $a.myString = 'Crab';
    

    ...也可以正常工作。

    不幸的是,像 setCharArray() 这样的辅助函数需要迭代来设置 @!myString,但希望将来会有所改变。

    IMPORTANT CAVEAT -- 此实现假定一旦分配对象,对 @!myString 的更改仅限于 Raku 端,否则一旦设置,$cached 值将屏蔽它们。目前,我真的没有找到解决方法,除非您想在每次需要访问 @!myString 时花费这些周期来创建一个新的 Str a-fresh。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-12-07
      • 1970-01-01
      • 2015-06-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-14
      • 2017-05-12
      相关资源
      最近更新 更多