line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# You may distribute under the terms of either the GNU General Public License |
2
|
|
|
|
|
|
|
# or the Artistic License (the same terms as Perl itself) |
3
|
|
|
|
|
|
|
# |
4
|
|
|
|
|
|
|
# (C) Paul Evans, 2012-2022 -- leonerd@leonerd.org.uk |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
package Struct::Dumb; |
7
|
|
|
|
|
|
|
|
8
|
7
|
|
|
7
|
|
369575
|
use strict; |
|
7
|
|
|
|
|
55
|
|
|
7
|
|
|
|
|
173
|
|
9
|
7
|
|
|
7
|
|
31
|
use warnings; |
|
7
|
|
|
|
|
10
|
|
|
7
|
|
|
|
|
253
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
our $VERSION = '0.13'; |
12
|
|
|
|
|
|
|
|
13
|
7
|
|
|
7
|
|
35
|
use Carp; |
|
7
|
|
|
|
|
12
|
|
|
7
|
|
|
|
|
348
|
|
14
|
|
|
|
|
|
|
|
15
|
7
|
|
|
7
|
|
46
|
use Scalar::Util qw( refaddr ); |
|
7
|
|
|
|
|
10
|
|
|
7
|
|
|
|
|
386
|
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
# 'overloading.pm' was only added in 5.10 |
18
|
|
|
|
|
|
|
# Before that we can't easily implement forbidding of @{} overload, so lets not |
19
|
7
|
|
|
7
|
|
34
|
use constant HAVE_OVERLOADING => eval { require overloading }; |
|
7
|
|
|
|
|
11
|
|
|
7
|
|
|
|
|
9
|
|
|
7
|
|
|
|
|
669
|
|
20
|
|
|
|
|
|
|
|
21
|
7
|
|
|
7
|
|
33
|
use constant HAVE_FEATURE_CLASS => defined eval { require feature; $feature::feature{class} }; |
|
7
|
|
|
|
|
10
|
|
|
7
|
|
|
|
|
20
|
|
|
7
|
|
|
|
|
27
|
|
|
7
|
|
|
|
|
1920
|
|
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=head1 NAME |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
C - make simple lightweight record-like structures |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
=head1 SYNOPSIS |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
use Struct::Dumb; |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
struct Point => [qw( x y )]; |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
my $point = Point(10, 20); |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
printf "Point is at (%d, %d)\n", $point->x, $point->y; |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
$point->y = 30; |
38
|
|
|
|
|
|
|
printf "Point is now at (%d, %d)\n", $point->x, $point->y; |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
Z<> |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
struct Point3D => [qw( x y z )], named_constructor => 1; |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
my $point3d = Point3D( z => 12, x => 100, y => 50 ); |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
printf "Point3d's height is %d\n", $point3d->z; |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
Z<> |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
struct Point3D => [qw( x y z )], predicate => "is_Point3D"; |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
my $point3d = Point3D( 1, 2, 3 ); |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
printf "This is a Point3D\n" if is_Point3D( $point3d ); |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
Z<> |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
use Struct::Dumb qw( -named_constructors ) |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
struct Point3D => [qw( x y z )]; |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
my $point3d = Point3D( x => 100, z => 12, y => 50 ); |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
=head1 DESCRIPTION |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
C creates record-like structure types, similar to the C |
67
|
|
|
|
|
|
|
keyword in C, C++ or C#, or C in Pascal. An invocation of this module |
68
|
|
|
|
|
|
|
will create a construction function which returns new object references with |
69
|
|
|
|
|
|
|
the given field values. These references all respond to lvalue methods that |
70
|
|
|
|
|
|
|
access or modify the values stored. |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
It's specifically and intentionally not meant to be an object class. You |
73
|
|
|
|
|
|
|
cannot subclass it. You cannot provide additional methods. You cannot apply |
74
|
|
|
|
|
|
|
roles or mixins or metaclasses or traits or antlers or whatever else is in |
75
|
|
|
|
|
|
|
fashion this week. |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
On the other hand, it is tiny, creates cheap lightweight array-backed |
78
|
|
|
|
|
|
|
structures, uses nothing outside of core. It's intended simply to be a |
79
|
|
|
|
|
|
|
slightly nicer way to store data structures, where otherwise you might be |
80
|
|
|
|
|
|
|
tempted to abuse a hash, complete with the risk of typoing key names. The |
81
|
|
|
|
|
|
|
constructor will C if passed the wrong number of arguments, as will |
82
|
|
|
|
|
|
|
attempts to refer to fields that don't exist. Accessor-mutators will C |
83
|
|
|
|
|
|
|
if invoked with arguments. (This helps detect likely bugs such as accidentally |
84
|
|
|
|
|
|
|
passing in the new value as an argument, or attempting to invoke a stored |
85
|
|
|
|
|
|
|
C reference by passing argument values directly to the accessor.) |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
$ perl -E 'use Struct::Dumb; struct Point => [qw( x y )]; Point(30)' |
88
|
|
|
|
|
|
|
usage: main::Point($x, $y) at -e line 1 |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
$ perl -E 'use Struct::Dumb; struct Point => [qw( x y )]; Point(10,20)->z' |
91
|
|
|
|
|
|
|
main::Point does not have a 'z' field at -e line 1 |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
$ perl -E 'use Struct::Dumb; struct Point => [qw( x y )]; Point(1,2)->x(3)' |
94
|
|
|
|
|
|
|
main::Point->x invoked with arguments at -e line 1. |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
Objects in this class are (currently) backed by an ARRAY reference store, |
97
|
|
|
|
|
|
|
though this is an internal implementation detail and should not be relied on |
98
|
|
|
|
|
|
|
by using code. Attempting to dereference the object as an ARRAY will throw an |
99
|
|
|
|
|
|
|
exception. |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
I: That on development perls that support C |
102
|
|
|
|
|
|
|
is used instead of a blessed ARRAY reference. This implementation choice |
103
|
|
|
|
|
|
|
should be transparent to the end-user, as all the same features are supported. |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
=head2 CONSTRUCTOR FORMS |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
The C and C declarations create two different kinds |
108
|
|
|
|
|
|
|
of constructor function, depending on the setting of the C |
109
|
|
|
|
|
|
|
option. When false, the constructor takes positional values in the same order |
110
|
|
|
|
|
|
|
as the fields were declared. When true, the constructor takes a key/value pair |
111
|
|
|
|
|
|
|
list in no particular order, giving the value of each named field. |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
This option can be specified to the C and C |
114
|
|
|
|
|
|
|
functions. It defaults to false, but it can be set on a per-package basis to |
115
|
|
|
|
|
|
|
default true by supplying the C<-named_constructors> option on the C |
116
|
|
|
|
|
|
|
statement. |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
When using named constructors, individual fields may be declared as being |
119
|
|
|
|
|
|
|
optional. By preceeding the field name with a C> character, the constructor |
120
|
|
|
|
|
|
|
is instructed not to complain if a named parameter is not given for that |
121
|
|
|
|
|
|
|
field; instead it will be set to C. |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
struct Person => [qw( name age ?address )], |
124
|
|
|
|
|
|
|
named_constructor => 1; |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
my $bob = Person( name => "Bob", age => 20 ); |
127
|
|
|
|
|
|
|
# This is valid because 'address' is marked as optional |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
=cut |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
sub import |
132
|
|
|
|
|
|
|
{ |
133
|
10
|
|
|
10
|
|
65
|
my $pkg = shift; |
134
|
10
|
|
|
|
|
22
|
my $caller = caller; |
135
|
|
|
|
|
|
|
|
136
|
10
|
|
|
|
|
17
|
my %default_opts; |
137
|
|
|
|
|
|
|
my %syms; |
138
|
|
|
|
|
|
|
|
139
|
10
|
|
|
|
|
22
|
foreach ( @_ ) { |
140
|
2
|
100
|
|
|
|
5
|
if( $_ eq "-named_constructors" ) { |
141
|
1
|
|
|
|
|
2
|
$default_opts{named_constructor} = 1; |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
else { |
144
|
1
|
|
|
|
|
2
|
$syms{$_}++; |
145
|
|
|
|
|
|
|
} |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
10
|
100
|
|
|
|
43
|
keys %syms or $syms{struct}++; |
149
|
|
|
|
|
|
|
|
150
|
10
|
|
|
|
|
14
|
my %export; |
151
|
|
|
|
|
|
|
|
152
|
10
|
100
|
|
|
|
35
|
if( delete $syms{struct} ) { |
153
|
|
|
|
|
|
|
$export{struct} = sub { |
154
|
7
|
|
|
7
|
|
1193
|
my ( $name, $fields, @opts ) = @_; |
155
|
7
|
|
|
|
|
34
|
_struct( $name, $fields, scalar caller, lvalue => 1, %default_opts, @opts ); |
156
|
9
|
|
|
|
|
65
|
}; |
157
|
|
|
|
|
|
|
} |
158
|
10
|
100
|
|
|
|
40
|
if( delete $syms{readonly_struct} ) { |
159
|
|
|
|
|
|
|
$export{readonly_struct} = sub { |
160
|
1
|
|
|
1
|
|
81
|
my ( $name, $fields, @opts ) = @_; |
161
|
1
|
|
|
|
|
4
|
_struct( $name, $fields, scalar caller, lvalue => 0, %default_opts, @opts ); |
162
|
1
|
|
|
|
|
4
|
}; |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
|
165
|
10
|
50
|
|
|
|
23
|
if( keys %syms ) { |
166
|
0
|
|
|
|
|
0
|
croak "Unrecognised export symbols " . join( ", ", keys %syms ); |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
|
169
|
7
|
|
|
7
|
|
42
|
no strict 'refs'; |
|
7
|
|
|
|
|
14
|
|
|
7
|
|
|
|
|
2299
|
|
170
|
10
|
|
|
|
|
25
|
*{"${caller}::$_"} = $export{$_} for keys %export; |
|
10
|
|
|
|
|
5244
|
|
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=head1 FUNCTIONS |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
=cut |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
my %_STRUCT_PACKAGES; |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
sub _struct |
180
|
|
|
|
|
|
|
{ |
181
|
8
|
|
|
8
|
|
25
|
my ( $name, $_fields, $caller, %opts ) = @_; |
182
|
|
|
|
|
|
|
|
183
|
8
|
|
|
|
|
19
|
my $lvalue = !!$opts{lvalue}; |
184
|
8
|
|
|
|
|
16
|
my $named = !!$opts{named_constructor}; |
185
|
|
|
|
|
|
|
|
186
|
8
|
|
|
|
|
20
|
my $pkg = "${caller}::$name"; |
187
|
|
|
|
|
|
|
|
188
|
8
|
|
|
|
|
17
|
my @fields = @$_fields; |
189
|
|
|
|
|
|
|
|
190
|
8
|
|
|
|
|
12
|
my %optional; |
191
|
8
|
|
66
|
|
|
45
|
s/^\?// and $optional{$_}++ for @fields; |
192
|
|
|
|
|
|
|
|
193
|
8
|
|
|
|
|
11
|
my %subs; |
194
|
8
|
|
|
0
|
|
27
|
$subs{DESTROY} = sub {}; |
195
|
|
|
|
|
|
|
$subs{AUTOLOAD} = sub :lvalue { |
196
|
2
|
|
|
2
|
|
754
|
my ( $field ) = our $AUTOLOAD =~ m/::([^:]+)$/; |
197
|
2
|
|
|
|
|
239
|
croak "$pkg does not have a '$field' field"; |
198
|
0
|
|
|
|
|
0
|
my $dummy; ## croak can't be last because it isn't lvalue, so this line is required |
199
|
8
|
|
|
|
|
28
|
}; |
200
|
|
|
|
|
|
|
|
201
|
8
|
|
|
|
|
11
|
my $constructor; |
202
|
|
|
|
|
|
|
|
203
|
8
|
|
|
|
|
11
|
if( HAVE_FEATURE_CLASS ) { |
204
|
|
|
|
|
|
|
_build_class_for_feature_class( $pkg, \@fields, \%optional, $named, $lvalue, \$constructor ); |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
else { |
207
|
8
|
|
|
|
|
23
|
_build_class_for_classical( $pkg, \@fields, \%optional, $named, $lvalue, \$constructor ); |
208
|
|
|
|
|
|
|
} |
209
|
|
|
|
|
|
|
|
210
|
7
|
|
|
7
|
|
44
|
no strict 'refs'; |
|
7
|
|
|
|
|
10
|
|
|
7
|
|
|
|
|
3754
|
|
211
|
8
|
|
|
|
|
28
|
*{"${pkg}::$_"} = $subs{$_} for keys %subs; |
|
16
|
|
|
|
|
55
|
|
212
|
8
|
|
|
|
|
16
|
*{"${caller}::$name"} = $constructor; |
|
8
|
|
|
|
|
22
|
|
213
|
|
|
|
|
|
|
|
214
|
8
|
100
|
|
|
|
21
|
if( my $predicate = $opts{predicate} ) { |
215
|
1
|
|
50
|
2
|
|
3
|
*{"${caller}::$predicate"} = sub { ( ref($_[0]) || "" ) eq $pkg }; |
|
1
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
15
|
|
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
|
218
|
8
|
|
|
|
|
27
|
*{"${pkg}::_forbid_arrayification"} = sub { |
219
|
1
|
|
|
1
|
|
2
|
return if !HAVE_OVERLOADING and caller eq __PACKAGE__; |
220
|
1
|
|
|
|
|
74
|
croak "Cannot use $pkg as an ARRAY reference" |
221
|
8
|
|
|
|
|
26
|
}; |
222
|
|
|
|
|
|
|
|
223
|
8
|
|
|
|
|
40
|
require overload; |
224
|
|
|
|
|
|
|
$pkg->overload::OVERLOAD( |
225
|
2
|
|
|
2
|
|
8
|
'@{}' => sub { $_[0]->_forbid_arrayification; return $_[0] }, |
|
1
|
|
|
|
|
5
|
|
226
|
1
|
|
|
1
|
|
264
|
'0+' => sub { refaddr $_[0] }, |
227
|
1
|
|
|
1
|
|
16
|
'""' => sub { sprintf "%s=Struct::Dumb(%#x)", $pkg, refaddr $_[0] }, |
228
|
1
|
|
|
1
|
|
3
|
'bool' => sub { 1 }, |
229
|
8
|
|
|
|
|
88
|
fallback => 1, |
230
|
|
|
|
|
|
|
); |
231
|
|
|
|
|
|
|
|
232
|
8
|
|
|
|
|
509
|
$_STRUCT_PACKAGES{$pkg} = { |
233
|
|
|
|
|
|
|
named => $named, |
234
|
|
|
|
|
|
|
fields => \@fields, |
235
|
|
|
|
|
|
|
} |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
sub _build_class_for_classical |
239
|
|
|
|
|
|
|
{ |
240
|
8
|
|
|
8
|
|
21
|
my ( $pkg, $fields, $optional, $named, $lvalue, $constructorvar ) = @_; |
241
|
8
|
|
|
|
|
18
|
my @fields = @$fields; |
242
|
|
|
|
|
|
|
|
243
|
8
|
100
|
|
|
|
17
|
if( $named ) { |
244
|
|
|
|
|
|
|
$$constructorvar = sub { |
245
|
6
|
|
|
6
|
|
1614
|
my %values = @_; |
246
|
6
|
|
|
|
|
8
|
my @values; |
247
|
6
|
|
|
|
|
9
|
foreach ( @fields ) { |
248
|
18
|
100
|
100
|
|
|
150
|
exists $values{$_} or $optional->{$_} or |
249
|
|
|
|
|
|
|
croak "usage: $pkg requires '$_'"; |
250
|
17
|
|
|
|
|
26
|
push @values, delete $values{$_}; |
251
|
|
|
|
|
|
|
} |
252
|
5
|
100
|
|
|
|
24
|
if( my ( $extrakey ) = keys %values ) { |
253
|
1
|
|
|
|
|
62
|
croak "usage: $pkg does not recognise '$extrakey'"; |
254
|
|
|
|
|
|
|
} |
255
|
4
|
|
|
|
|
10
|
bless \@values, $pkg; |
256
|
3
|
|
|
|
|
10
|
}; |
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
else { |
259
|
5
|
|
|
|
|
8
|
my $fieldcount = @fields; |
260
|
5
|
|
|
|
|
27
|
my $argnames = join ", ", map "\$$_", @fields; |
261
|
|
|
|
|
|
|
$$constructorvar = sub { |
262
|
10
|
100
|
|
10
|
|
2298
|
@_ == $fieldcount or croak "usage: $pkg($argnames)"; |
263
|
9
|
|
|
|
|
95
|
bless [ @_ ], $pkg; |
264
|
5
|
|
|
|
|
32
|
}; |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
|
267
|
8
|
|
|
|
|
12
|
my %subs; |
268
|
8
|
|
|
|
|
27
|
foreach ( 0 .. $#fields ) { |
269
|
20
|
|
|
|
|
26
|
my $idx = $_; |
270
|
20
|
|
|
|
|
28
|
my $field = $fields[$idx]; |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
BEGIN { |
273
|
7
|
|
|
7
|
|
1176
|
overloading->unimport if HAVE_OVERLOADING; |
274
|
|
|
|
|
|
|
} |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
$subs{$field} = $lvalue |
277
|
8
|
100
|
|
8
|
|
1743
|
? sub :lvalue { @_ > 1 and croak "$pkg->$field invoked with arguments"; |
278
|
7
|
|
|
|
|
29
|
shift->[$idx] } |
279
|
1
|
50
|
|
1
|
|
5
|
: sub { @_ > 1 and croak "$pkg->$field invoked with arguments"; |
280
|
20
|
100
|
|
|
|
76
|
shift->[$idx] }; |
|
1
|
|
|
|
|
4
|
|
281
|
|
|
|
|
|
|
} |
282
|
|
|
|
|
|
|
|
283
|
7
|
|
|
7
|
|
47
|
no strict 'refs'; |
|
7
|
|
|
|
|
13
|
|
|
7
|
|
|
|
|
5128
|
|
284
|
8
|
|
|
|
|
27
|
*{"${pkg}::$_"} = $subs{$_} for keys %subs; |
|
20
|
|
|
|
|
98
|
|
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
sub _build_class_for_feature_class |
288
|
|
|
|
|
|
|
{ |
289
|
0
|
|
|
0
|
|
0
|
my ( $pkg, $fields, $optional, $named, $lvalue, $constructorvar ) = @_; |
290
|
0
|
|
|
|
|
0
|
my @fields = @$fields; |
291
|
0
|
|
|
|
|
0
|
my %optional = %$optional; |
292
|
|
|
|
|
|
|
|
293
|
0
|
0
|
|
|
|
0
|
if( $named ) { |
294
|
0
|
|
|
|
|
0
|
my %fieldnames = map { $_ => 1 } @fields; |
|
0
|
|
|
|
|
0
|
|
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
$$constructorvar = sub { |
297
|
0
|
|
|
0
|
|
0
|
my %values = @_; |
298
|
0
|
|
|
|
|
0
|
foreach ( @fields ) { |
299
|
0
|
0
|
0
|
|
|
0
|
exists $values{$_} or $optional{$_} or |
300
|
|
|
|
|
|
|
croak "usage: $pkg requires '$_'"; |
301
|
|
|
|
|
|
|
} |
302
|
0
|
|
0
|
|
|
0
|
$fieldnames{$_} or croak "usage: $pkg does not recognise '$_'" for keys %values; |
303
|
0
|
|
|
|
|
0
|
return $pkg->new( %values ); |
304
|
0
|
|
|
|
|
0
|
}; |
305
|
|
|
|
|
|
|
} |
306
|
|
|
|
|
|
|
else { |
307
|
0
|
|
|
|
|
0
|
my $fieldcount = @fields; |
308
|
0
|
|
|
|
|
0
|
my $argnames = join ", ", map "\$$_", @fields; |
309
|
|
|
|
|
|
|
$$constructorvar = sub { |
310
|
0
|
0
|
|
0
|
|
0
|
@_ == $fieldcount or croak "usage: $pkg($argnames)"; |
311
|
0
|
|
|
|
|
0
|
my %values; @values{@fields} = @_; |
|
0
|
|
|
|
|
0
|
|
312
|
0
|
|
|
|
|
0
|
return $pkg->new( %values ); |
313
|
0
|
|
|
|
|
0
|
}; |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
|
316
|
0
|
0
|
|
|
|
0
|
$lvalue = $lvalue ? " :lvalue" : ""; |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
my @fieldcode = map { |
319
|
0
|
|
|
|
|
0
|
my $name = $_; |
|
0
|
|
|
|
|
0
|
|
320
|
0
|
|
|
|
|
0
|
my $var = "\$$name"; |
321
|
|
|
|
|
|
|
|
322
|
0
|
|
|
|
|
0
|
" field $var;", |
323
|
|
|
|
|
|
|
" ADJUST {", |
324
|
|
|
|
|
|
|
" $var = delete \$_[0]->{$name};", |
325
|
|
|
|
|
|
|
" }", |
326
|
|
|
|
|
|
|
" method $name$lvalue { \@_ and croak \"$pkg->$name invoked with arguments\"; $var }", |
327
|
|
|
|
|
|
|
} @$fields; |
328
|
|
|
|
|
|
|
|
329
|
0
|
|
|
|
|
0
|
my $code = join( "\n", |
330
|
|
|
|
|
|
|
"use experimental 'class';", |
331
|
|
|
|
|
|
|
"class $pkg {", |
332
|
|
|
|
|
|
|
" use Carp;", |
333
|
|
|
|
|
|
|
@fieldcode, |
334
|
|
|
|
|
|
|
"}", "" ); |
335
|
|
|
|
|
|
|
|
336
|
0
|
0
|
|
|
|
0
|
eval "$code; 1" or die $@; |
337
|
|
|
|
|
|
|
} |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
=head2 struct |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
struct $name => [ @fieldnames ], |
342
|
|
|
|
|
|
|
named_constructor => (1|0), |
343
|
|
|
|
|
|
|
predicate => "is_$name"; |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
Creates a new structure type. This exports a new function of the type's name |
346
|
|
|
|
|
|
|
into the caller's namespace. Invoking this function returns a new instance of |
347
|
|
|
|
|
|
|
a type that implements those field names, as accessors and mutators for the |
348
|
|
|
|
|
|
|
fields. |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
Takes the following options: |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
=over 4 |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
=item named_constructor => BOOL |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
Determines whether the structure will take positional or named arguments. |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
=item predicate => STR |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
If defined, gives the name of a second function to export to the caller's |
361
|
|
|
|
|
|
|
namespace. This function will be a type test predicate; that is, a function |
362
|
|
|
|
|
|
|
that takes a single argmuent, and returns true if-and-only-if that argument is |
363
|
|
|
|
|
|
|
an instance of this structure type. |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
=back |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
=cut |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
=head2 readonly_struct |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
readonly_struct $name => [ @fieldnames ], |
372
|
|
|
|
|
|
|
... |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
Similar to L, but instances of this type are immutable once |
375
|
|
|
|
|
|
|
constructed. The field accessor methods will not be marked with the |
376
|
|
|
|
|
|
|
C<:lvalue> attribute. |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
Takes the same options as L. |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
=cut |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
=head1 DATA::DUMP FILTER |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
I |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
If L is loaded, an extra filter is applied so that struct |
387
|
|
|
|
|
|
|
instances are printed in a format matching that which would construct them. |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
struct Colour => [qw( red green blue )]; |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
use Data::Dump; |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
my %hash = ( col => Colour( 0.8, 0.5, 0.2 ) ); |
394
|
|
|
|
|
|
|
Data::Dump::dd \%hash; |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
# prints {col => main::Colour(0.8, 0.5, 0.2)} |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
=head1 NOTES |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
=head2 Allowing ARRAY dereference |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
The way that forbidding access to instances as if they were ARRAY references |
403
|
|
|
|
|
|
|
is currently implemented uses an internal method on the generated structure |
404
|
|
|
|
|
|
|
class called C<_forbid_arrayification>. If special circumstances require that |
405
|
|
|
|
|
|
|
this exception mechanism be bypassed, the method can be overloaded with an |
406
|
|
|
|
|
|
|
empty C body, allowing the struct instances in that class to be |
407
|
|
|
|
|
|
|
accessed like normal ARRAY references. For good practice this should be |
408
|
|
|
|
|
|
|
limited by a C override. |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
For example, L needs to access the instances as plain ARRAY |
411
|
|
|
|
|
|
|
references so it can walk the data structure looking for reference cycles. |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
use Devel::Cycle; |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
{ |
416
|
|
|
|
|
|
|
no warnings 'redefine'; |
417
|
|
|
|
|
|
|
local *Point::_forbid_arrayification = sub {}; |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
memory_cycle_ok( $point ); |
420
|
|
|
|
|
|
|
} |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
=head1 TODO |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
=over 4 |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
=item * |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
Consider adding an C option, giving name of another function to |
429
|
|
|
|
|
|
|
convert structs to key/value pairs, or a HASH ref. |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
=back |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
=head1 AUTHOR |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
Paul Evans |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
=cut |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
sub maybe_apply_datadump_filter |
440
|
|
|
|
|
|
|
{ |
441
|
0
|
0
|
|
0
|
0
|
0
|
return unless $INC{"Data/Dump.pm"}; |
442
|
|
|
|
|
|
|
|
443
|
0
|
|
|
|
|
0
|
require Data::Dump::Filtered; |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
Data::Dump::Filtered::add_dump_filter( sub { |
446
|
0
|
|
|
0
|
|
0
|
my ( $ctx, $obj ) = @_; |
447
|
0
|
0
|
|
|
|
0
|
return undef unless my $meta = $_STRUCT_PACKAGES{ $ctx->class }; |
448
|
|
|
|
|
|
|
|
449
|
0
|
|
|
|
|
0
|
my $fields = $meta->{fields}; |
450
|
|
|
|
|
|
|
return { |
451
|
|
|
|
|
|
|
dump => sprintf "%s(%s)", $ctx->class, |
452
|
|
|
|
|
|
|
join ", ", map { |
453
|
0
|
|
|
|
|
0
|
my $field = $fields->[$_]; |
|
0
|
|
|
|
|
0
|
|
454
|
|
|
|
|
|
|
|
455
|
0
|
0
|
|
|
|
0
|
( $meta->{named} ? "$field => " : "" ) . |
456
|
|
|
|
|
|
|
Data::Dump::dump($obj->$field) |
457
|
|
|
|
|
|
|
} 0 .. $#$fields |
458
|
|
|
|
|
|
|
}; |
459
|
0
|
|
|
|
|
0
|
}); |
460
|
|
|
|
|
|
|
} |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
if( defined &Data::Dump::dump ) { |
463
|
|
|
|
|
|
|
maybe_apply_datadump_filter; |
464
|
|
|
|
|
|
|
} |
465
|
|
|
|
|
|
|
else { |
466
|
|
|
|
|
|
|
# A package var we observe that Data/Dump.pm seems to set when loaded |
467
|
|
|
|
|
|
|
# We can't attach to VERSION because too many other things get upset by |
468
|
|
|
|
|
|
|
# that. |
469
|
|
|
|
|
|
|
$Data::Dump::DEBUG = bless \( my $x = \&maybe_apply_datadump_filter ), |
470
|
|
|
|
|
|
|
"Struct::Dumb::_DestroyWatch"; |
471
|
|
|
|
|
|
|
} |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
{ |
474
|
|
|
|
|
|
|
package Struct::Dumb::_DestroyWatch; |
475
|
|
|
|
|
|
|
my $GD = 0; |
476
|
6
|
|
|
6
|
|
5048
|
END { $GD = 1 } |
477
|
0
|
0
|
|
0
|
|
0
|
sub DESTROY { ${$_[0]}->() unless $GD; } |
|
0
|
|
|
|
|
0
|
|
478
|
|
|
|
|
|
|
} |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
0x55AA; |