|
|
| |
Class::Generate(3) |
User Contributed Perl Documentation |
Class::Generate(3) |
Class::Generate - Generate Perl class hierarchies
use Class::Generate qw(class subclass delete_class);
# Declare class Class_Name, with the following types of members:
class
Class_Name => [
s => '$', # scalar
a => '@', # array
h => '%', # hash
c => 'Class', # Class
c_a => '@Class', # array of Class
c_h => '%Class', # hash of Class
'&m' => 'body', # method
];
# Allocate an instance of class_name, with members initialized to the
# given values (pass arrays and hashes using references).
$obj = Class_Name->new ( s => scalar,
a => [ values ],
h => { key1 => v1, ... },
c => Class->new,
c_a => [ Class->new, ... ],
c_h => [ key1 => Class->new, ... ] );
# Scalar type accessor:
$obj->s($value); # Assign $value to member s.
$member_value = $obj->s; # Access member's value.
# (Class) Array type accessor:
$obj->a([value1, value2, ...]); # Assign whole array to member.
$obj->a(2, $value); # Assign $value to array member 2.
$obj->add_a($value); # Append $value to end of array.
@a = $obj->a; # Access whole array.
$ary_member_value = $obj->a(2); # Access array member 2.
$s = $obj->a_size; # Return size of array.
$value = $obj->last_a; # Return last element of array.
# (Class) Hash type accessor:
$obj->h({ k_1=>v1, ..., k_n=>v_n }) # Assign whole hash to member.
$obj->h($key, $value); # Assign $value to hash member $key.
%hash = $obj->h; # Access whole hash.
$hash_member_value = $obj->h($key); # Access hash member value $key.
$obj->delete_h($key); # Delete slot occupied by $key.
@keys = $obj->h_keys; # Access keys of member h.
@values = $obj->h_values; # Access values of member h.
$another = $obj->copy; # Copy an object.
if ( $obj->equals($another) ) { ... } # Test equality.
subclass s => [ <more members> ], -parent => 'class_name';
The "Class::Generate" package exports
functions that take as arguments a class specification and create from these
specifications a Perl 5 class. The specification language allows many
object-oriented constructs: typed members, inheritance, private members,
required members, default values, object methods, class methods, class
variables, and more.
CPAN contains similar packages. Why another? Because
object-oriented programming, especially in a dynamic language like Perl, is
a complicated endeavor. I wanted a package that would work very hard to
catch the errors you (well, I anyway) commonly make. I wanted a package that
could help me enforce the contract of object-oriented programming. I also
wanted it to get out of my way when I asked.
You create classes by invoking the "class"
function. The "class" function has two
forms:
class Class_Name => [ specification ]; # Objects are array-based.
class Class_Name => { specification }; # Objects are hash-based.
The result is a Perl 5 class, in a package
"Class_Name". This package must not exist
when "class" is invoked.
An array-based object is faster and smaller. A hash-based object
is more flexible. Subsequent sections explain where and why flexibility
matters.
The specification consists of zero or more name/value pairs. Each
pair declares one member of the class, with the given name, and with
attributes specified by the given value.
In the simplest name/value form, the value you give is a string that defines the
member's type. A '$' denotes a scalar member type. A
'@' denotes an array type. A
'%' denotes a hash type. Thus:
class Person => [ name => '$', age => '$' ];
creates a class named "Person"
with two scalar members, "name" and
"age".
If the type is followed by an identifier, the identifier is
assumed to be a class name, and the member is restricted to a blessed
reference of the class (or one of its subclasses), an array whose elements
are blessed references of the class, or a hash whose keys are strings and
whose values are blessed references of the class. For scalars, the
"$" may be omitted; i.e.,
"Class_Name" and
$Class_Name are equivalent. The class need not be
declared using the "Class::Generate"
package.
Each class that you generate has a constructor named
"new". Invoking the constructor creates an
instance of the class. You may provide "new"
with parameters to set the values of members:
class Person => [ name => '$', age => '$' ];
$p = Person->new; # Neither name nor age is defined.
$q = Person->new( name => 'Jim' ); # Only name is defined.
$r = Person->new( age => 32 ); # Only age is defined.
A class has a standard set of accessor methods for each member you specify. The
accessor methods depend on a member's type.
The member is a scalar. The member has a single method
"name". If called with no arguments, it
returns the member's current value. If called with arguments, it sets the
member to the first value:
$p = Person->new;
$p->age(32); # Sets age member to 32.
print $p->age; # Prints 32.
If the "Class_Name" form is
used, the member must be a reference blessed to the named class or to one of
its subclasses. The method will "croak"
(see Carp) if the argument is not a blessed reference to an instance of
"Class_Name" or one of its subclasses.
class Person => [
name => '$',
spouse => 'Person' # Works, even though Person
]; # isn't yet defined.
$p = Person->new(name => 'Simon Bar-Sinister');
$q = Person->new(name => 'Polly Purebred');
$r = Person->new(name => 'Underdog');
$r->spouse($q); # Underdog marries Polly.
print $r->spouse->name; # Prints 'Polly Purebred'.
print "He's married" if defined $p->spouse; # Prints nothing.
$p->spouse('Natasha Fatale'); # Croaks.
The member is an array. If the @Class form is used, all
members of the array must be a blessed reference to
"Class" or one of its subclasses. An array
member has four associated methods:
- "name"
- With no argument, "name" returns the
member's whole array.
With one argument, "name"'s
behavior depends on whether the argument is an array reference. If it is
not, then the argument must be an integer i, and
"name" returns element i of the
member. If no such element exists,
"name" returns
"undef". If the argument is an array
reference, it is cast into an array and assigned to the member.
With two arguments, the first argument must be an integer
i. The second argument is assigned to element i of the
member.
- "add_name"
- This method appends its arguments to the member's array.
- "name_size"
- This method returns the index of the last element in the array.
- "last_name"
- This method returns the last element of
"name", or
"undef" if
"name" has no elements. It's a shorthand
for
"$o->array_mem($o->array_mem_size)".
For example:
class Person => [ name => '$', kids => '@Person' ];
$p = Person->new;
$p->add_kids(Person->new(name => 'Heckle'),
Person->new(name => 'Jeckle'));
print $p->kids_size; # Prints 1.
$p->kids([Person->new(name => 'Bugs Bunny'),
Person->new(name => 'Daffy Duck')]);
$p->add_kids(Person->new(name => 'Yosemite Sam'),
Person->new(name => 'Porky Pig'));
print $p->kids_size; # Prints 3.
$p->kids(2, Person->new(name => 'Elmer Fudd'));
print $p->kids(2)->name; # Prints 'Elmer Fudd'.
@kids = $p->kids; # Get all the kids.
print $p->kids($p->kids_size)->name; # Prints 'Porky Pig'.
print $p->last_kids->name; # So does this.
The member is a hash. If the %Class form is used, all
values in the hash must be a blessed reference to
"Class" or one of its subclasses. A hash
member has four associated methods:
- "name"
- With no arguments, "name" returns the
member's whole hash.
With one argument that is a hash reference, the member's value
becomes the key/value pairs in that reference. With one argument that is
a string, the element of the hash keyed by that string is returned. If
no such element exists, "name" returns
"undef".
With two arguments, the second argument is assigned to the
hash, keyed by the string representation of the first argument.
- "name_keys"
- The "name_keys" method returns all keys
associated with the member.
- "name_values"
- The "name_values" method returns all
values associated with the member.
- "delete_name"
- The "delete_name" method takes one or
more arguments. It deletes from "name"'s
hash all elements matching the arguments.
For example:
class Person => [ name => '$', kids => '%Kid_Info' ];
class Kid_Info => [
grade => '$',
skills => '@'
];
$f = new Person(
name => 'Fred Flintstone',
kids => { Pebbles => new Kid_Info(grade => 1,
skills => ['Programs VCR']) }
);
print $f->kids('Pebbles')->grade; # Prints 1.
$b = new Kid_Info;
$b->grade('Kindergarten');
$b->skills(['Knows Perl', 'Phreaks']);
$f->kids('BamBam', $b);
print join ', ', $f->kids_keys; # Prints "Pebbles, BamBam",
# though maybe not in that order.
All members also have a method "undef_m". This
method undefines a member "m".
"Class::Generate" also generates methods that
you can invoke on an object instance. These are as follows:
Use the "copy" method to copy the value of an
object. The expression:
$p = $o->copy;
assigns to $p a copy of
$o. Members of $o that are
classes (or arrays or hashes of classes) are copied using their own
"copy" method.
Use the "equals" method to test the equality
of two object instances:
if ( $o1->equals($o2) ) { ... }
The two object instances are equal if members that have values in
$o1 have equal values in
$o2, and vice versa. Equality is tested as you would
expect: two scalar members are equal if they have the same value; two array
members are equal if they have the same elements; two hash members are equal
if they have the same key/value pairs.
If a member's value is restricted to a class, then equality is
tested using that class' "equals" method.
Otherwise, it is tested using the "eq"
operator.
By default, all members participate in the equality test. If one
or more members possess true values for the
"key" attribute, then only those members
participate in the equality test.
You can override this definition of equality. See "ADDING
METHODS".
As shown, you specify each member as a
"name=>value" pair. If the
"value" is a string, it specifies the
member's type. The value may also be a hash reference. You use hash references
to specify additional member attributes. The following is a complete list of
the attributes you may specify for a member:
- type=>string
- If you use a hash reference for a member's value, you must use the
"type" attribute to specify its type:
scalar_member => { type => '$' }
- required=>boolean
- If the "required" attribute is true, the
member must be passed each time the class' constructor is invoked:
class Person => [ name => { type => '$', required => 1 } ];
Person->new ( name => 'Wilma' ); # Valid
Person->new; # Invalid
Also, you may not call
"undef_name" for the member.
- default=>value
- The "default" attribute provides a
default value for a member if none is passed to the constructor:
class Person => [ name => '$',
job => { type => '$',
default => "'Perl programmer'" } ];
$p = Person->new(name => 'Larry');
print $p->job; # Prints 'Perl programmer'.
$q = Person->new(name => 'Bjourne', job => 'C++ programmer');
print $q->job; # Unprintable.
The value is treated as a string that is evaluated when the
constructor is invoked.
For array members, use a string that looks like a Perl
expression that evaluates to an array reference:
class Person => {
name => '$',
lucky_numbers => { type => '@', default => '[42, 17]' }
};
class Silly => {
UIDs => { # Default value is all UIDs
type => '@', # currently in /etc/passwd.
default => 'do {
local $/ = undef;
open PASSWD, "/etc/passwd";
[ map {(split(/:/))[2]} split /\n/, <PASSWD> ]
}'
}
};
Specify hash members analogously.
The value is evaluated each time the constructor is invoked.
In "Silly", the default value for
"UIDs" can change between invocations.
If the default value is a reference rather than a string, it is not
re-evaluated. In the following, default values for
"e1" and
"e2" are based on the members of
@default_value each time
"Example->new" is invoked, whereas
"e3"'s default value is set when the
"class" function is invoked to define
"Example":
@default_value = (1, 2, 3);
$var_name = '@' . __PACKAGE__ . '::default_value';
class Example => {
e1 => { type => '@', default => "[$var_name]" },
e2 => { type => '@', default => \@default_value },
e3 => { type => '@', default => [ @default_value ] }
};
Example->new; # e1, e2, and e3 are all identical.
@default_value = (10, 20, 30);
Example->new; # Now only e3 is (1, 2, 3).
There are two more things to know about default values that
are strings. First, if a member is typed, the
"class" function evaluates its
(string-based) default value to ensure that it is of the correct type
for the member. Be aware of this if your default value has side effects
(and see "Checking Default Value Types").
Second, the context of the default value is the
"new()" method of the package
generated to implement your class. That's why
"e1" in
"Example", above, needs the name of
the current package in its default value.
- post=>code
- The value of this attribute is a string of Perl code. It is executed
immediately after the member's value is modified through its accessor.
Within "post" code, you can refer to
members as if they were Perl identifiers. For instance:
class Person => [ age => { type => '$',
post => '$age *= 2;' } ];
$p = Person->new(age => 30);
print $p->age; # Prints 30.
$p->age(15);
print $p->age; # Prints 30 again.
The trailing semicolon used to be required, but everyone
forgot it. As of version 1.06 it's optional:
'$age*=2' is accepted and equivalent to
'$age*=2;' (but see "BUGS").
You reference array and hash members as usual (except for
testing for definition; see "BUGS"). You can reference
individual elements, or the whole list:
class Foo => [
m1 => { type => '@', post => '$m1[$#m1/2] = $m2{xxx};' },
m2 => { type => '%', post => '@m1 = keys %m2;' }
];
You can also invoke accessors. Prefix them with a
"&":
class Bar => [
m1 => { type => '@', post => '&undef_m1;' },
m2 => { type => '%', post => '@m1 = &m2_keys;' }
];
$o = new Bar;
$o->m1([1, 2, 3]); # m1 is still undefined.
$o->m2({a => 1, b => 2}); # Now m1 is qw(a b).
- pre=>code
- The "pre" key is similar to the
"post" key, but it is executed just
before an member is changed. It is not executed if the member is
only accessed. The "pre" and
"post" code have the same scope, which
lets you share variables. For instance:
class Foo => [
mem => { type => '$', pre => 'my $v = $mem;', post => 'return $v;' }
];
$o = new Foo;
$p = $o->mem(1); # Sets $p to undef.
$q = $o->mem(2); # Sets $q to 1.
is a way to return the previous value of
"mem" any time it's modified (but see
"NOTES").
- assert=>expression
- The value of this key should be a Perl expression that evaluates to true
or false. Use member names in the expression, as with
"post". The expression will be tested
any time the member is modified through its accessors. Your code will
"croak" if the expression evaluates to
false. For instance,
class Person => [
name => '$',
age => { type => '$',
assert => '$age =~ /^\d+$/ && $age < 200' } ];
ensures the age is reasonable.
The assertion is executed after any
"post" code associated with the
member.
- private=>boolean
- If the "private" attribute is true, the
member cannot be accessed outside the class; that is, it has no accessor
functions that can be called outside the scope of the package defined by
"class". A private member can, however,
be accessed in "post",
"pre", and
"assert" code of other members of the
class.
- protected=>boolean
- If the "protected" attribute is true,
the member cannot be accessed outside the class or any of its subclasses.
A protected member can, however, be accessed in
"post",
"pre", and
"assert" code of other members of the
class or its subclasses.
- readonly=>boolean
- If this attribute is true, then the member cannot be modified through its
accessors. Users can set the member only by using the class constructor.
The member's accessor that is its name can retrieve but not set the
member. The "undef_"name accessor
is not defined for the member, nor are other accessors that might modify
the member. (Code in "post" can set it,
however.)
- key=>boolean
- If this attribute is true, then the member participates in equality tests.
See "Equals".
- nocopy=>value
- The "nocopy" attribute gives you some
per-member control over how the "copy"
method. If "nocopy" is false (the
default), the original's value is copied as described in "Copy".
If "nocopy" is true, the original's
value is assigned rather than copied; in other words, the copy and the
original will have the same value if the original's value is a
reference.
You may include a "new" attribute in the
specification to affect the constructor. Its value must be a hash reference.
Its attributes are:
- required=>list of constraints
- This is another (and more general) way to require that parameters be
passed to the constructor. Its value is a reference to an array of
constraints. Each constraint is a string that must be an expression
composed of Perl logical operators and member names. For example:
class Person => {
name => '$',
age => '$',
height => '$',
weight => '$',
new => { required => ['name', 'height^weight'] }
};
requires member "name", and
exactly one of "height" or
"weight". Note that the names are
not prefixed with "$",
"@", or
"%".
Specifying a list of constraints as an array reference can be
clunky. The "class" function also lets
you specify the list as a string, with individual constraints separated
by spaces. The following two strings are equivalent to the above
"required" attribute:
'name height^weight'
'name&(height^weight)'
However, 'name & (height ^
weight)' would not work. The
"class" function interprets it as a
five-member list, four members of which are not valid expressions.
This equivalence between a reference to array of strings and a
string of space-separated items is used throughout
"Class::Generate". Use whichever form
works best for you.
- post=>string of code
- The "post" key is similar to the
"post" key for members. Its value is
code that is inserted into the constructor after parameter values have
been assigned to members. The "class"
function performs variable substitution.
The "pre" key is not
recognized in "new".
- assert=>expression
- The "assert" key's value is inserted
just after the "post" key's value (if
any). Assertions for members are inserted after the constructor's
assertion.
- comment=>string
- This attribute's value can be any string. If you save the class to a file
(see "Saving the Classes"), the string is included as a comment
just before the member's methods.
- style=>style definition
- The "style" attribute controls how
parameters are passed to the class' constructor. See "PARAMETER
PASSING STYLES".
Accessors often do not provide a class with enough functionality. They also do
not encapsulate your algorithms. For these reasons, the
"class" function lets you add methods. Both
object methods and class methods are allowed.
Add methods using an member of the form
"'&name'=>body", where
"body" is a string containing valid Perl
code. This yields a method "name" with the
specified "body". For object methods, the
"class" function performs variable
substitution as described in "ADVANCED MEMBER SPECIFICATIONS". For
example,
class Person => [ first_name => '$',
last_name => '$',
'&name' => q{return "$first_name $last_name";}
];
$p = Person->new(first_name => 'Barney', last_name => 'Rubble');
print $p->name; # Prints "Barney Rubble".
class Stack => [ # Venerable example.
top_e => { type => '$', private => 1, default => -1 },
elements => { type => '@', private => 1, default => '[]' },
'&push' => '$elements[++$top_e] = $_[0];',
'&pop' => '$top_e--;',
'&top' => 'return $elements[$top_e];'
];
A method has the following attributes which, like members, are
specified as a hash reference:
- body=>code
- This attribute specifies the method's body. It is required if other
attributes are given.
- private=>boolean
- The method cannot be accessed outside the class.
class Example => [
m1 => '$',
m2 => '$',
'&public_interface' => q{return &internal_algorithm;},
'&internal_algorithm' => { private => 1,
body => q{return $m1 + 4*$m2;} }
];
$e = Example->new(m1 => 2, m2 => 4);
print $e->public_interface; # Prints 18.
print $e->internal_algorithm; # Run-time error.
- protected=>boolean
- If true, the method cannot be accessed outside the class or any of its
subclasses.
- class_method=>boolean
- If true, the method applies to classes rather than objects. Member name
substitution is not performed within a class method. You can,
however, use accessors, as usual:
class Foo => [
m1 => '$', m2 => '@',
'&to_string' => {
class_method => 1,
body => 'my $o = $_[0];
return $o->m1 . "[" . join(",", $o->m2) . "]"';
}
];
$f = Foo->new(m1 => 1, m2 => [2, 3]);
print Foo->to_string($f); # Prints "1[2,3]"
You can also call class methods from within instance and class
methods. Use the "Class->method"
syntax.
Currently, a class method may be public or private, but not
protected.
- objects=>list of object instance expressions
- This attribute is only used for class methods. It is needed when the
method refers to a private or protected member or method. Its argument is
a list, each element of which is a Perl expression that occurs in the body
of the class method. The expression must evaluate to an instance of the
class. It will be replaced by an appropriate reference to the private
member or method. For example:
class Bar => [
mem1 => '$',
mem2 => { type => '@', private => 1 },
'&meth1' => 'return $mem1 + $#mem2;',
'&meth2' => { private => 1,
body => 'return eval join "+", @mem2;' },
'&cm1' => { class_method => 1,
objects => '$o',
body => 'my $o = Bar->new(m1 => 8);
$o->mem2([4, 5, 6]);
return $o->meth2;' },
'&cm2' => { class_method => 1,
objects => ['$o', 'Bar->new(m1 => 3)'],
body => 'my $o => Bar->new(m1 => 8);
$o->mem2(0, Bar->new(m1 => 3)->meth2);
return $o;' }
];
The "objects" attribute for
"cm1" tells
"class" to treat all occurrences of
the string $o as an instance of class
"Bar", giving the expression access to
private members and methods of "Bar".
The string can be an arbitrary expression, as long as it's valid Perl
and evaluates to an instance of the class; hence the use of
"Bar->new(m1 => 3)" in
"cm2". The string must match exactly,
so "Bar->new(m1 => 8)" is not
replaced.
You can add a method named &equals to
provide your own definition of equality. An example:
$epsilon = 0.1e-20;
class Imaginary_No => { # Treat floating-point
real => '$', # values as equal if their
imag => '$', # difference is less than
'&equals' => qq|my \$o = \$_[0]; # some epsilon.
return abs(\$real - \$o->real) < $epsilon &&
abs(\$imag - \$o->imag) < $epsilon;|
};
If you declare &equals to be private,
you create a class whose instances cannot be tested for equality except
within the class.
The "subclass()" function declares classes
that are subclasses of another class. The statement:
subclass S => [ specification ], -parent => P
declares a package "s", and a
constructor function "new", just like
"class"; but
"s->new" yields a blessed reference
that inherits from "P". You can use all
the attributes discussed above in the specification of a subclass. (Prior to
version 1.02 you specified the parent using
"parent=>P", but this is deprecated in
favor of "-parent=>P".)
As of version 1.05, the "class"
function also accepts "-parent". In other
words,
class S => [ specification ], -parent => P
subclass S => [ specification ], -parent => P
are equivalent.
Class "P" need not have been
defined using the "class" or
"subclass" function. It must have a
constructor named "new", however, or at
least be an ancestor of a class that does.
A subclass may be either an array or hash reference. Its parent
must be the same type of reference.
You can inherit from multiple classes, providing all are
hash-based ("Class::Generate" does not
support multiple inheritance for array-based classes). Just list more than
one class as the value of "parent":
subclass S => { specification }, -parent => 'P1 P2 P3';
Elements of the @ISA array for package
"S" appear in the order you list them.
This guarantee should let you determine the order in which methods are
invoked.
The subclass constructor automatically calls its parent
constructor. It passes to the parent constructor any parameters that aren't
members of the subclass.
Subclass members with the same name as that of their parent
override their parent methods.
You can access a (non-private) member or method of a parent within
user-defined code in the same way you access members or methods of the
class:
class Person => [
name => '$',
age => '$',
'&report' => q{return "$name is $age years old"}
];
subclass Employee => [
job_title => '$',
'&report' => q{return "$name is $age years old and is a $job_title";}
], -parent => 'Person';
subclass Manager => [
workers => '@Employee',
'&report' => q{return "$name is $age years old, is a $job_title, " .
"and manages " . (&workers_size + 1) . " people";}
], -parent => 'Employee';
If a class has multiple parents, and these parents have members
whose names conflict, the name used is determined by a depth-first search of
the parents.
The previous example shows that a subclass may declare a member or
method whose name is already declared in one of its ancestors. The subclass
declaration overrides any ancestor declarations: the
"report" method behaves differently
depending on the class of the instance that invokes it.
Sometimes you override an ancestor's method to add some extra
functionality. In that situation, you want the overriding method to invoke
the ancestor's method. All user-defined code in
"Class::Generate" has access to a variable
$self, which is a blessed reference. You therefore
can use Perl's "SUPER::" construct to get
at ancestor methods:
class Person => [
name => '$',
age => '$',
'&report' => q{return &name . ": $age years old"}
];
subclass Employee => [
job_title => '$',
'&report' => q{return $self->SUPER::report . "; job: $job_title";}
], -parent => 'Person';
subclass Manager => [
workers => '@Employee',
'&report' => q{return $self->SUPER::report . "; manages: " .
(&workers_size + 1) . " people";}
], -parent => 'Employee';
Currently, you cannot access a protected method of an ancestor
this way. The following code will generate a run-time error:
class Foo => [
'&method' => { protected => 1, body => '...' },
];
subclass Bar => [
'&method' => { protected => 1, body => '$self->SUPER::method;' }
], -parent => 'Foo';
You can delete classes you declare using
"Class::Generate", after which you can
declare another class with the same name. Use the
"delete_class" function, which accepts as
arguments one or more class names:
class Person => [ name => '$' ];
delete_class 'Person'; # Nah...
class Name => [ last => '$', first => '$' ]; # ... let's really encapsulate.
class Person => [ name = 'Name' ]; # That's better.
This function silently ignores classes that don't exist, but it
croaks if you try to delete a package that wasn't declared using
"Class::Generate".
You can affect the specification of a class using certain flags. A flag is a
key/value pair passed as an argument to
"class" (or
"subclass"). The first character of the key
is always a hyphen. The following is a list of recognized flags:
- -use=>list
- Your "pre" or
"post" code, or your methods, may want
to use functions declared in other packages. List these packages as the
value of the "-use" flag. For example,
suppose you are creating a class that does date handling, and you want to
use functions in the "Time::Local" and
"Time::localtime" packages. Write the
class as follows:
class Date_User => [ ... ],
-use => 'Time::Local Time::localtime';
Any code you add to
"Date_User" can now access the time
functions declared in these two packages.
To import functions, you need to use the array reference
form:
class Foo => [ ... ],
-use => ["FileHandle 'autoflush'"];
Otherwise, the "class"
function would assume you want to use two packages,
"Filehandle" and
'autoflush'.
- -class_vars=>list
- A class can have class variables, i.e., variables accessible to all
instances of the class as well as to class methods. Specify class
variables using the "-class_vars" flag.
For example, suppose you want the average age of all Persons:
$compute_average_age = '$n++; $total += $age; $average = $total/$n;';
class Person => [
name => '$',
age => { type => '$', required => 1, readonly => 1 },
new => { post => $compute_average_age }
],
-class_vars => '$n $total $average';
$p = Person->new(age => 24); # Average age is now 24.
$q = Person->new(age => 30); # Average age is now 27.
You can also provide an initial value for class variables.
Specify the value of "-class_vars" as
an array reference. If any member of this array is a hash reference, its
members are taken to be variable name/initial value pairs. For
example:
class Person => [ name => '$',
age => { type => '$', required => 1, readonly => 1 },
new => { post => $compute_average_age }
],
-class_vars => [ { '$n' => 0 },
{ '$total' => 0 },
'$average' ];
"Class::Generate" evaluates
the initial value as part of evaluating your class. This means you must
quote any strings:
class Room => [
],
-class_vars => [ { '$board' => "'white'" } ];
- -virtual=>boolean
- A virtual class is a class that cannot be instantiated, although its
descendents can. Virtual classes are useful as modeling aids and debugging
tools. Specify them using the "-virtual"
flag:
class Parent => [ e => '$' ], -virtual => 1;
subclass Child => [ d => '$' ], -parent => 'Parent';
Child->new; # This is okay.
Parent->new; # This croaks.
There is no built-in way to specify a virtual method, but the
following achieves the desired effect:
class Foo => [ '&m' => 'die "m is a virtual method";' ];
- -comment=>string
- The string serves as a comment for the class.
- -options=>{ options }
- This flag lets you specify options that alter the behavior of
"Class::Generate". See
"OPTIONS".
- -exclude=>string
- Sometimes it isn't appropriate for a user to be able to copy an object.
And sometimes testing the equality of two object instances makes no sense.
For these and other situations, you have some control over the
automatically generated methods for each class.
You control method generation using the
"-exclude" flag. Its value is a string
of space-separated regular expressions. A method is included if it does
not match any of the regular expressions. For example, a person
has a unique social security number, so you might want a class where a
person can't be copied:
class Person => [
name => '$',
ssn => { type => '$', readonly => 1 }
], -exclude => '^copy$';
$o = Person->new name => 'Forrest Gump', ssn => '000-00-0000';
$p = $o->copy; # Run-time error.
The "-exclude" flag can
describe a whole range of esoteric restrictions:
class More_Examples => [
mem1 => { type => '$', required => 1 },
mem2 => { type => '@', required => 1 },
mem3 => '%',
new => { post => '%mem3 = map { $_ => $mem1 } @mem2' }
], -exclude => 'undef_ mem2_size mem3';
$o = More_Examples->new mem1 => 1, mem2 => [2, 3, 4];
@keys = $o->mem3_keys; # Run-time error.
$o->undef_mem1; # Ditto.
print $o->last_mem2; # This works as expected.
print $o->mem2_size; # But this blows up.
In "More_Examples", it isn't
possible to undefine a member. The size of
"mem2" can't be determined. And
"mem3" is effectively private (it
can't even be accessed from class methods).
By default, parameters to the constructor are passed using a
"name=>value" form. You have some control
over this using the "style" key in a
constructor's "new" specifier. It lets you
pass parameters to constructors using one of the following styles:
- Key/Value
- This is the default. Parameters are passed using the
"name=>value" form, as shown in
previous examples. Specify it as
"style=>'key_value'" if you want to
be explicit.
- Positional
- Parameters are passed based on a positional order you specify. For
example:
class Foo => [ e1 => '$', e2 => '@', e3 => '%',
new => { style => 'positional e1 e2 e3' } ];
$obj = Foo->new(1); # Sets e1 to 1.
$obj = Foo->new(1, [2,3]); # Ditto, and sets e2 to (2,3).
my %hash = (foo => 'bar');
$obj = Foo->new(1, # Ditto,
[2, 3], # ditto,
{%hash}); # and sets e3 to %hash.
You must list all non-private members, although you do not
have to include all of them in every invocation of
"new". Also, if you want to set
"e1" and
"e3" but not
"e2", you can give
"undef" as
"e2"'s value:
$obj = Foo->new(1, undef, { e3_value => 'see what I mean?' });
- Mixed Styles
- Parameters are passed mixing the positional and key/value styles. The
values following "mix" are the names of
the positional parameters, in the order in which they will be passed. This
style is useful when certain parameters are "obvious". For
example:
class Person => [
first_name => { type => '$', required => 1 },
last_name => { type => '$', required => 1 },
favorite_language => '$',
favorite_os => '$',
new => { style => 'mix first_name last_name' }
];
$obj = Person->new('Joe', 'Programmer', favorite_language => 'Perl');
$obj = Person->new('Sally', 'Codesmith', favorite_os => 'Linux');
The positional parameters need not be required, but they must
all be given if you want to set any members passed as key/value
parameters.
- Your Own Parameter Handling
- Finally, sometimes you want a constructor whose parameters aren't the
members. Specify such classes using
"own". Access the parameters through
@_, as usual in Perl:
class Person => [
first => { type => '$', private => 1 },
last => { type => '$', private => 1 },
new => { style => 'own',
post => '($first, $last) = split /\s+/, $_[0];' },
'&name' => q|return $last . ', ' . $first;|
];
$p = Person->new('Fred Flintstone');
print $p->name; # Prints 'Flintstone, Fred'.
If "own" is followed by a
space, and the class has a parent, everything after the space is treated
as a space-separated list of Perl expressions, and these expressions are
passed to the superclass constructor. The expressions are passed in the
order given. Thus:
subclass Child => [
grade => '$',
new => { style => 'own $_[0]', post => '$grade = $_[1];' }
], -parent => 'Person';
Now you can create a "Child"
by passing the grade as the second parameter, and the name as the
first:
$c = Child->new('Penny Robinson', 5);
The "own" style causes the
"type",
"required", and
"default" member specifications to be
ignored.
If you use styles other than
"key_value", you must be aware of how a
subclass constructor passes parameter to its superclass constructor.
"Class::Generate" has some understanding
of styles, but not all combinations make sense, and for those that do, you
have to follow certain conventions. Here are the rules for a subclass
"S" with parent
"P":
- 1.
- If "S"'s constructor uses the
"key_value" style,
"P"'s constructor must use the
"key_value" or
"own" style. The parameters are treated
as a hash; "P"'s constructor receives a
hash with all elements indexed by nonprivate members of
"S" deleted.
"P"'s constructor must not expect the
hash elements to be passed in a prespecified order.
- 2.
- If "S"'s constructor uses the
"positional" style,
"P"'s constructor may use any style. If
"S" has
"n" nonprivate members, then parameters
0..n-1 are used to assign members of
"S". Remaining parameters are passed to
"P::new", in the same order they were
passed to "S::new".
- 3.
- If "S"'s constructor uses the
"mix" style,
"P"'s constructor may use any style. If
"S" has n nonprivate members, of
which p are passed by position, then parameters
0..(p-1)+2*(n-p) are used to assign members of
"S". Remaining parameters are passed to
"P::new", in the same order they were
passed to "S::new".
- 4.
- If "S"'s constructor uses the
"own" style, you are responsible for
ensuring that it passes parameters to
"P"'s constructor in the correct
style.
The "Class::Generate" package provides what
its author believes is reasonable default style and behavior. But if you
disagree, there are certain defaults you can control on a class-by-class
basis, or for all classes you generate. These defaults are specified as
options. An option is given in one of two ways:
- 1.
- Via the "-options" flag, the value of
which is a hash reference containing the individual options. This affects
an individual class.
- 2.
- By setting a variable declared in
"Class::Generate". The variable has the
same name as the option. This affects all subsequently generated classes
except those where the option is explicitly overridden via the
"-options" flag. You may export these
variables, although they are not exported by default.
The following sections list the options that
"class" and
"subclass" recognize.
If the "save" option is true for class
"c", the code implementing it will be saved
to file "c.pm". This is useful in several
situations:
- 1.
- You may need functionality that "class"
and "subclass" cannot provide.
- 2.
- Errors in your methods, or in "pre" and
"post" code, can result in obscure
diagnostics. Debugging classes is easier if they are saved in files. This
is especially true if you use Emacs for debugging, as Emacs does not
handle evaluated expressions very well.
- 3.
- If you have many classes, there is overhead in regenerating them each time
you execute your program. Accessing them in files may be faster.
If the value of "save" looks
like a Perl file name (i.e., if it ends in
".pl" or
".pm"), the class is appended to that
file. This feature lets your program avoid the overhead of opening and
closing multiple files. It also saves you from the burden of maintaining
multiple files.
All comments specified using
"comment" attributes and the
"-comment" flag are saved along with your
code.
Sometimes you want to be able to assign a whole array to an array member (or a
whole hash to a hash member):
class Person => [
name => '$',
parents => '@Person',
new => { style => 'positional name parents' }
];
$p = Person->new('Will Robinson');
$p->parents( [ Person->new('John Robinson'),
Person->new('Maureen Robinson') ] );
But sometimes you don't. Often you only have one member value
available at any time, and you only want to add values:
class Person => [
name => '$',
parents => '@Person',
new => { style => 'positional name parents' }
],
-options => { accept_refs => 0 };
$p = Person->new('Will Robinson');
$p->add_parents( Person->new('Maureen Robinson'),
Person->new('John Robinson') );
Passing references is a matter of taste and situation. If you
don't think you will need or want to do it, set the
"accept_refs" option to false. An added
benefit is that the classes will catch more parameter-passing errors,
because with references there are two meanings for passing a single
parameter to the method.
By default, the classes include a "use strict"
directive. You can change this (if you must) using a false value for the
"strict" option.
You can use Perl's lexical warnings through
"Class::Generate". Any class you generate
can have "use warnings" or not, as you see
fit. This is controlled by the "warnings"
option, the value of which may be:
- A scalar, in which case your class will be generated with
"use warnings" or
"no warnings", depending on whether the
scalar evaluates to true or false, respectively. If the scalar is
"undef", your class won't even contain
"no warnings". If the scalar looks like
a positive integer, your class will contain the single line:
use warnings;
If the scalar doesn't look like a positive integer, its value
is appended after "warnings".
Thus:
-options => { warnings => 'FATAL => qw(void)' }
yields:
use warnings FATAL => qw(void);
- An array reference containing a list of key/value pairs. Valid keys are
"register",
"use", and
"no". If the key is
"register" and the value evaluates to
true, your class will contain a "use
warnings::register" line. If the key is
"use" or
"no", your class will enable or disable
warnings, respectively, according to the value that follows. If the value
looks like a positive integer, your class will contain a
"use"/"no
warnings" line. If it looks like anything else that evaluates
to true, your class will contain a
"use"/"no
warnings" line followed by the value's string representation.
Warnings are enabled and disabled in the order they appear in
the array. For example:
-options => { warnings => [use => 1, no => 'qw(io)', register => 1] }
yields:
use warnings;
no warnings qw(io);
use warnings::register;
Perl won't let you specify a warnings category until you
import a module, so the following won't work:
class Too_Soon => { ... }, -options => { warnings => { register => 1 } };
use warnings 'Too_Soon';
because the "use warnings"
statement is processed at compile time. In fact, you probably don't want
to use the "register" key except for
classes you save. See "Saving the Classes".
The default value of the
"warnings" option is 1, meaning your class
will contain a "use warnings" pragma.
"Class::Generate" is intended for generating
classes, not packages containing classes. For that reason,
"class" and
"subclass" croak if you try to define a
class whose name is a package that already exists. Setting the
"allow_redefine" option to true changes this
behavior. It also opens up a minefield of potential errors, so be careful.
By default, the reference to an object instance is stored in a variable named
$self. If you prefer another name (e.g.,
$this), you can specify it using the
"instance_var" option:
class Foo => {
array => '@'
};
subclass Bar => {
bar_mem => {
type => '$',
pre => 'print "array_size is ", $this->array_size;'
}
}, -parent => 'Foo',
-options => { instance_var => 'this' };
By default, the reference to a class variable is stored in a variable named
$class. If you prefer another name, you can specify it
using the "class_var" option.
The "class" and
"subclass" functions check that any
"pre",
"post", or
"assert" code is valid Perl. They do so by
creating and evaluating a subroutine that contains the code, along with
declarations that mimic the context in which the code would execute. The
alternative would be to wait until the entire class has been defined, but that
would yield error messages with line numbers that don't correspond to your
code. Never underestimate the value of meaningful error messages.
However, this approach results in code being evaluated twice: once
to check its syntax and semantics, and again when the package is created. To
avoid this overhead, set the "check_code"
option to false.
A default value for a member is checked against the type of the member. If a
member is an array, for instance, you will get a warning if
"class" detects that its default value is
anything other than an array reference.
If you specify a default value as a string,
"class" and
"subclass" have to evaluate the string to
see if it yields a value of the correct type. This may cause unwanted
behavior if the string is an expression with side effects. Furthermore, your
program will behave differently depending on whether warnings are in
effect.
If you set the "check_default"
option to false, "class" and
"subclass" will not check the types of
default values. It's common to do so after you have debugged a class.
By default, "Class::Generate" lets you create
a new object instance only from a class name, i.e.,
"Class->new". However, some Perl
programmers prefer another style, wherein you can create an object from either
a class or an instance (see perlobj). The
"nfi" (new from
instance) option controls whether you can use both:
class C => { ... };
$o = C->new; # Always works.
$p = $o->new; # Works if nfi option is true.
Several sections have mentioned errors that will cause your classes to croak if
used incorrectly. Examples include constructor invocation that omits a
required member's value and passing a scalar where a reference is expected.
These checks are useful, especially during debugging, but they can slow your
code. If you set the "check_params" option
to a false value, "Class::Generate" will
omit these checks. Furthermore, if you set
"check_params" to
"undef",
"Class::Generate" will omit assertions too.
A class that checks parameters automatically includes the Carp
package. "Class::Generate" generates code
that uses methods in this package, especially
"croak", to report errors.
The downside of changing the default
"check_params" value should be obvious to
any experienced programmer.
The following is a list of the diagnostics the
"class" and
"subclass" functions can produce. Each
diagnostic is prefixed with "(F)" or "(W)", indicating
that it is fatal or a warning, respectively. Warning messages are only emitted
if you use the "warnings" pragma.
Classes that contain user defined code can yield Perl errors and
warnings. These messages are prefixed by one of the following phrases:
Class class_name, member "name": In "<x>" code:
Class class_name, method "name":
where "<x>" is one of
"pre",
"post", or
"assert". See perldiag for an explanation
of such messages. The message will include a line number that is relative to
the lines in the erroneous code fragment, as well as the line number on
which the class begins. For instance, suppose the file
"stacks.pl" contains the following
code:
#! /bin/perl
use warnings;
use Class::Generate 'class';
class Stack => [
top_e => { type => '$', private => 1, default => -1 },
elements => { type => '@', private => 1, default => '[]' },
'&push' => '$elements[++$top_e] = $_[0];',
'&pop' => '$top_e--;',
'&top' => 'return $elements[$top_e];'
];
class Integer_Stack => [
'&push' => q{die 'Not an integer' if $_[0] !~ /^-?\d+$/;
$self->SUPER:push($_[0]);} # Meant "::", not ":".
], -parent => 'Stack';
Executing this file yields:
Subclass "Integer_Stack", method "push": syntax error at line 2,
near "->SUPER:"
at stacks.pl line 11
meaning the error occurs in the second line of the
"push" method.
The "class" and
"subclass" functions emit the following
diagnostics:
- "-class_vars" flag must be string or array reference
- (F) The value of the "-class_vars" flag
must be a string containing a space-separated list of class variables, or
a reference to an array of strings, each of which specifies one or more
class variables.
- "-exclude" flag must be string or array reference
- (F) The value of the "-exclude" flag
must be a string containing a space-separated list of regular expressions,
or a reference to an array of strings, each of which specifies one or more
regular expressions.
- "-pod" flag must be scalar value or hash reference
- (F) The value of the "-pod" flag must be
either a scalar that evaluates to a boolean value or a hash reference
whose elements denote sections of POD documentation.
- "-use" flag must be string or array reference
- (F) The value of the "-use" flag must be
a string containing a space-separated list of packages to use, or a
reference to an array of strings, each of which is a package to use.
- %s: "%s" is reserved
- (F) The member name conflicts with the instance variable name for the
class. Change the member name, or change the instance variable name using
the "instance_var" option.
- %s: "-pod" flag must be scalar value or hash reference
- (F) You gave a value of the "-pod" flag
for the <class> or <subclass> function that isn't a scalar
value or a hash reference.
- %s: "required" attribute ignored for private/protected member
"%s"
- (W) If member is private or protected, it cannot be a constructor
parameter.
- %s: %s-based class must have %s-based parent (%s is %s-based)
- (F) If the class specified using
"subclass" is specified as an array
reference, its parent must also be specified as array references. If it is
specified as a hash reference, its parent(s) must be specified as hash
references.
- %s: A package of this name already exists
- (F) The name passed to "class" or
"subclass" must not be the name of an
existing package, unless the
"allow_redefine" option is true.
- %s: An array reference based subclass must have exactly one parent
- (F) Multiple inheritance is only permitted for class hierarchies specified
as hash references.
- %s: Cannot append to "%s": %s
- (F) The "save" option was true, but a
class definition cannot be appended to the named file for the specified
operating system-specific reason.
- %s: Cannot save to "%s": %s
- (F) The "save" option was true, but the
class cannot be saved to the named file for the specified operating
system-specific reason.
- %s: Cannot continue after errors
- (F) The class specification includes user defined code that is not valid
Perl.
- %s: Class was not declared using Class::Generate
- (F) An argument of "delete_class" is the
name of a package that wasn't declared using
"class" or
"subclass".
- %s: Default value for "%s" is not correctly typed
- (W) The value of the "default" attribute
specified for the named member is either a reference that is does not
match the type required for the member, or a string that, when evaluated,
does not match the member's type.
- %s: Elements must be in array or hash reference
- (F) A class specification must be an array or hash reference. Scalars,
lists, and other references are not allowed.
- %s: Error in new => { style => '... %s' }: %s is not a member
- (F) A class' constructor specifies a style that requires listing members,
and the list includes a value that is not a member.
- %s: Error in new => { style => '... %s' }: %s is not a public
member
- (F) A class' constructor specifies a style that requires listing members,
and the list includes the name of a private or protected member. Only
public members can be passed as a constructor parameter.
- %s: Error in new => { style => '... %s' }: Name "%s" used
%d times
- (F) A class' constructor specifies a style that requires listing members,
and the list contains the same member name more than once.
- %s: Evaluation failed (problem in Class::Generate?)
- (F) You have done something the creator of
"Class::Generate" did not anticipate,
you've found a bug (or both), or you've got a trailing comment in
user-defined code (see "BUGS"). Please report either of the
first two cases. (This message is accompanied by some contextual
information that can help you identify the error. Please include that
information too.)
- %s: Extra parameters in style specifier
- (F) A class' constructor was specified to use the
"mix" style, but more values were listed
than exist non-private members.
- %s: Invalid member/method name "%s"
- (F) The names of members and methods must be valid Perl identifiers,
starting with a letter or underscore and containing only letters,
underscores, or digits.
- %s: Invalid parameter-passing style (must be string or array
reference)
- (F) Within the "new" attribute of a
class, the "style" attribute's value
must be given as a string or a reference to an array of strings.
- %s: Invalid parameter-passing style type "%s"
- (F) Within the "new" attribute of a
class, the "style" attribute's value
must begin with one of the words
"key_value",
"mix",
"positional", or
"own".
- %s: Invalid specification for objects of "%s" (must be string or
array reference)
- (F) For the named class method, the expressions to be recognized as
objects must be passed as a string or a reference to a list of
strings.
- %s: Invalid specification for required constructor parameters (must be
string or array reference)
- (F) Within the "new" attribute of a
class, the "required" attribute's value
must be given as a string or a reference to an array of strings.
- %s: Invalid specification of member "%s" (must be string or hash
reference with "type" key)
- (F) A member's specification must be given as either a string (describing
its type) or a hash reference (which must contain the
"type" key).
- %s: Invalid specification of method "%s" (must be string or hash
reference with "body" key)
- (F) The value of a method name must be a string (specifying the method's
body) or a hash reference, one of whose elements is keyed by
"body".
- %s: Member "%s": "%s" is not a valid type
- (F) The type of a member must be "$",
"@",
"%", one of these three types followed
by an identifier, or an identifier.
- %s: Member "%s": Unknown attribute "%s"
- (W) One of the attributes given in the hash reference specifying the named
member is not a recognized attribute.
- %s: Member "%s": Value of pod must be a hash reference
- (F) The value of the "pod" attribute
given in the hash reference specifying the name member is not a hash
reference.
- "%s: Member "%s" declared both private and protected
(protected assumed)
- (W) The member's specification has
"TRUE" values for both the
"protected" and
"private" attributes. The more general
attribute, "protected", will be
used.
- %s: Method "%s": A class method cannot be protected
- (F) In the current implementation, a class method must be public or
private.
- %s: Method "%s": Unknown attribute "%s"
- (W) One of the attributes given in the hash reference specifying the named
method is not a recognized attribute.
- %s: Missing/extra members in style
- (F) A class' constructor was specified to use the
"positional" style, but not all
non-private members (or too many members) were listed.
- %s: Name "%s" used %d times
- (F) The set of member and method names cannot contain duplicates. Member
and method names share the same namespace.
- %s: Parent class "%s" was not defined using class() or
subclass(); %s reference assumed
- (W) The "subclass" function permits
parents to be any package, not just those defined using
"class" and
"subclass". However, because certain
capabilities are lost (e.g., base type checking), it emits warnings when
it detects such subclasses. Also, the
"subclass" function assumes that
invoking "parent->new" returns a
blessed reference to whatever the child expects. If it's wrong, you'll get
a run-time error when your program invokes one of the parent's or child's
accessors.
- %s: Parent package "%s" does not exist
- (F) The "subclass" function requires all
parent classes to exist (meaning each is a package) before it is
invoked.
- %s: Probable mismatch calling constructor in superclass
"%s"
- (W) The "subclass" function's
constructor specifies a parameter passing style that is likely to conflict
with the style of its parent's.
- %s: Required params list for constructor contains unknown member
"%s"
- (F) Within the "new" attribute of a
class, the "required" attribute's value
contains a name that is not given as class member.
- %s: Specification for "new" must be hash reference
- (F) The "new" attribute of a class must
be given as a hash reference.
- %s: Value of %s option must be an identifier (without a
"$")
- (F) The value passed to the "class_var"
or "instance_var" option must, when
prefixed with a "$", be a valid Perl
scalar identifier. Note that you do not prefix it with a
"$". Use:
-options => { class_var => 'x' }
and not
-options => { class_var => '$x' }
- Cannot save reference as initial value for "%s"
- (W) References are legal default or initial values. However, they cannot
be saved using the "save" option.
- Each class variable must be scalar or hash reference
- (F) The specification of a class variable must be either a string
containing a valid Perl variable name, or a hash reference; for each
key/value pair of the reference the key must be a valid Perl variable
name, and the value must be a Perl expression to be used as the variable's
default value.
- Expected string or array reference
- (F) The value of an attribute's key/value pair should have been a string
or an array reference, but was not.
- Expected string or hash reference
- (F) The value of an attribute's key/value pair should have been a string
or a hash reference, but was not.
- Invalid parent specification (must be string or array reference)
- (F) The "subclass" function requires the
class' parent(s) to be specified as either a string, or a list of strings
passed as an array reference.
- Missing subclass parent
- (F) The "subclass" function requires the
class' parent(s) to be specified, using the
"parent=>list" form.
- Missing/extra arguments to class()
- (F) The "class" function requires at
least two arguments: the class' name and specification. Its other
arguments must be flags.
- Missing/extra arguments to subclass()
- (F) The "subclass" function requires at
least four arguments: the class' name, the class' specification, and the
class' parent as a key/value pair. Other arguments to
"subclass" must be flags.
- Options must be in hash reference
- (F) The value of the "-options" flag
must be a hash reference.
- Unknown flag "%s" ignored
- (W) One of the arguments to "class" or
"subclass" begins with
"-", but is not a recognized flag.
- Unknown option "%s" ignored
- (W) The "-options" flag includes a
key/value pair where the key is not a recognized option.
- Warnings array reference must have even number of elements
- (F) Your array reference to the
"warnings" option isn't valid. It must
be a list of key/value pairs. See Warnings.
- Warnings array: Unknown key "%s"
- (F) A key in the array used as the value of the
"warnings" option must be
"register",
"use", or
"no".
- Warnings must be scalar value or array reference
- (F) You specified a value for the
"warnings" option that isn't a scalar or
a array reference. See Warnings.
Unless you change the default values of
"Class::Generate"'s diagnostic options, your
classes will contain code that can cause the following diagnostics to be
emitted:
- %s: Failed assertion: %s
- (F) The specified assertion did not evaluate to true.
- %s: Invalid number of parameters
- (F) One of the class' accessors was invoked with too many or too few
parameters.
- %s: Invalid parameter value (expected %s)
- (F) One of the class' accessors was invoked with a parameter of the wrong
type.
- %s: Member is read-only
- (F) Member "m" was specified read-only,
but "$o->m" was invoked with
parameters that looked like an attempt to set
"m".
- %s: Parameter constraint "%s" failed
- (F) The constraint for the constructor, which involves operators, has
failed.
- %s: Virtual class
- (F) The constructor of a virtual class has been directly invoked.
- %s::new: %s: Not a member
- (F) The named class uses the key/value parameter passing style. Its
constructor was invoked with one or more key/value pairs whose keys don't
name a member of the class. This message often occurs when you forget that
keys are needed.
- %s::new: Invalid value for %s
- (F) The class' constructor was given an improper value for a member. If
the member is a list, the value passed is the wrong kind of list (or not a
list). If the member is typed, the value passed isn't of that type.
- %s::new: Missing or invalid value for %s
- (F) The class' constructor was invoked without a value for a required
member, or the value was not of the correct type (e.g., hash reference
where an array reference is needed, not a blessed reference to the
necessary class).
- %s::new: Missing value for required member %s
- (F) The class' constructor was invoked without a required parameter.
- %s::new: Odd number of parameters
- (F) The named class uses the key/value or mix parameter passing style. Its
constructor was invoked with too many or too few (not just right)
parameters, i.e., something that didn't look like a list of key/value
pairs (accounting for any positional parameters in the mix style).
- %s::new: Only %d parameter(s) allowed (%d given)
- (F) The named class uses the positional parameter passing style. Its
constructor was invoked with more parameters than the class has
members.
PERLDIAG(1), PERLLEXWARN(1), PERLOBJ(1), Carp(3),
Class::Struct(3)
Copyright (c) 1999, 2000, 2006 Steven Wartik. All rights reserved. This program
is free software; you can redistribute it and/or modify it under the same
terms as Perl itself.
It would be nice to have more selective control of what errors are checked.
That "pre" and
"post" code share the same name space can
cause problems if the "check_code" option
is true. The reason is that the "post"
code is evaluated with a prepended copy of
"pre" in which all newlines are replaced
by spaces. This replacement ensures that an error message about
"post" refers to the appropriate line
number within "post". However, if the
"pre" code contains the
"<<" quotation form, Perl will
probably complain about a syntax error; and if it contains a comment, Perl
will ignore the first line of the "post"
code. Note that this only affects code checking, not the package that's
generated.
In the current implementation, if member
"m" is an array, then within user-defined
code, the test "if ( @m ) { ... }" does
not work in hash-based classes unless @m is
initialized. Use "$#m != -1" instead. Or
specify "m" as explicitly empty:
m => { type => '@', default => '[]' }
Hash members have an analogous bug. Use
"scalar(&m_keys) != 0" to test if a
hash member is empty in user-defined code.
The &equals and
© functions do not understand multiple
inheritance.
Member and method name substitution in code can be fooled if a
member name is also declared as a variable. Unfortunately the
"class" function doesn't detect this
error. In the following class, the $f of
$f[0] is mistaken for scalar member
"f", not array variable
@f:
class Foo => {
f => {
type => '$',
post => 'my @f;
$f[0] = $f; # This causes an error.
${f}[0] = $f;' # However, this works.
}
};
If a class declares a protected method
"m", and one of its subclasses also
declares "m", the subclass can't access
the parent's version.
A subclass can declare a member
"m" that overrides a parent's declaration
of "m", but it can cause some logical
inconsistencies and isn't recommended. Tne entire model of member overriding
may change in some future version.
The "subclass" function doesn't
test for circularity in inheritance hierarchies.
The "-allow_redefine" option is
intended to let you add functionality to packages not declared using
"Class::Generate". This rule isn't
strictly enforced, but probably should be. If you redefine a class, you
change the constructor, as well as the
"copy" and
"equals" methods. Consider the
following:
class Foo => [ mem1 => '$' ];
class Foo => [ mem2 => '@' ], # Intends to add a member to Foo.
-options => { allow_redefine => 1 };
$o = new Foo;
$o->mem1('this works');
$o->mem2([qw(so does this)]);
$p = $o->copy; # But this doesn't copy mem1.
$p = new Foo mem1 => 'x'; # And this fails.
Use inheritance instead.
The algorithm that adds a trailing semicolon to
"pre" and
"post" code uses a simple regular
expression to test for a semicolon's presence. Please don't strain it by
doing odd things like embedding comments:
post => 'print "foo" # This will fail, with an obscure error message.',
post => 'print "foo"' # Use this form instead.
Default values that are references cannot be saved to a file.
In "pre" and
"post" code, you can access method
parameters via @_. You probably should not do so
except for scalar members, though. For array members, the same code is
inserted into m and "add_"m,
and the code can't tell which method it's in. Even within method m,
the code can't tell if it's looking at a reference or a value if
$accept_refs is true.
The "UNIVERSAL::isa" function is
used to determine if something is an array or hash reference. This presents
possibilities for metaspecification functions.
The name "_cginfo" is reserved
in all packages generated by
"Class::Generate".
Emacs users will quickly discover that the form:
class Foo => [ member_name => '$' ];
causes subsequent lines to be incorrectly indented. The reason is
that perl-mode is confused by '$'. Use instead:
class Foo => [ member_name => "\$" ];
This package clearly owes much to
"Class::Struct". I thank its creators for
their insights.
Speaking of which, unlike
"Class::Struct", the
"m" accessor for member
"m" does not return a value when it is
set. This is a deliberate design decision. If you would like that feature,
you can simulate it for (scalar) member
"m" by including
"return $m;" as
"post" code. However, you then
short-circuit any assertion for member
"m".
The following websites have more information about this module, and may be of
help to you. As always, in addition to those websites please use your favorite
search engine to discover more resources.
- MetaCPAN
A modern, open-source CPAN search engine, useful to view POD
in HTML format.
<https://metacpan.org/release/Class-Generate>
- RT: CPAN's Bug Tracker
The RT ( Request Tracker ) website is the default bug/issue
tracking system for CPAN.
<https://rt.cpan.org/Public/Dist/Display.html?Name=Class-Generate>
- CPANTS
The CPANTS is a website that analyzes the Kwalitee ( code
metrics ) of a distribution.
<http://cpants.cpanauthors.org/dist/Class-Generate>
- CPAN Testers
The CPAN Testers is a network of smoke testers who run
automated tests on uploaded CPAN distributions.
<http://www.cpantesters.org/distro/C/Class-Generate>
- CPAN Testers Matrix
The CPAN Testers Matrix is a website that provides a visual
overview of the test results for a distribution on various
Perls/platforms.
<http://matrix.cpantesters.org/?dist=Class-Generate>
- CPAN Testers Dependencies
The CPAN Testers Dependencies is a website that shows a chart
of the test results of all dependencies for a distribution.
<http://deps.cpantesters.org/?module=Class::Generate>
Please report any bugs or feature requests by email to
"bug-class-generate at rt.cpan.org", or
through the web interface at
<https://rt.cpan.org/Public/Bug/Report.html?Queue=Class-Generate>. You
will be automatically notified of any progress on the request by the system.
The code is open to the world, and available for you to hack on. Please feel
free to browse it and play with it, or whatever. If you want to contribute
patches, please send me a diff or prod me to pull from your repository :)
<https://github.com/shlomif/perl-Class-Generate>
git clone https://github.com/shlomif/perl-Class-Generate
Please report any bugs or feature requests on the bugtracker website
<https://github.com/shlomif/perl-Class-Generate/issues>
When submitting a bug or request, please include a test-file or a
patch to an existing test-file that illustrates the bug or desired
feature.
This software is copyright (c) 2020 by unknown.
This is free software; you can redistribute it and/or modify it
under the same terms as the Perl 5 programming language system itself.
Visit the GSP FreeBSD Man Page Interface. Output converted with ManDoc. |