内容

名称

Class::Struct - 将类 struct 类似的数据类型声明为 Perl 类

语法

use Class::Struct;
        # declare struct, based on array:
struct( CLASS_NAME => [ ELEMENT_NAME => ELEMENT_TYPE, ... ]);
        # declare struct, based on hash:
struct( CLASS_NAME => { ELEMENT_NAME => ELEMENT_TYPE, ... });

package CLASS_NAME;
use Class::Struct;
        # declare struct, based on array, implicit class name:
struct( ELEMENT_NAME => ELEMENT_TYPE, ... );

# Declare struct at compile time
use Class::Struct CLASS_NAME => [ELEMENT_NAME => ELEMENT_TYPE, ...];
use Class::Struct CLASS_NAME => {ELEMENT_NAME => ELEMENT_TYPE, ...};

# declare struct at compile time, based on array, implicit
# class name:
package CLASS_NAME;
use Class::Struct ELEMENT_NAME => ELEMENT_TYPE, ... ;

package Myobj;
use Class::Struct;
        # declare struct with four types of elements:
struct( s => '$', a => '@', h => '%', c => 'My_Other_Class' );

my $obj = Myobj->new;               # constructor

                                # scalar type accessor:
my $element_value = $obj->s;           # element value
$obj->s('new value');               # assign to element

                                # array type accessor:
my $ary_ref = $obj->a;                 # reference to whole array
my $ary_element_value = $obj->a(2);    # array element value
$obj->a(2, 'new value');            # assign to array element

                                # hash type accessor:
my $hash_ref = $obj->h;                # reference to whole hash
my $hash_element_value = $obj->h('x'); # hash element value
$obj->h('x', 'new value');          # assign to hash element

                                # class type accessor:
my $element_value = $obj->c;           # object reference
$obj->c->method(...);               # call method of object
$obj->c(new My_Other_Class);        # assign a new object

说明

Class::Struct 导出一个函数,struct。给定元素名称和类型列表,以及可选的类名称,struct 创建一个实现“类 struct 类似”数据结构的 Perl 5 类。

新类获得一个构造函数方法 new,用于创建 struct 对象。

struct 数据中的每个元素都有一个访问器方法,用于分配给元素并获取其值。可以通过在包中声明同名 sub 来覆盖默认访问器。(请参阅示例 2。)

每个元素的类型可以是标量、数组、哈希或类。

struct() 函数

struct 函数有三种形式的参数列表。

struct( CLASS_NAME => [ ELEMENT_LIST ]);
struct( CLASS_NAME => { ELEMENT_LIST });
struct( ELEMENT_LIST );

第一种和第二种形式明确指明了要创建的类的名称。第三种形式假定当前包名称为类名称。

由第一种和第三种形式创建的类的对象基于数组,而由第二种形式创建的类的对象基于哈希。基于数组的形式会快一些且更小;基于哈希的形式更灵活。

struct 创建的类不能是除 UNIVERSAL 以外的另一个类的子类。

但是,它可以用作其他类的超类。为了方便这一点,生成的构造方法使用双参数祝福。此外,如果该类基于哈希,则每个元素的键都以类名称为前缀(请参阅《Perl Cookbook》,第 13.12 条)。

在由 struct 创建的类中,不得明确定义名为 new 的函数。

ELEMENT_LIST 的形式为

NAME => TYPE, ...

每个名称-类型对声明结构的一个元素。每个元素名称都将定义为访问器方法,除非明确定义了该名称的方法;在后一种情况下,如果设置了警告标志 (-w),则会发出警告。

编译时类创建

Class::Struct 可以在编译时创建你的类。这样做的主要原因显而易见,因此你的类就像 Perl 中的任何其他类一样。在编译时创建你的类会使事件顺序类似于使用任何其他类(或 Perl 模块)。

编译时类创建和运行时类创建之间没有显着的速度提升,只是一种新的、更标准的事件顺序。

