Instalación Automática de Métodos con Class::Struct

El módulo Class::Struct forma parte de la distribución núcleo de Perl. El módulo exporta una única función denominada struct. La función struct recibe como argumentos una lista que describe la estructura de datos asociada con un objeto. El efecto es la generación de un método constructor (que tiene por nombre new) y métodos de acceso.

Creación de Métodos

La siguiente sesión con el depurador muestra un ejemplo de uso:

lhp@nereida:~/LCLASS_TRAIT/myexample$ perl -MClass::Struct -wde 0
main::(-e:1):   0
  DB<1> struct Foo => { a => '@', s => '$', h => '%', c => 'Tutu' }
  DB<2> struct Tutu => [ nombre => '$', apellidos => '@' ]
Estas dos líneas crean los métodos.

Objetos Hash y Objetos Lista

La diferencia entre usar la sintáxis de llaves y la de corchetes estriba en que el objeto construido es un hash en el caso de las llaves y un array en el caso de los corchetes:

  DB<32> x Tutu->new(nombre=>'Alberto', apellidos=>[qw(Gonzalez Martinez)])
0  Tutu=ARRAY(0x8704558)
   0  'Alberto'
   1  ARRAY(0x85a3fe0)
      0  'Gonzalez'
      1  'Martinez'

Estructura del Constructor

Activando $Data::Dumper::Deparse podemos ver la forma del constructor proporcionado:

DB<23> use Data::Dumper
DB<24> $Data::Dumper::Deparse = 1
DB<25> p Dumper(\&Foo::new)
$VAR1 = sub {
    package Foo;
    use strict 'refs';
    my($class, %init) = @_;

    $class = 'Foo' unless @_;

    my($r) = {};

    if (defined $init{'c'}) {
        if (ref $init{'c'} eq 'HASH') {
            $$r{'Foo::c'} = 'Tutu'->new(%{$init{'c'};});
        }
        elsif (UNIVERSAL::isa($init{'c'}, 'Tutu')) {
            $$r{'Foo::c'} = $init{'c'};
        }
        else {
            croak('Initializer for c must be hash or Tutu reference');
        }
    }
      croak('Initializer for h must be hash reference') 
    if defined $init{'h'} and ref $init{'h'} ne 'HASH';

    $$r{'Foo::h'} = defined $init{'h'} ? $init{'h'} : {};

      croak('Initializer for a must be array reference') 
    if defined $init{'a'} and ref $init{'a'} ne 'ARRAY';

    $$r{'Foo::a'} = defined $init{'a'} ? $init{'a'} : [];

    $$r{'Foo::s'} = defined $init{'s'} ? $init{'s'} : undef;

    bless $r, $class;
};

Construccion

Ahora podemos construir objetos de esas clases::

  DB<4> x $a = Tutu->new(nombre=>'Juan', apellidos=>[qw(Gonzalez Hernandez)])
0  Tutu=ARRAY(0x84eb024)
   0  'Juan'
   1  ARRAY(0x8474e5c)
      0  'Gonzalez'
      1  'Hernandez'
  DB<5> x $b = Foo->new(a=>[1..3], s=>'hola', h=>{x=>1, y=>2}, c=>$a)
0  Foo=HASH(0x850524c)
   'Foo::a' => ARRAY(0x84eb4e0)
      0  1
      1  2
      2  3
   'Foo::c' => Tutu=ARRAY(0x84eb024)
      0  'Juan'
      1  ARRAY(0x8474e5c)
         0  'Gonzalez'
         1  'Hernandez'
   'Foo::h' => HASH(0x851bed0)
      'x' => 1
      'y' => 2
   'Foo::s' => 'hola'

Acceso a Atributos de Tipo Lista

Los siguientes comandos muestran como acceder a los elementos de un atributo de tipo lista:

  DB<6> x $b->a
0  ARRAY(0x84eb4e0)
   0  1
   1  2
   2  3
  DB<7> x $b->a(1)
0  2
  DB<8> x $b->a(1,7)
0  7
  DB<9> x $b->a(1)
0  7
  DB<30> $b->a([9..12])
  DB<31> x $b->a
0  ARRAY(0x85399f0)
   0  9
   1  10
   2  11
   3  12

El Formato de un Método de Acceso

Mediante Dumper podemos ver el codigo del método de acceso. He introducido comentarios que explican las diferentes partes del método::

  DB<26> p Dumper(\&Foo::a)
$VAR1 = sub {
    package Foo;
    use strict 'refs';
    my $r = shift @_;

    my $i;
    # Si no quedan args retornamos la ref al array
    @_ ? ($i = shift @_) : return($$r{'Foo::a'});

    # Si $i es ARR y no args modificamos array
    if (ref $i eq 'ARRAY' and not @_) {
        $$r{'Foo::a'} = $i;
        return $r;
    }

    croak('Too many args to a') if @_ > 1;

    # $i definido y no es ARR y no args. Modificamos elemento
    @_ ? ($$r{'Foo::a'}[$i] = shift @_) : $$r{'Foo::a'}[$i];
};

Acceso a Atributos de Tipo Escalar

Mas sencillo aún es acceder a un atributo escalar:

  DB<10> x $b->s
0  'hola'
  DB<11> x $b->s('mundo')
0  'mundo'
  DB<12> x $b->s
0  'mundo'

Acceso a Atributos de Tipo Hash

Los siguientes comandos muestran accesos al atributo de tipo hash:

  DB<13> x $b->h
0  HASH(0x851bed0)
   'x' => 1
   'y' => 2
  DB<14> x $b->h('x')
0  1
  DB<15> x $b->h(x => 3)
0  3
  DB<16> x $b->h('x')
0  3

Acceso a Atributos de Tipo Objeto

Los atributos de un objeto pueden ser de tipo objeto:

  DB<17> x $b->c
0  Tutu=ARRAY(0x84eb024)
   0  'Juan'
   1  ARRAY(0x8474e5c)
      0  'Gonzalez'
      1  'Hernandez'
  DB<18> x $b->c->nombre
0  'Juan'
  DB<19> x $b->c->nombre('Pedro')
0  'Pedro'
  DB<20>



Subsecciones
Casiano Rodríguez León
2009-10-04