Как определить строки фиксированной длины в структуре Perl6 NativeCall?

У меня есть сторонняя библиотека 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) является нативным ('test_lib') { ... }
my $struct = myStruct.new();
fillStruct($ структура); # Дает ошибку сегмента с любым вариантом, который я пробовал до сих пор

Как я могу сделать эту работу?

2 ответа

Как уже говорили другие, в настоящее время не существует способа достичь этого.

Я прибег к определению новых функций C в качестве обходного пути. Функция эффективно действует как метод доступа, возвращая только те поля, которые мне нужны, в качестве дискретных указателей, удобных для NativeCall.

Надеемся, что в какой-то момент сообщество получит необходимую поддержку для этого случая.

На момент написания, это не похоже на обработку.
В качестве обходного пути я мог бы позволить макросу генерировать 32 int8, адекватно размещая имена полей.

По состоянию на июль 2020 года у вас должно получиться что-то вроде этого:

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 будет пустой. Затем объект Str создается с помощью декодированного Buf и возвращается. Это надежда, что сложность, наблюдаемая в реализации объекта, затем будет скрыта от пользователя, чтобы:

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

... будет работать, как и следовало ожидать, и то же самое:

$a.myString = 'Crab';

... тоже будет работать без проблем.

К сожалению, вспомогательной функции вроде setCharArray() необходимо выполнить итерацию для установки @! MyString, но, надеюсь, в будущем это изменится.

ВАЖНОЕ ПРЕДУПРЕЖДЕНИЕ. Эта реализация предполагает, что изменения в @! MyString ограничиваются стороной Raku после выделения объекта, в противном случае после установки значение $cached будет маскировать их. На данный момент я действительно не вижу способа обойти это, если только вы не хотите тратить циклы на создание нового Str a-fresh каждый раз, когда вам нужно получить доступ к @! MyString.

Другие вопросы по тегам