Adding user mode types for Perl 6 NativeCall structs
boxing, nativecall, raku
Solution
You could write a trait that handles automatic attribute conversion on fetching or storing the attribute. The following should get you started:
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}";
}
}
}
For example, take your use case of `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);
}
Finally, some example code to show it works:
my $span = CTimeSpan.new;
say $span;
say $span.end;
$span.end = DateTime.now;
say $span;
say $span.end;
Problem
The Perl 6 docs list a bunch of types. Some of them, such as `Str`, have more complicated box/unbox behaviors. Is it possible to define my own type, specifying my own routines for the box/unboxing? For a particular project, I have a bunch of types I'm reusing, and basically cut/pasting my accessor functions over and over. For example, the C Struct uses a `time_t`, and I plug in accessor methods to go to/from a `DateTime`. Another example is a comma-separated list, I'd like to go to/from an `Array` and take care of the `split`/`join` automagically. Is there a better way to do this? Edit: Add Example: ``` 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 ``` This works, I can set the various fields of the `CStruct` in Perl-ish ways (no lvalue, but I can pass them in as parameters). Now I want to use `time_t`, `FooType_t`, etc. for many fields in a lot of structs and have them act the same way. Is there a better way other than to just copy those methods over and over? Maybe macros could help here? I haven't mastered them yet.