2017-05-06 10 views
6

Le Perl 6 docs répertorie plusieurs types. Certains d'entre eux, tels que Str, ont des comportements boîte/unbox plus complexes.Ajout de types de mode utilisateur pour les structures Perl 6 NativeCall

Est-il possible de définir mon propre type, en spécifiant mes propres routines pour la boîte/unboxing? Pour un projet particulier, j'ai un tas de types que je réutilise, et fondamentalement couper/coller mes fonctions d'accesseur encore et encore.

Par exemple, la structure C utilise un time_t, et je branche des méthodes d'accès pour aller à/partir d'un DateTime. Un autre exemple est une liste séparée par des virgules, je voudrais aller à/partir d'un Array et prendre soin de la split/join automagically.

Y a-t-il une meilleure façon de procéder?

Modifier: Ajouter Exemple:

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 

Cela fonctionne, je peux définir les différents domaines de la CStruct de manière Perl-ish (pas lvalue, mais je peux les passer en tant que paramètres) .

Maintenant, je veux utiliser time_t, FooType_t, etc. pour beaucoup de champs dans beaucoup de structures et les faire agir de la même manière. Y a-t-il un meilleur moyen que de simplement copier ces méthodes encore et encore?

Peut-être que des macros pourraient vous aider? Je ne les ai pas encore maîtrisés.

+0

Gestion de la mémoire devient difficile comme je l'aborde aussi. Probablement le meilleur pour traiter le CStruct très cru et couche une classe plus intelligente sur le dessus. –

+0

BTW: J'aime NativeCall. C'est incroyable comme il est facile d'interagir avec les bibliothèques C. –

Répondre

8

Vous pouvez écrire un trait qui gère la conversion automatique d'attributs lors de l'extraction ou du stockage de l'attribut. Ce qui suit devrait vous aider à démarrer:

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}"; 
     } 
    } 
} 

Par exemple, prenez votre cas d'utilisation de 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); 
} 

Enfin, quelques exemples de code pour montrer cela fonctionne:

my $span = CTimeSpan.new; 
say $span; 
say $span.end; 

$span.end = DateTime.now; 
say $span; 
say $span.end;