元素类型和访问器方法

四种元素类型——标量、数组、哈希和类——由字符串表示——'$''@''%' 和类名称——前面可以加上 '*'

struct 为元素提供的访问器方法取决于元素的声明类型。

标量 ('$''*$')

元素是标量,默认初始化为 undef(但请参阅 "使用 new 初始化")。

访问器的参数(如果有)被分配给元素。

如果元素类型是 '$',则返回元素的值(在分配之后)。如果元素类型是 '*$',则返回对元素的引用。

数组 ('@''*@')

元素是一个数组,默认初始化为 ()

如果没有参数,访问器将返回对元素的整个数组的引用(无论元素是否指定为 '@''*@')。

如果有一个或两个参数,第一个参数是一个指定数组中一个元素的索引;第二个参数(如果存在)将分配给数组元素。如果元素类型为 '@',访问器将返回数组元素值。如果元素类型为 '*@',则返回对数组元素的引用。

作为一个特例,当访问器以数组引用作为唯一参数调用时,这将导致对整个数组元素进行赋值。将返回对象引用。

哈希 ('%''*%')

元素是一个哈希,默认初始化为 ()

如果没有参数,访问器将返回对元素的整个哈希的引用(无论元素是否指定为 '%''*%')。

如果有一个或两个参数,第一个参数是一个指定哈希中一个元素的键;第二个参数(如果存在)将分配给哈希元素。如果元素类型为 '%',访问器将返回哈希元素值。如果元素类型为 '*%',则返回对哈希元素的引用。

作为一个特例,当访问器以哈希引用作为唯一参数调用时,这将导致对整个哈希元素进行赋值。将返回对象引用。

类 ('Class_Name''*Class_Name')

元素的值必须是对命名类或其子类进行祝福的引用。该元素默认情况下未初始化。

访问器的参数(如果有)将分配给元素。如果这不是一个适当的对象引用,访问器将 croak

如果元素类型不以 '*' 开头,访问器将返回元素值(在赋值之后)。如果元素类型以 '*' 开头,则返回对元素本身的引用。

使用 new 初始化

struct 始终创建一个名为 new 的构造函数。该构造函数可以获取一个初始化器列表,用于新结构的各个元素。

每个初始化器都是一对值:元素名称 => 。标量元素的初始化器值只是一个标量值。数组元素的初始化器是一个数组引用。哈希的初始化器是一个哈希引用。

类元素的初始化器是对应类的对象,或其子类的对象,或对哈希的引用,其中包含要传递给元素构造函数的命名参数。

有关初始化的示例,请参见下面的示例 3。

示例

示例 1

为结构元素提供也是结构的类类型是嵌套结构的方式。此处,Timeval 表示时间(秒和微秒),Rusage 有两个元素,每个元素的类型均为 Timeval

use Class::Struct;

struct( Rusage => {
    ru_utime => 'Timeval',  # user time used
    ru_stime => 'Timeval',  # system time used
});

struct( Timeval => [
    tv_secs  => '$',        # seconds
    tv_usecs => '$',        # microseconds
]);

# create an object:
my $t = Rusage->new(ru_utime=>Timeval->new(),
    ru_stime=>Timeval->new());

# $t->ru_utime and $t->ru_stime are objects of type Timeval.
# set $t->ru_utime to 100.0 sec and $t->ru_stime to 5.0 sec.
$t->ru_utime->tv_secs(100);
$t->ru_utime->tv_usecs(0);
$t->ru_stime->tv_secs(5);
$t->ru_stime->tv_usecs(0);
示例 2

可以重新定义访问器函数,以提供对值等的附加检查。此处,我们希望 count 元素始终为非负,因此我们相应地重新定义 count 访问器。

package MyObj;
use Class::Struct;

# declare the struct
struct ( 'MyObj', { count => '$', stuff => '%' } );

