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, 2011-2012 -- leonerd@leonerd.org.uk |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
package Sentinel; |
7
|
|
|
|
|
|
|
|
8
|
8
|
|
|
8
|
|
487080
|
use strict; |
|
8
|
|
|
|
|
75
|
|
|
8
|
|
|
|
|
248
|
|
9
|
8
|
|
|
8
|
|
44
|
use warnings; |
|
8
|
|
|
|
|
16
|
|
|
8
|
|
|
|
|
328
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
our $VERSION = '0.06'; |
12
|
|
|
|
|
|
|
|
13
|
8
|
|
|
8
|
|
98
|
use Exporter 'import'; |
|
8
|
|
|
|
|
14
|
|
|
8
|
|
|
|
|
645
|
|
14
|
|
|
|
|
|
|
our @EXPORT = qw( sentinel ); |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
eval { |
17
|
|
|
|
|
|
|
require XSLoader; |
18
|
|
|
|
|
|
|
XSLoader::load( __PACKAGE__, $VERSION ); |
19
|
|
|
|
|
|
|
} or do { |
20
|
|
|
|
|
|
|
# pureperl fallback |
21
|
8
|
|
|
8
|
|
54
|
no warnings 'redefine'; |
|
8
|
|
|
|
|
18
|
|
|
8
|
|
|
|
|
1326
|
|
22
|
|
|
|
|
|
|
*sentinel = \&Sentinel::PP::sentinel; |
23
|
|
|
|
|
|
|
}; |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=head1 NAME |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
C - create lightweight SCALARs with get/set callbacks |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
=head1 SYNOPSIS |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
package Some::Class; |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
use Sentinel; |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
sub foo :lvalue |
36
|
|
|
|
|
|
|
{ |
37
|
|
|
|
|
|
|
my $self = shift; |
38
|
|
|
|
|
|
|
sentinel get => sub { return $self->get_foo }, |
39
|
|
|
|
|
|
|
set => sub { $self->set_foo( $_[0] ) }; |
40
|
|
|
|
|
|
|
} |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
sub bar :lvalue |
43
|
|
|
|
|
|
|
{ |
44
|
|
|
|
|
|
|
my $self = shift; |
45
|
|
|
|
|
|
|
sentinel value => $self->get_bar, |
46
|
|
|
|
|
|
|
set => sub { $self->set_bar( $_[0] ) }; |
47
|
|
|
|
|
|
|
} |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
sub splot :lvalue |
50
|
|
|
|
|
|
|
{ |
51
|
|
|
|
|
|
|
sentinel obj => shift, get => \&get_splot, set => \&set_splot; |
52
|
|
|
|
|
|
|
} |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
sub wibble :lvalue |
55
|
|
|
|
|
|
|
{ |
56
|
|
|
|
|
|
|
sentinel obj => shift, get => "get_wibble", set => "set_wibble"; |
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
=head1 DESCRIPTION |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
This module provides a single lvalue function, C, which yields a |
62
|
|
|
|
|
|
|
scalar that invoke callbacks to get or set its value. Primarily this is useful |
63
|
|
|
|
|
|
|
to create lvalue object accessors or other functions, to invoke actual code |
64
|
|
|
|
|
|
|
when a new value is set, rather than simply updating a scalar variable. |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
=cut |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
=head1 FUNCTIONS |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
=head2 sentinel |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
$scalar = sentinel %args |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
Returns (as an lvalue) a scalar with magic attached to it. This magic is used |
75
|
|
|
|
|
|
|
to get the value of the scalar, or to inform of a new value being set, by |
76
|
|
|
|
|
|
|
invoking callback functions supplied to the sentinel. Takes the following |
77
|
|
|
|
|
|
|
named arguments: |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
=over 8 |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=item get => CODE |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
A C reference or C method name to invoke when the value of the |
84
|
|
|
|
|
|
|
scalar is read, to obtain its value. The value returned from this code will |
85
|
|
|
|
|
|
|
appear as the value of the scalar. |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
=item set => CODE |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
A C reference or C method name to invoke when a new value for the |
90
|
|
|
|
|
|
|
scalar is written. The code will be passed the new value as its only argument. |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
=item value => SCALAR |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
If no C callback is provided, this value is given as the initial value of |
95
|
|
|
|
|
|
|
the scalar. If the scalar manages to survive longer than a single assignment, |
96
|
|
|
|
|
|
|
its value on read will retain the last value set to it. |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
=item obj => SCALAR |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
Optional value to pass as the first argument into the C and C |
101
|
|
|
|
|
|
|
callbacks. If this value is provided, then the C and C callbacks may |
102
|
|
|
|
|
|
|
be given as direct sub references to object methods, or simply method names, |
103
|
|
|
|
|
|
|
rather than closures that capture the referent object. This avoids the runtime |
104
|
|
|
|
|
|
|
overhead of creating lots of small one-use closures around the object. |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
=back |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
=head1 MUTATION ACCESSORS |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
A useful behaviour of this module is generation of mutation accessor methods |
111
|
|
|
|
|
|
|
that automatically wrap C/C accessor/mutator pairs: |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
foreach (qw( name address age height )) { |
114
|
|
|
|
|
|
|
my $name = $_; |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
no strict 'refs'; |
117
|
|
|
|
|
|
|
*$name = sub :lvalue { |
118
|
|
|
|
|
|
|
sentinel obj => shift, get => "get_$name", set => "set_$name"; |
119
|
|
|
|
|
|
|
}; |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
This is especially useful for methods whose values are simple strings or |
123
|
|
|
|
|
|
|
numbers, because they allow Perl's rich set of mutation operators to be |
124
|
|
|
|
|
|
|
applied to the object's values. |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
$obj->name =~ s/-/_/g; |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
substr( $obj->address, 100 ) = ""; |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
$obj->age++; |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
$obj->height /= 100; |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
=head1 XS vs PUREPERL |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
If an XS compiler is available at build time, this module is implemented using |
137
|
|
|
|
|
|
|
XS. If not, it falls back on an implementation using a Cd scalar. A |
138
|
|
|
|
|
|
|
pureperl installation can also be requested at build time by passing the |
139
|
|
|
|
|
|
|
C<--pp> argument to F: |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
$ perl Build.PL --pp |
142
|
|
|
|
|
|
|
$ ./Build |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
With thanks to C, C, and others from C for |
147
|
|
|
|
|
|
|
assisting with trickier bits of XS logic. Thanks to C for suggesting a |
148
|
|
|
|
|
|
|
pureperl implementation for XS-challenged systems. |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
=head1 AUTHOR |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
Paul Evans |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
=cut |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
package # Hide from CPAN |
157
|
|
|
|
|
|
|
Sentinel::PP; |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
sub sentinel :lvalue |
160
|
|
|
|
|
|
|
{ |
161
|
0
|
|
|
0
|
|
|
my %args = @_; |
162
|
0
|
|
|
|
|
|
tie my $scalar, "Sentinel::PP", $args{value}, $args{get}, $args{set}, $args{obj}; |
163
|
0
|
|
|
|
|
|
$scalar; |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
|
166
|
8
|
|
|
8
|
|
58
|
use constant { VALUE => 0, GET => 1, SET => 2, OBJ => 3 }; |
|
8
|
|
|
|
|
13
|
|
|
8
|
|
|
|
|
2887
|
|
167
|
|
|
|
|
|
|
sub TIESCALAR |
168
|
|
|
|
|
|
|
{ |
169
|
0
|
|
|
0
|
|
|
my $class = shift; |
170
|
0
|
|
|
|
|
|
bless [ @_ ], $class; |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
sub FETCH |
174
|
|
|
|
|
|
|
{ |
175
|
0
|
|
|
0
|
|
|
my $self = shift; |
176
|
0
|
|
|
|
|
|
my $get = $self->[GET]; |
177
|
0
|
|
|
|
|
|
my $obj = $self->[OBJ]; |
178
|
0
|
0
|
0
|
|
|
|
if( defined $get and !ref $get and defined $obj ) { |
|
|
0
|
0
|
|
|
|
|
179
|
|
|
|
|
|
|
# Method |
180
|
0
|
|
|
|
|
|
return $obj->$get; |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
elsif( defined $get ) { |
183
|
0
|
0
|
|
|
|
|
return $get->( defined $obj ? ( $obj ) : () ); |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
else { |
186
|
0
|
|
|
|
|
|
return $self->[VALUE]; |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
sub STORE |
191
|
|
|
|
|
|
|
{ |
192
|
0
|
|
|
0
|
|
|
my $self = shift; |
193
|
0
|
|
|
|
|
|
my ( $value ) = @_; |
194
|
0
|
|
|
|
|
|
my $set = $self->[SET]; |
195
|
0
|
|
|
|
|
|
my $obj = $self->[OBJ]; |
196
|
0
|
0
|
0
|
|
|
|
if( defined $set and !ref $set and defined $obj ) { |
|
|
0
|
0
|
|
|
|
|
197
|
|
|
|
|
|
|
# Method |
198
|
0
|
|
|
|
|
|
$obj->$set( $value ); |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
elsif( defined $set ) { |
201
|
0
|
0
|
|
|
|
|
$set->( defined $obj ? ( $obj ) : (), $value ); |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
|
204
|
0
|
|
|
|
|
|
$self->[VALUE] = $value; |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
0x55AA; |