| 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'!" |