Как определить строки фиксированной длины в структуре 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.