line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package UID; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
############################################################################################################################### |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
=head1 NAME |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
B E<8212> Create unique identifier constants |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
=cut |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
############################################################################################################################### |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 VERSION |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
Version 0.24 (April 16, 2009) |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
=cut |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
our $VERSION="0.24"; |
22
|
|
|
|
|
|
|
|
23
|
1
|
|
|
1
|
|
24542
|
use strict; use warnings; use Carp; use utf8; |
|
1
|
|
|
1
|
|
2
|
|
|
1
|
|
|
1
|
|
37
|
|
|
1
|
|
|
1
|
|
5
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
25
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
76
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4
|
|
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
=head1 SYNOPSIS |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
use UID "foo"; # define a unique ID |
29
|
|
|
|
|
|
|
use UID BAR=>BAZ=>QUX=>; # define some more |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
print foo==foo; # true |
32
|
|
|
|
|
|
|
print foo==BAR; # false |
33
|
|
|
|
|
|
|
print foo=="foo"; # false |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
do_stuff(foo 42, BAR "bar", BAZ "foo"); |
36
|
|
|
|
|
|
|
# similar to do_stuff(foo=>42, BAR=>"bar", BAZ=>"foo") |
37
|
|
|
|
|
|
|
# except the UID foo can be unambiguously distinguished from the string "foo" |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
=head1 DESCRIPTION |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
The C module lets you declare unique identifiers E<8212> values that you can be sure will not be coincidentally matched by some other value. |
43
|
|
|
|
|
|
|
The values are not "universally" unique (UUIDs/GUIDs); they are unique for a single run of your program. |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
Define the identifiers with "C |
46
|
|
|
|
|
|
|
"C |
47
|
|
|
|
|
|
|
any value equal to I must be I itself (or a copy of it). No other UID (in the same process) will be equal to I. |
48
|
|
|
|
|
|
|
Cs can be compared to each other (or to other values) using either C<==> or C (or C or C, of course). |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
A typical use of C objects is to form a named pair (like C<< FOO=>42 >>), but note that the pair-comma (C<< => >>) implicitly |
51
|
|
|
|
|
|
|
quotes the preceding word, so C<< FOO=>42 >> really means C<< "FOO"=>42 >>, using the string C<"FOO"> rather than the UID C. |
52
|
|
|
|
|
|
|
However, a comma is not always needed; you can say simply C and often get the same effect as C. |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
=cut |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
#=========================================================================== |
60
|
|
|
|
|
|
|
# |
61
|
|
|
|
|
|
|
# UID |
62
|
|
|
|
|
|
|
# |
63
|
|
|
|
|
|
|
#=========================================================================== |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
#UIDs are scoped to the package that imports them; could assign "globals" by tracking them in this package (some hash to store the names or something?) Is this really useful??? (other than for polluting namespaces) |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
sub import |
69
|
|
|
|
|
|
|
# Create a new UID of the given name |
70
|
|
|
|
|
|
|
# |
71
|
|
|
|
|
|
|
# We take a list of names and make subs out of them (like "use constant") |
72
|
|
|
|
|
|
|
# The subs don't do anything; they just return a unique reference (object) -- |
73
|
|
|
|
|
|
|
# since the ref is anonymous, it will be unique (for this process) |
74
|
|
|
|
|
|
|
# Needs to happen at compile time, so Perl will parse the sub/uid names nicely |
75
|
|
|
|
|
|
|
# (our own "import" routine will export our identifiers at |
76
|
|
|
|
|
|
|
{ |
77
|
4
|
|
|
4
|
|
3123
|
my $class=shift; # first arg will always be the package name |
78
|
|
|
|
|
|
|
|
79
|
4
|
|
|
|
|
19
|
for (@_) # each name requested |
80
|
|
|
|
|
|
|
{ |
81
|
5
|
50
|
0
|
|
|
15
|
carp "WARNING: Ignoring UID '$_' because it is a ref/object, not a plain string" and next if ref $_; # UIDs can only be made out of plain strings (valid sub names) ###Should we allow this since the object will get stringified? or force the user to stringify it himself explicitly? |
82
|
5
|
50
|
0
|
|
|
66
|
carp "WARNING: Ignoring UID '$_' because that name is already being used" and next if caller()->can($_); # hm, should be able to override this if using "no warnings redefine"! |
83
|
|
|
|
|
|
|
#die rather than warn, unless the existing sub is also a UID?? |
84
|
|
|
|
|
|
|
|
85
|
5
|
|
|
|
|
13
|
my $name=(caller()."::$_"); # fully qualified name |
86
|
5
|
|
|
|
|
16
|
my $ID=bless [$_, $name], $class; # uniqueness: since the array-ref is lexically scoped here, we'll never get this exact ref any other way |
87
|
1
|
|
|
1
|
|
182
|
no strict 'refs'; # because we're going to declare the sub using a name built out of a string |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
153
|
|
88
|
5
|
100
|
|
55
|
|
27
|
*{$name}=sub {return $ID, @_ if wantarray; croak "ERROR: attempt to use args after UID $ID which is in scalar context (perhaps you need a comma after $ID?)" if @_; return $ID}; |
|
5
|
50
|
|
|
|
45
|
|
|
55
|
|
|
|
|
7410
|
|
|
48
|
|
|
|
|
105
|
|
|
48
|
|
|
|
|
203
|
|
89
|
|
|
|
|
|
|
# if called in array context, return any args as well (allows us to use "UID x, y" without an extra comma); |
90
|
|
|
|
|
|
|
# otherwise return just the UID ref itself; if we tried to pass args when being used in scalar context, |
91
|
|
|
|
|
|
|
# complain, because those args would effectively be lost (list in scalar context uses only the first item) |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
# We bless our refs so we can do some useful objecty stuff: |
96
|
1
|
|
|
1
|
|
7675
|
use overload q(""), sub { "«$_[0][0]»" }; # stringifying will return the name so we can usefully print out our IDs in error messages, etc. |
|
1
|
|
|
12
|
|
1125
|
|
|
1
|
|
|
|
|
9
|
|
|
12
|
|
|
|
|
8279
|
|
97
|
1
|
|
|
1
|
|
92
|
use overload '${}', sub {\ "«$_[0][1]»" }; # scalarly de-reffing also has the effect of stringifying, to return the fully qualified name |
|
1
|
|
|
2
|
|
2
|
|
|
1
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
21
|
|
98
|
|
|
|
|
|
|
|
99
|
19
|
100
|
|
19
|
0
|
876
|
sub compare { ref($_[0]) eq ref($_[1]) and overload::StrVal($_[0]) eq overload::StrVal($_[1]) }; # for comparing two UIDs (note that first we compare the class -- if they're different kinds of objects, they can't match; if they are, then we compare the actual "memory address" values of the underlying refs, which can only be the same if both sides are in fact the same UID |
100
|
1
|
|
|
1
|
|
119
|
use overload "==", \&compare; use overload "!=", sub {not &compare}; |
|
1
|
|
|
1
|
|
2
|
|
|
1
|
|
|
1
|
|
5
|
|
|
1
|
|
|
|
|
75
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
7
|
|
|
1
|
|
|
|
|
4
|
|
101
|
1
|
|
|
1
|
|
69
|
use overload "eq", \&compare; use overload "ne", sub {not &compare}; |
|
1
|
|
|
1
|
|
2
|
|
|
1
|
|
|
7
|
|
4
|
|
|
1
|
|
|
|
|
65
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
5
|
|
|
7
|
|
|
|
|
926
|
|
102
|
|
|
|
|
|
|
|
103
|
1
|
0
|
|
1
|
|
150
|
use overload nomethod=> sub { croak "ERROR: cannot use the '$_[3]' operator with a UID (in: ".($_[2]?"$_[1] $_[3] $_[0][0]":"$_[0][0]() $_[3] $_[1]").")" }; |
|
1
|
|
|
0
|
|
2
|
|
|
1
|
|
|
|
|
7
|
|
|
0
|
|
|
|
|
0
|
|
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
###TODO: add & or | for combining flags? |
106
|
|
|
|
|
|
|
###TODO: prolly should disallow redefining BEGIN, etc.! (plagiarise some more from use-constant) |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
=head1 EXAMPLES |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
Here is an example that uses UIDs for the names of named parameters. |
113
|
|
|
|
|
|
|
Let's suppose we have a function (C) that takes for its arguments a list of items to do stuff to, |
114
|
|
|
|
|
|
|
and an optional list of filenames to log its actions to. |
115
|
|
|
|
|
|
|
Using ordinary strings to name the groups of arguments would look something like this: |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
do_stuff(ITEMS=> $a, $b, $c, $d, $e, FILES=> $foo, $bar); |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
The function can go through all the args looking for our "ITEMS" and "FILES" keywords. |
120
|
|
|
|
|
|
|
However, if one of the items happened to be the string "FILES", the function would get confused. |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
We could do something such as make the arguments take the form of a hash of array-refs |
123
|
|
|
|
|
|
|
(a perfectly good solution, albeit one that requires more punctuation). |
124
|
|
|
|
|
|
|
Or we could use UIDs (which actually allows for slightly less punctuation): |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
use UID qw/ITEMS FILES/; |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
do_stuff(ITEMS $a, $b, $c, $d, $e, FILES $foo, $bar); |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
Now the function can check for the UID C unambiguously; no string or other object will match it. |
131
|
|
|
|
|
|
|
Of course, you can still use I where it doesn't make sense (e.g., saying C; |
132
|
|
|
|
|
|
|
but you can't make something else that is intended to be different but that accidentally turns out to be equal to I. |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
=head1 TECHNICALITIES |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
Cs work by defining a subroutine of the given name in the caller's namespace. |
138
|
|
|
|
|
|
|
The sub simply returns a UID object. |
139
|
|
|
|
|
|
|
Any arguments that you feed to this sub are returned as well, which is why you can say C without a comma to separate the terms; |
140
|
|
|
|
|
|
|
that expression simply returns the list C<(FOO, $bar)>. |
141
|
|
|
|
|
|
|
(However, beware of imposing list context where it's not wanted: C puts C<$bar> in list context, as opposed to C. |
142
|
|
|
|
|
|
|
Also, if you are passing UIDs as arguments to a function that has a prototype, a scalar prototype (C<$>) |
143
|
|
|
|
|
|
|
can force the UID to return only itself, and a subsequent arg will need to be separated with a comma.) |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
These subroutines work very much as do the constants you get from C |
146
|
|
|
|
|
|
|
Of course, this means that the names chosen must be valid symbols (actually, you can call things almost anything in Perl, |
147
|
|
|
|
|
|
|
if you're prepared to refer to them using circumlocutions like C<&{"a bizarre\nname"}>!). |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
A UID overloads stringification to return a value consisting of its name when used as a string |
150
|
|
|
|
|
|
|
(so C |
151
|
|
|
|
|
|
|
You can also treat it as a scalar-reference to get a string with the fully-qualified name |
152
|
|
|
|
|
|
|
(that is, including the name of the package in which it lives: C). |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
The comparison operators C<==> and C and their negations are also overloaded for UID objects: |
155
|
|
|
|
|
|
|
comparing a UID to anything will return false unless both sides are UIDs; |
156
|
|
|
|
|
|
|
and if both are, their blessed references are compared. |
157
|
|
|
|
|
|
|
(Not the values the references are referring to, which are simply the UIDs' names, but rather the string-values of the refs, |
158
|
|
|
|
|
|
|
which are based on their locations in memory E<8212> |
159
|
|
|
|
|
|
|
since different references will always have different values, this guarantees uniqueness.) |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=head1 ERROR MESSAGES |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
=over 1 |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
=item WARNING: Ignoring UID '$_' because it is a ref/object, not a plain string |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
You tried to make a UID out of something like an array-ref or an object. |
169
|
|
|
|
|
|
|
The module is looking for a string or strings that it can define in your namespace, and will skip over this arg. |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
=item WARNING: Ignoring UID '$_' because that name is already being used |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
A subroutine (or constant, or other UID, or anything else that really is also a sub) |
175
|
|
|
|
|
|
|
has already been declared with the given name. |
176
|
|
|
|
|
|
|
UID prevents you from redefining that name and skips over it. |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
=item ERROR: attempt to use args after UID $_ which is in scalar context (perhaps you need a comma after $_?) |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
You put (what appear to be) arguments after a UID, but the UID is in scalar context, thus only a single value can be used |
182
|
|
|
|
|
|
|
(not the UID plus its arguments). The solution is probably to put a comma after the UID, or strategically place some parentheses, |
183
|
|
|
|
|
|
|
to separate it from the following item, rather than letting it take that item as an argument. |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
=item ERROR: cannot use the '$_' operator with a UID |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
You tried to operate on a UID with an operator that doesn't apply (which is pretty much all of them). |
188
|
|
|
|
|
|
|
UIDs can be compared with C<==> or C, but you can't add, subtract, divide, xor them, etc. |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=back |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
=head1 BUGS & OTHER ANNOYANCES |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
No particular bugs are known at the moment. Please report any problems or other feedback |
196
|
|
|
|
|
|
|
to C<< >>, or through the web interface at L. |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
Note that UIDs are less useful for hash keys, because the keys have to be strings, not objects. |
199
|
|
|
|
|
|
|
You are able to use a UID as a key, but the stringified value (its name) will actually be used (and could conceivably |
200
|
|
|
|
|
|
|
be accidentally duplicated). |
201
|
|
|
|
|
|
|
However, there are modules that can give you hash-like behaviour while allowing objects as keys, |
202
|
|
|
|
|
|
|
such as L or L or L. |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
There are other places where Perl will want to interpret a UID (like any other sub name) as a string rather than as a function call. |
205
|
|
|
|
|
|
|
Sometimes you need to say things like C<+FOO> or C to make sure C is evaluated as a UID and not as a string literal. |
206
|
|
|
|
|
|
|
As mentioned, hash keys are one such situation; also C<< => >> implicitly quotes the preceding word. |
207
|
|
|
|
|
|
|
Note that C<&FOO> will work to force the sub interpretation, but is actually shorthand for C<&FOO(@_)>, |
208
|
|
|
|
|
|
|
i.e. it re-passes the caller's C<@_>, which is probably not what you want. |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
Comparing a UID to something else (C) will correctly return true only if the C<$something> is |
211
|
|
|
|
|
|
|
indeed (a copy of) the C object; but comparing something to a UID (C<$something==FOO>) could return an unexpected result. |
212
|
|
|
|
|
|
|
This is because of the way Perl works with overloaded operators: the value on the left gets to decide the meaning of C<==> (or C). |
213
|
|
|
|
|
|
|
Thus putting the UID first will check for UID-equality; if some other object comes first, it could manhandle the UID and compare, |
214
|
|
|
|
|
|
|
say, its string value instead. |
215
|
|
|
|
|
|
|
(It probably will work anyway, if the other code is well-behaved, but you should be aware of the possibility.) |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
=cut |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
### Example of tricky object that deliberately confounds our UIDs: |
220
|
|
|
|
|
|
|
# use UID foo; |
221
|
|
|
|
|
|
|
# use overload q(==), sub {${$_[0]} eq ${$_[1]}}; use overload fallback=>1; |
222
|
|
|
|
|
|
|
# my $x="«main::foo»"; my $o=bless \$x; |
223
|
|
|
|
|
|
|
# |
224
|
|
|
|
|
|
|
# print foo==$o?"Y":"N"; # uses UID's comparison, correctly says no |
225
|
|
|
|
|
|
|
# print $o==&foo ?"Y":"N"; # uses cheater's comparison, says yes! |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
=pod |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
While C is slightly cleaner than C or C<< FOO=>$stuff >> [which would be an auto-quoted bareword anyway], |
230
|
|
|
|
|
|
|
remember that C is actually implemented as a function call taking C<$a> and C<$b> as arguments; |
231
|
|
|
|
|
|
|
thus it imposes list context on them. Most of the time this doesn't matter, |
232
|
|
|
|
|
|
|
but if the item coming after a UID needs to be in scalar context, you may need to say something like C or C. |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
The user should have more control over the warnings and errors that C spits out. |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=head1 COLOPHONICS |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
Copyright 2007 David Green, C<< t cpan.org> >> |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
Thanks to Tom Phoenix and others who contributed to C |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
This module is free software; you may redistribute it or modify it under the same terms as Perl itself. See L. |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
=cut |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
AYPWIP: "I think so, Brain, but then my name would be 'Thumby'!" |