# override the default accessor method for 'count'
sub count {
    my $self = shift;
    if ( @_ ) {
        die 'count must be nonnegative' if $_[0] < 0;
        $self->{'MyObj::count'} = shift;
        warn "Too many args to count" if @_;
    }
    return $self->{'MyObj::count'};
}

package main;
$x = new MyObj;
print "\$x->count(5) = ", $x->count(5), "\n";
                        # prints '$x->count(5) = 5'

print "\$x->count = ", $x->count, "\n";
                        # prints '$x->count = 5'

print "\$x->count(-5) = ", $x->count(-5), "\n";
                        # dies due to negative argument!
示例 3

可以向生成类的构造函数传递 element=>value 对的列表,用以初始化结构。如果未为特定元素指定初始化器,则会执行其默认初始化。不存在的元素的初始化器将被静默忽略。

请注意,嵌套类的初始化器可以指定为该类的对象,或指定为对哈希的引用,其中包含传递给嵌套结构的构造函数的初始化器。

use Class::Struct;

struct Breed =>
{
    name  => '$',
    cross => '$',
};

struct Cat =>
[
    name     => '$',
    kittens  => '@',
    markings => '%',
    breed    => 'Breed',
];


my $cat = Cat->new( name => 'Socks',
           kittens  => ['Monica', 'Kenneth'],
           markings => { socks=>1, blaze=>"white" },
           breed    => Breed->new(name=>'short-hair', cross=>1),
      or:  breed    => {name=>'short-hair', cross=>1},
                  );

print "Once a cat called ", $cat->name, "\n";
print "(which was a ", $cat->breed->name, ")\n";
print "had 2 kittens: ", join(' and ', @{$cat->kittens}), "\n";

作者和修改历史记录

Damian Conway 于 2001-09-10 修改,v0.62。

Modified implicit construction of nested objects.
Now will also take an object ref instead of requiring a hash ref.
Also default initializes nested object attributes to undef, rather
than calling object constructor without args
Original over-helpfulness was fraught with problems:
    * the class's constructor might not be called 'new'
    * the class might not have a hash-like-arguments constructor
    * the class might not have a no-argument constructor
    * "recursive" data structures didn't work well:
              package Person;
              struct { mother => 'Person', father => 'Person'};

Casey West 于 2000-11-08 修改,v0.59。

Added the ability for compile time class creation.

Damian Conway 于 1999-03-05 修改,v0.58。

Added handling of hash-like arg list to class ctor.

Changed to two-argument blessing in ctor to support
derivation from created classes.

Added classname prefixes to keys in hash-based classes
(refer to "Perl Cookbook", Recipe 13.12 for rationale).

Corrected behaviour of accessors for '*@' and '*%' struct
elements.  Package now implements documented behaviour when
returning a reference to an entire hash or array element.
Previously these were returned as a reference to a reference
to the element.

Jim Miner 于 1997-04-02 重命名为 Class::Struct 并修改。

members() function removed.
Documentation corrected and extended.
Use of struct() in a subclass prohibited.
User definition of accessor allowed.
Treatment of '*' in element types corrected.
Treatment of classes as element types corrected.
Class name to struct() made optional.
Diagnostic checks added.

最初由 Dean Roehrich 编写,名为 Class::Template

# Template.pm   --- struct/member template builder
#   12mar95
#   Dean Roehrich
#
# changes/bugs fixed since 28nov94 version:
#  - podified
# changes/bugs fixed since 21nov94 version:
#  - Fixed examples.
# changes/bugs fixed since 02sep94 version:
#  - Moved to Class::Template.
# changes/bugs fixed since 20feb94 version:
#  - Updated to be a more proper module.
#  - Added "use strict".
#  - Bug in build_methods, was using @var when @$var needed.
#  - Now using my() rather than local().
#
# Uses perl5 classes to create nested data types.
# This is offered as one implementation of Tom Christiansen's
# "structs.pl" idea.