NAME
Class::Generate - Generate Perl class hierarchies
SYNOPSIS
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';
DESCRIPTION
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.
THE CLASS FUNCTION
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.
MEMBER TYPES
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.
CREATING INSTANCES
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.
ACCESSOR METHODS
A class has a standard set of accessor methods for each member you specify. The accessor methods depend on a member's type.
Scalar (name => '$', name => 'Class_Name', or name => '$Class_Name')
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.
Array (name => '@' or name => '@Class')
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, andname
returns element i of the member. If no such element exists,name
returnsundef
. 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
, orundef
ifname
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.
Hash (name => '%' or name => '%Class')
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
returnsundef
.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 fromname
'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.
COMMON METHODS
All members also have a method undef_m
. This method undefines a member m
.
OBJECT INSTANCE METHODS
Class::Generate
also generates methods that you can invoke on an object instance. These are as follows:
Copy
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.
Equals
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".
ADVANCED MEMBER SPECIFICATIONS
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 forUIDs
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 fore1
ande2
are based on the members of@default_value
each timeExample->new
is invoked, wherease3
's default value is set when theclass
function is invoked to defineExample
:@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 whye1
inExample
, 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 thepost
key, but it is executed just before an member is changed. It is not executed if the member is only accessed. Thepre
andpost
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 willcroak
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 byclass
. A private member can, however, be accessed inpost
,pre
, andassert
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 inpost
,pre
, andassert
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 inpost
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 thecopy
method. Ifnocopy
is false (the default), the original's value is copied as described in "Copy". Ifnocopy
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.
AFFECTING THE CONSTRUCTOR
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 ofheight
orweight
. 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 aboverequired
attribute:'name height^weight' 'name&(height^weight)'
However,
'name & (height ^ weight)'
would not work. Theclass
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 thepost
key for members. Its value is code that is inserted into the constructor after parameter values have been assigned to members. Theclass
function performs variable substitution.The
pre
key is not recognized innew
. - assert=>expression
-
The
assert
key's value is inserted just after thepost
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".
ADDING METHODS
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 forcm1
tellsclass
to treat all occurrences of the string$o
as an instance of classBar
, giving the expression access to private members and methods ofBar
. 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 ofBar->new(m1 => 3)
incm2
. The string must match exactly, soBar->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
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';
THE DELETE_CLASS FUNCTION
delete_class
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
.
FLAGS
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
orpost
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 theTime::Local
andTime::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 ofmem2
can't be determined. Andmem3
is effectively private (it can't even be accessed from class methods).
PARAMETER PASSING STYLES
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 asstyle=>'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 sete1
ande3
but note2
, you can giveundef
ase2
'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 thetype
,required
, anddefault
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
:
If
S
's constructor uses thekey_value
style,P
's constructor must use thekey_value
orown
style. The parameters are treated as a hash;P
's constructor receives a hash with all elements indexed by nonprivate members ofS
deleted.P
's constructor must not expect the hash elements to be passed in a prespecified order.If
S
's constructor uses thepositional
style,P
's constructor may use any style. IfS
hasn
nonprivate members, then parameters 0..n-1 are used to assign members ofS
. Remaining parameters are passed toP::new
, in the same order they were passed toS::new
.If
S
's constructor uses themix
style,P
's constructor may use any style. IfS
has n nonprivate members, of which p are passed by position, then parameters 0..(p-1)+2*(n-p) are used to assign members ofS
. Remaining parameters are passed toP::new
, in the same order they were passed toS::new
.If
S
's constructor uses theown
style, you are responsible for ensuring that it passes parameters toP
's constructor in the correct style.
OPTIONS
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:
Via the
-options
flag, the value of which is a hash reference containing the individual options. This affects an individual class.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.
Saving the Classes
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:
You may need functionality that
class
andsubclass
cannot provide.Errors in your methods, or in
pre
andpost
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.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.
Passing References
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.
Strictness
By default, the classes include a use strict
directive. You can change this (if you must) using a false value for the strict
option.
Warnings
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
orno warnings
, depending on whether the scalar evaluates to true or false, respectively. If the scalar isundef
, your class won't even containno 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
, andno
. If the key isregister
and the value evaluates to true, your class will contain ause warnings::register
line. If the key isuse
orno
, 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 ause
/no warnings
line. If it looks like anything else that evaluates to true, your class will contain ause
/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 theregister
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.
Redefining an Existing Package
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.
Instance Variable Name
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' };
Class Variable Name
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.
Checking User-Defined Code
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.
Checking Default Value Types
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.
Creating New Objects From Instances
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.
Checking Parameter Values
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.
DIAGNOSTICS
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.
Compile-Time Diagnostics
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
orsubclass
must not be the name of an existing package, unless theallow_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 usingclass
orsubclass
. - %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, thestyle
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, thestyle
attribute's value must begin with one of the wordskey_value
,mix
,positional
, orown
. - %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, therequired
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 theprotected
andprivate
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 usingclass
andsubclass
. However, because certain capabilities are lost (e.g., base type checking), it emits warnings when it detects such subclasses. Also, thesubclass
function assumes that invokingparent->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, therequired
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
orinstance_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 theparent=>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 tosubclass
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
orsubclass
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 beregister
,use
, orno
. - 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.
Run-Time Diagnostics
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 setm
. - %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.
SEE ALSO
PERLDIAG(1), PERLLEXWARN(1), PERLOBJ(1), Carp(3), Class::Struct(3)
COPYRIGHT
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.
BUGS
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.
NOTES
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
.