Добавление типов пользовательских режимов для структур Perl 6 NativeCall
Документы Perl 6 содержат список типов. Некоторые из них, такие как Str
, иметь более сложное поведение box/unbox.
Можно ли определить мой собственный тип, указав мои собственные процедуры для коробки / распаковки? Для конкретного проекта у меня есть куча типов, которые я повторно использую, и я в основном сокращаю / вставляю свои функции доступа снова и снова.
Например, C Struct использует time_t
, и я подключаю методы доступа, чтобы перейти к / от DateTime
, Другим примером является список через запятую, я хотел бы перейти к / от Array
и заботиться о split
/join
автомагически.
Есть лучший способ сделать это?
Редактировать: Добавить пример:
constant time_t = uint64;
constant FooType_t = uint16;
enum FooType <A B C>;
class Foo is repr('CStruct') is rw
{
has uint32 $.id;
has Str $.name;
has FooType_t $.type;
has time_t $.time;
method name(Str $n?) {
$!name := $n with $n;
$!name;
}
method type(FooType $t?) {
$!type = $t with $t;
FooType($!type);
}
method time(DateTime $d?) {
$!time = .Instant.to-posix[0].Int with $d;
DateTime.new($!time)
}
}
my $f = Foo.new;
$f.id = 12;
$f.name('myname');
$f.type(B);
$f.time(DateTime.new('2000-01-01T12:34:56Z'));
say "$f.id() $f.name() $f.type() $f.time()";
# 12 myname B 2000-01-01T12:34:56Z
Это работает, я могу установить различные поля CStruct
в Perl-ish (без lvalue, но я могу передать их как параметры).
Теперь я хочу использовать time_t
, FooType_t
и т. д. для многих полей во многих структурах, и пусть они действуют одинаково. Есть ли лучший способ, чем просто копировать эти методы снова и снова?
Может быть, макросы могут помочь здесь? Я еще не освоил их.
1 ответ
Вы можете написать черту, которая обрабатывает автоматическое преобразование атрибутов при получении или сохранении атрибута. Следующее должно помочь вам начать:
multi sub trait_mod:<is>(Attribute:D $attr, :$autoconv!) {
use nqp;
my $name := $attr.name;
$attr.package.^add_method: $name.substr(2), do given $attr.type {
when .REPR eq 'P6int' {
method () is rw {
my $self := self;
Proxy.new:
FETCH => method () {
$autoconv.out(nqp::getattr_i($self, $self.WHAT, $name));
},
STORE => method ($_) {
nqp::bindattr_i($self, $self.WHAT, $name,
nqp::decont($autoconv.in($_)));
}
}
}
default {
die "FIXME: no idea how to handle {.^name}";
}
}
}
Например, взять ваш случай использования time_t
:
constant time_t = uint64;
class CTimeConversion {
multi method in(Int $_ --> time_t) { $_ }
multi method in(DateTime $_ --> time_t) { .posix }
method out(time_t $_ --> DateTime) { DateTime.new($_) }
}
class CTimeSpan is repr<CStruct> {
has time_t $.start is autoconv(CTimeConversion);
has time_t $.end is autoconv(CTimeConversion);
}
Наконец, пример кода, чтобы показать, что он работает:
my $span = CTimeSpan.new;
say $span;
say $span.end;
$span.end = DateTime.now;
say $span;
say $span.end;