line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Object::Hybrid;
|
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
#use 5.006;
|
4
|
|
|
|
|
|
|
|
5
|
1
|
|
|
1
|
|
16440
|
use strict qw[vars subs];
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
198
|
|
6
|
|
|
|
|
|
|
$Object::Hybrid::VERSION = '0.03_5';
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=head1 NAME
|
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
Object::Hybrid - promote Perl primitives (hashes, scalars, arrays, and filehandles), either tie()d or not, to become hybrid objects
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=head1 WARNING
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
Any specific interface that Object::Hybrid exposes may change (as it already did) until version 1.0 is reached.
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 SYNOPSIS
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
Here (and everywhere in this documentation) notion of "primitive" refers to hash, scalar, array, or filehandle (i.e. perltie types), either tie()d or non-tie()d, bless()ed or non-bless()ed.
|
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
Promote $primitive to become hybrid object:
|
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
use Object::Hybrid qw(promote); # declare promote() for use
|
23
|
|
|
|
|
|
|
promote $primitive;
|
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
Or (especially if you prefer to not export anything) use new() constructor...
|
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
use Object::Hybrid;
|
28
|
|
|
|
|
|
|
$hybrid = new Object::Hybrid $primitive; # $primitive becomes hybrid object
|
29
|
|
|
|
|
|
|
$hybrid = Object::Hybrid->new($primitive); # same
|
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
NOTE: tie()d primitive must be tie()d before promote(). If it needs to be tie()d later, the mutable_class => 1 argument to promote() should be used (see L"promote() function">).
|
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
After that $primitive remains the same, but it is now bless()ed as object that exposes corresponding perltie methods, so that the following, for example, become interchangeable for B %$primitive:
|
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
$primitive->{foo};
|
36
|
|
|
|
|
|
|
$primitive->FETCH('foo'); # same
|
37
|
|
|
|
|
|
|
$primitive->fetch('foo'); # same
|
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
Also, in case of tie()d primitive instead of:
|
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
tied(%$primitive)->method();
|
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
just be tied()less:
|
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
$primitive->method();
|
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
The later call will also not fail due to "can't find method" if method() is not defined by tied(%$primitive), becoming no-op that returns empty list or undef() scalar, making it easier to write code portable accross multiple different tied(%$primitive) classes as well as non-tie()d primitives. In contrast, $primitive->METHOD() aliase call is not similarly "fail-safe" and will raise an exception if neither FETCH() nor fetch() are defined.
|
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
In case non-tied() primitives need to be interchangeable with tied() ones that have extended tied() interface, instead of cumbersome (possibly repeating many times) tied()-conditional access expression:
|
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
tied(%$primitive) ?
|
52
|
|
|
|
|
|
|
tied(%$primitive)->FETCH('foo', @args)
|
53
|
|
|
|
|
|
|
: $primitive->{foo};
|
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
just:
|
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
$primitive->FETCH('foo', @args);
|
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
or faster:
|
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
$primitive->fast->FETCH('foo', @args);
|
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
However, for non-tie()d primitives the above tied()-conditional switch expression may still be significantly faster, so that it is still preferred for hot paths (tight loops, etc.). For speed and other tradeoffs involved see L"Performance"> and L"fast()"> sections.
|
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
If $FH is a plain filehandle or tiehandle tied to class that implements stat(), ftest() (and others) and self() method, then the following simple code need not to discriminate between plain filehandle and tiehandle:
|
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
promote $FH;
|
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
$FH->stat();
|
70
|
|
|
|
|
|
|
$FH->ftest('-X');
|
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
# same in indirect method notation:
|
73
|
|
|
|
|
|
|
STAT $FH;
|
74
|
|
|
|
|
|
|
FTEST $FH '-X';
|
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
# or sometimes self() method can be used for that end too:
|
77
|
|
|
|
|
|
|
stat $FH->self;
|
78
|
|
|
|
|
|
|
-X $FH->self;
|
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
=head1 DESCRIPTION
|
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
Some applications need to accept both plain primitives as well as tie()d primitives with additional extended interface available through tied() object. For example, application cache may be allowed to optionally use either screamingly fast plain hash or some highly evolved persistent hashes tie()d to disk storage (like DB_File, etc.). There are many similar examples for filehandles, arrays and even scalars. Those are cases when Object::Hybrid combined with simple coding style can make code that handles those primitives compatible across whole spectrum, from plain primitives to all types of extended tied() primitives. There are also other uses, including working around gaps in tiehandle implementation, adding some fancy operations to primitives without using tie() as well as plain syntactic sugar.
|
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
In the context of this module hybrid object is defined as a Perl object that represents its own bless()ed primitive (i.e. the primitive it is implemented with, currently hash, scalar, array, or filehandle). According to this definition, hybrid object can be seen as both primitive and object at the same time. In general case, it is a violation of object encapsulation to access object's underlying bless()ed primitive directly (at least outside of class's methods), but in special case of hybrid objects it is perfectly ok to do so - no violation of encapsulation takes place.
|
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
Hybrid objects are instances of the class that is referred to as "hybrid class". This module implements default hybrid class and exports promote() function that bless()es Perl's primitives (hash, scalar, array, or filehandle) into either default or user-specified (custom) hybrid class to make them hybrid objects.
|
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
Promoting primitive to become hybrid (i.e. bless()ing it into hybrid class) simply adds object interface to primitive and is a way to extend Perl primitives that is compatible with and complementary to another major way of extending primitives - perltie API.
|
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
Specifically, advantages of promote()ing primitives are:
|
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
=over
|
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
=item Compatibility
|
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
Hybrid object has corresponding perltie methods interface for accessing underlying bless()ed primitive that object is implemented with (e.g. tiehash methods for accessing underlying bless()ed hash, etc.). Moreover, for hybrid object the following, for example, are equivalent and interchangeable for B primitives:
|
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
$hybrid->{foo};
|
99
|
|
|
|
|
|
|
$hybrid->FETCH('foo'); # same
|
100
|
|
|
|
|
|
|
$hybrid->fetch('foo'); # same
|
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
Promoting primitives to become hybrids allows the same simple portable, non-specific code to manipulate (be compatible with) anything ranging from plain primitives to highly extended tie()d primitives as it unifies their interfaces and make them interchangeable.
|
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
For example, if same code is required to accept and handle both plain hashes, i.e. fast, in-memory hashes, and tie()d hashes with extended perltie interface, e.g. slow persistent hashes tie()d to disk storage, then it is useful to promote() each of those hashes to become hybrid. Whenever plain access is required the following code:
|
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
$hybrid->{foo};
|
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
will work for both in-memory and persistent hashes, and is really fast in case of in-memory hash. And in case you need to use extended interface, something like next code will also work for promote()d both in-memory and persistent hashes:
|
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
$hybrid->FETCH('foo', @args);
|
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
$hybrid->can('custom_method')
|
113
|
|
|
|
|
|
|
and $hybrid->custom_method();
|
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
For performnce comparison of various interface options see L"Performance"> section.
|
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
Despite promoting primitives to become hybrids turn them into Perl objects, compatibility with arbitrary Perl objects in practice has little value, since code that manipulates objects usually assume objects to be of very specific class.
|
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
=item tied()less access
|
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
Accessing tied() interface of tie()d primitive no longer requires cumbersome (possibly conditional) tied() call, i.e. instead of:
|
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
tied(%$hybrid)->method();
|
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
one can write:
|
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
$hybrid->method(); # same
|
128
|
|
|
|
|
|
|
$hybrid->fast->method(); # same, but may be faster
|
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
For performnce comparison of various interface options see L"Performance"> and L"fast()"> sections.
|
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
=item Incomplete tie() implementation workaround
|
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
Currently tie()ing filehandles is still incomplete in Perl: sysopen(), truncate(), flock(), fcntl(), stat() and -X cannot currently be trapped. However, both tieclasses and hybrid classes can define corresponding methods or use self() method (see L"Properties of hybrid objects">) to not distinguish between primitives for that matter:
|
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
promote $FH;
|
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
$FH->stat();
|
139
|
|
|
|
|
|
|
$FH->ftest('-X');
|
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
# same in indirect method notation:
|
142
|
|
|
|
|
|
|
STAT $FH;
|
143
|
|
|
|
|
|
|
FTEST $FH '-X';
|
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
# or sometimes self() method can be used for that end too:
|
146
|
|
|
|
|
|
|
stat $FH->self;
|
147
|
|
|
|
|
|
|
-X $FH->self;
|
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=item Operator overloading
|
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
Custom hybrid classes can be used for overloading operators on primitives. However, unfortunately such hybrid classes currently can only be used to promote() non-tied() primitives (see L"Operator overloading">).
|
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=back
|
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
Object::Hybrid is a lightweight pure Perl module with no dependencies beyond core.
|
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
=head1 Stop reading now (or how to use this documentation)
|
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
Usually, there is no need to read any of the following documentation to use Object::Hybrid - you can stop reading at this point. What you have read so far, or even just self-explanatory SYNOPSIS, is enough in most cases. The following documentation covers optional features that need not to be learned for using Object::Hybrid in most usual case (e.g. occasionally).
|
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=head1 C |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
use Object::Hybrid; # exports nothing
|
164
|
|
|
|
|
|
|
use Object::Hybrid $feature; # enables single named feature
|
165
|
|
|
|
|
|
|
use Object::Hybrid %options; # most general form
|
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
The following features are supported:
|
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
use Object::Hybrid 'promote';
|
170
|
|
|
|
|
|
|
use Object::Hybrid feature => 'promote'; # same
|
171
|
|
|
|
|
|
|
use Object::Hybrid feature => ['promote']; # same
|
172
|
|
|
|
|
|
|
use Object::Hybrid export => 'promote'; # same
|
173
|
|
|
|
|
|
|
use Object::Hybrid export => ['promote']; # same
|
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
which exports (i.e. declares for use) the promote() function into caller's namespace.
|
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
Next features depend on autobox pragma being installed (can be installed from CPAN archive):
|
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
use Object::Hybrid 'autobox';
|
180
|
|
|
|
|
|
|
use Object::Hybrid feature => 'autobox'; # same
|
181
|
|
|
|
|
|
|
use Object::Hybrid feature => ['autobox']; # same
|
182
|
|
|
|
|
|
|
use Object::Hybrid autobox => Object::Hybrid->CLASS; # same, but can be custom hybrid class
|
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
which will automatically promote() any primitive within the current scope, and "unpromote" them back beyond that scope. It is is equivalent to:
|
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
use Object::Hybrid;
|
187
|
|
|
|
|
|
|
use autobox
|
188
|
|
|
|
|
|
|
HASH => Object::Hybrid->CLASS,
|
189
|
|
|
|
|
|
|
SCALAR => Object::Hybrid->CLASS,
|
190
|
|
|
|
|
|
|
ARRAY => Object::Hybrid->CLASS; # can be custom hybrid class instead
|
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
And closely related is:
|
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
use Object::Hybrid 'autopromote';
|
195
|
|
|
|
|
|
|
use Object::Hybrid feature => 'autopromote'; # same
|
196
|
|
|
|
|
|
|
use Object::Hybrid feature => ['autopromote']; # same
|
197
|
|
|
|
|
|
|
use Object::Hybrid autopromote => Object::Hybrid->CLASS; # same, but can be custom hybrid class
|
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
which makes any method call on primitive in the lexical scope to automatically promote() that primitive.
|
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
=head1 promote() function
|
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
promote $primitive; # bless() to make $primitive a hybrid
|
204
|
|
|
|
|
|
|
promote $primitive => \%args; # same, but with named arguments
|
205
|
|
|
|
|
|
|
promote $primitive => %args; # same
|
206
|
|
|
|
|
|
|
promote $primitive => $class; # same, but with explicit $class to tie() to or bless() into
|
207
|
|
|
|
|
|
|
promote $primitive => $class, \%args; # same, but with named arguments
|
208
|
|
|
|
|
|
|
promote $primitive => $class, %args; # same
|
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
In case $primitive is (or will later be) tied(), the tied() object is used as object interface of the hybrid (see L"Delegation to tied() object">), unless custom hybrid $class is specified (see L"Custom hybrid $class">).
|
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
In any case, promote() never (re)tie()s primitive, only bless()es it.
|
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
If $primitive specified is of type not currently supported by Object::Hybrid, exception is raised. If not defined($primitive) or no $primitive is specified, then exception is raised unless custom $class is specified (see L"Custom hybrid $class">).
|
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
The return value is a hybrid object, i.e. $primitive itself (may be useful if promote() is used in expressions).
|
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
The class that primitive is bless()ed into by promote() is generated based on the type of primitive, whether it is tied(), and using custom $class, if any is specified. User should not assume anything about that resulting ref($primitive) class, except:
|
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
Object::Hybrid->Class->is(ref($primitive));
|
221
|
|
|
|
|
|
|
ref($primitive)->isa($class); # in case $class was specified
|
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
If promote() is called on already bless()ed $primitive, i.e. on object, it is equivalent to as if promote() was called on non-bless()ed $primitive with ref($primitive) passed as $class:
|
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
promote bless $primitive => $class;
|
226
|
|
|
|
|
|
|
promote $primitive => $class; # same
|
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
The "mutable_class" option commands whether the hybrid class used for specific primitive is mutable or not. The "mutable_class" => 1 values makes hybrid class mutable, allowing tie() on the primitive to happen after promote(). If hybrid gets tied()d or untie()d, its object interface immediately changes accordingly. Otherwise in case of immutable class, if tie() is called after promote(), perltie methods of tied() class cannot be called on hybrid. If "mutable_class" is undef(), mutable class is used for tied() primitives, immutable otherwise. The explicit "mutable_class" => 0 value makes hybrid class immutable - in theory this allows to use hybrid object interface different from tied() interface, which may be useful in some special cases, but may also violate hybrid equivalence requirement in case of tie()d primitives. Using this option leads to promote() bless()ing primitive into universal Object::Hybrid::CLASS instead of type-specific hybrid class, and this hurts performance very significantly. The semantics of this option is same for custom hybrid class, except in addition "mutable_class" => 1 should be set when custom class defines perltie methods.
|
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
Note that mutable class significantly reduce performance in case of non-tied primitives.
|
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
=head2 Custom hybrid $class
|
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
If custom hybrid $class is specified and either $primitive is not tied() or not_tied => 1 option is given, then $primitive is bless()ed to hybrid class that inherits from $class.
|
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
If however, C<< Object::Hybrid::Class->is($class) >> is true (means $class is the complete hybrid class implementation, then .$primitive is simply bless()ed into $class.
|
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
Custom $class must be type-specific for given $primitive, so the type-conditional expression for $class may need to be used by caller.
|
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
If custom hybrid $class is specified without $primitive or with not defined($primitive), then $primitive of the type expected by $class is autovivified. If $class do not allow to determine what $primitive type it expects, then exception is raised.
|
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
=head1 Properties of hybrid objects
|
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
The following are the properties of hybrid objects:
|
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
=head2 Equivalent perltie API
|
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
Hybrid object exposes perltie interface (perltie methods) for its underlying bless()ed primitive B that interface is equivalent to directly accessing underlying primitive - B tie()d and not tie()d. This interface equivalence is what makes, say, next two lines to have exactly same effect for hash hybrids:
|
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
$hybrid->{foo};
|
251
|
|
|
|
|
|
|
$hybrid->FETCH('foo'); # same
|
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
For performance comparison of various interface options see L"Performance"> section.
|
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
=head2 Complete perltie API
|
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
Currently tie()ing filehandles is still incomplete in Perl: sysopen(), truncate(), flock(), fcntl(), stat() and -X can't currently be trapped. However, hybrid object provides equivalent samename methods and their uppercase aliases (SYSOPEN(), TRUNCATE(), FLOCK(), FCNTL(), STAT() and FTEST()) to fill the gap and also allow compatibility with tiehandle classes that also implement them. This allows to write portable code that works around gaps in tiehandle implementation by B using methods on hybrids filehandles instead of Perl built-in functions, for example:
|
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
promote $FH;
|
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
$FH->stat();
|
262
|
|
|
|
|
|
|
$FH->ftest('-X');
|
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
# same with indirect method calls...
|
265
|
|
|
|
|
|
|
STAT $FH;
|
266
|
|
|
|
|
|
|
FTEST $FH '-X';
|
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
Thus, to avoid problems with gaps in tiehandle implementation simply always call methods on hybrids instead of Perl built-in functions.
|
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
=head2 Delegation to tied() object
|
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
In case of tied() primitives, hybrid object tries to delegate all method calls to tied() object. Delegation invokes called method on tied() object instead of hybrid. This allows invoking perltie methods B on hybrid directly (instead of on tied() object):
|
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
$hybrid->STORE($value, @args);
|
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
Delegation exposes entire interface of tied() object as hybrid object interface. Caller may use $hybrid->can('foo') to determine whether tied() object (if any) or hybrid implement certain methods.
|
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
If tied() object provides no called method, delegation fails and then hybrid object falls back to calling its own samename method (called "fallback method") on hybrid itself. Fallback methods are simply same methods that would be called if hybrid is not tied(). This means methods of the non-tied() hybrid remain available if hybrid is tied(), unless tied() object exposes its own samename methods.
|
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
In other words, tie()d hybrid may expose methods that tied() object do not provide. For example, since stat() is not a perltie method currently, it is unlikely to be implemented by tiehandle class, but it is provided by hybrid object to have portable C<< promote($tied_fh)->stat() >> workarounds for currently broken C.
|
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
Note that delegation for standard perltie methods almost always works (no fallback), because normally tieclass does implement perltie methods.
|
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
=head2 Method aliases
|
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
Hybrid object provides altered-case aliases for all its methods (including lowercased aliases for all perltie methods).
|
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
This feature is especially relevant in case when there are samename built-in functions for accessing primitives: shift(), exists(), seek(), etc. In this case and as general coding style for hybrids: the lower case should be used as functions or in direct method calls notation, while upper case can be used for indirect method call notation (later minimizes chances of indirect method notation colliding with non-parenthesized same name function calls with single scalar argument, like C). For example:
|
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
seek $FH, 0, 0; # function call (coma after $FH, no extra arguments)
|
291
|
|
|
|
|
|
|
SEEK $FH 0, 0, @args; # indirect method call (no coma after $FH, @args extended interface)
|
292
|
|
|
|
|
|
|
$FH->seek(0, 0, @args); # direct method call (@args extended interface)
|
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
In case of tie()d hybrid it is more efficient to call method that is defined by underlying tied() class, for example, FETCH() is likely to be faster than fetch() for tie()d primitives (as their tied() classes usually define no fetch(), just FETCH()). In all other cases aliases are equally efficient.
|
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
Setting global C<$Object::Hybrid::Portable = 1> (usually local()ized to some block) changes behavior of aliases making them non-equivalent. Calls of lower-case aliases now do not fail (are "fail-safe") due to method being not defined by either hybrid class or underlying tied() class (if any), becoming no-op and returning empty list or undef scalar. This allows to write portable code that calls non-standard methods on tied() hybrids without (ab)using can() calls or eval{} wraps, which otherwise would make code cumbersome (e.g. in case of eval{} it is necessary to manually distinguish "can't find method" from errors in defined method, etc.). But it is of course risky too, as typos will not blow up, leading to silent error propagation, so that $Object::Hybrid::Portable = 1 should be used with care, after first testing code without it.
|
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
In contrast, upper-case aliases are not similarly fail-safe under C<$Object::Hybrid::Portable = 1>, calling them is a fatal error if method, both lower-case and upper-case, is not defined, so that they can be used to ensure method is really called (mnemonics: insist on it being called by writing it in upper case). If, however, lower-case method is defined, the upper-case call will call it, not fail. For example:
|
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
{
|
301
|
|
|
|
|
|
|
local $Object::Hybrid::Portable = 1;
|
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
$hybrid->non_existing_method(); # will not fail due to "can't find method"
|
304
|
|
|
|
|
|
|
$hybrid->NON_EXISTING_METHOD(); # fatal "can't find method" error
|
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
$hybrid->maybe_existing_method(); # will not fail due to "can't find method"
|
307
|
|
|
|
|
|
|
$hybrid->MAYBE_EXISTING_METHOD(); # may be a fatal "can't find method" error
|
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
$filehandle_hybrid->fetch(); # will not fail due to "can't find method"
|
310
|
|
|
|
|
|
|
$filehandle_hybrid->FETCH(); # likely fatal "can't find method" error (since filehandles normally have no FETCH()), but will call fetch() (not fail) if fetch() happens to be defined
|
311
|
|
|
|
|
|
|
}
|
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
=head2 call()
|
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
$hybrid->call(method => @args);
|
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
is the short form of:
|
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
{
|
320
|
|
|
|
|
|
|
local $Object::Hybrid::Portable = 1;
|
321
|
|
|
|
|
|
|
$hybrid->method(@args);
|
322
|
|
|
|
|
|
|
}
|
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
Except in case of call() caller() within &$method is one level deeper in the stack (which may be unexpected by methods that use caller()) and character case of method()'s name is irrelevant.
|
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
=head2 fast()
|
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
The fast() efficiently returns tied() object for tie()d invocant and invocant itself for non-tied. The fast() method is used for "manual" delegation to tied() object as a way of performance optimization:
|
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
$hybrid->fast->FETCH('a'); # for tied() $hybrid is much faster than...
|
331
|
|
|
|
|
|
|
$hybrid->FETCH('a');
|
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
$hybrid->fast->can('foo'); # for tied() $hybrid is much faster than...
|
334
|
|
|
|
|
|
|
$hybrid->can('foo');
|
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
For non-tied hybrids, however, situation is reversed, but in absolute terms using fast() often pays off, especially where tied hybrids are more common throughput. The trade-off however is that $hybrid->fast->FETCH() syntax provides no method aliases, no fail-safety for non-defined methods in case of true $Object::Hybrid::Portable, and raises exception instead of falling back to calling samename hybrid's method in case tied() class defines no called method, so that using it is more risky and requires better knowledge of tied() classes involved.
|
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
=head2 self() method
|
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
The self() method returns underlying primitive: either bless()ed primitive of the hybrid object (i.e. hybrid object itself) or, if possible, real underlying primitive that is used by tied() object/class.
|
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
Many tied() objects (like Tie::ExtraHash) transparently delegate operations on tie()d primitive to real primitive encapsulated somewhere inside that tied() object, using object just to store some additional state. If this is the case, tied() class may define self() as accessor for that underlying primitive to expose it to methods of the hybrid class and to users. The self() method allows to access that real primitive directly, falling back to hybrid's bless() primitive, if it is not possible or tied() class do not provide self() method. As a result, methods of custom hybrid class can have access to both tie()d bless()ed primitive (slow) and underlying real primitive (fast).
|
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
The tied() class must not define self() if this may result in violation of encapsulation, i.e. if delegation to underlying real primitive is not transparent enough. Example of transparent tieclass that may define self() is Tie::ExtraHash. On the other hand some tieclasses, like Tie::StdHash, are so transparent that need not to define self() at all as default one is good for them.
|
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
The self() method can have a number of useful applications, in particular to work around gaps in tiehandle implementation and to increase performance, as it allows hybrid methods to quickly bypass perltie layer and operate on underlying primitive directly, which may bring significant efficiency benefits, especially for some bulk operations.
|
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
For example, since there is no yet perltie support for stat() and -X tests, called on tiehandle they do not propagate to underlying real filehandle, so they should be somehow propagated manually, but it requires knowing how to get underlying filehandle, if there is any, out of tied() object. Defining self() method in tieclass is supposed to do just that, and hybrid classes are expected to define self() method as well, so that portable code (assuming tied() classes define self()) can simply be:
|
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
promote $FH;
|
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
stat $FH->self;
|
353
|
|
|
|
|
|
|
-X $FH->self;
|
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
# or nearly same using methods (default implementations of these methods also use self() under the hood):
|
356
|
|
|
|
|
|
|
STAT $FH;
|
357
|
|
|
|
|
|
|
FTEST $FH '-X';
|
358
|
|
|
|
|
|
|
$FH->stat();
|
359
|
|
|
|
|
|
|
$FH->ftest('-X');
|
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
If tieclass defines self(), the sysopen(), truncate(), flock(), fcntl(), stat() and ftest() methods of corresponding tie()d hybrid object will operate correctly without tieclass implementing them.
|
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
The Hybrid::Object makes no use of self() only it it is defined by tieclass of tie()d primitive, so that hybrids do not depend on tieclass implementing self() method.
|
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
=head2 Optional bless() method
|
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
If custom hybrid class defines bless() method, the C<< $hybrid_class->bless($primitive) >> is called instead of otherwise calling C. The bless() method is optional. It can be used as constructor/initializer for hybrid object, except it is to reuse $primitive (same as built-in bless()) instead of creating new.
|
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
=head2 C<< Object::Hybrid->Class >>
|
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
Hybrid objects can be recognized with the following test:
|
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
Object::Hybrid->Class->is($hybrid);
|
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
=head1 new() method
|
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
$hybrid = new Object::Hybrid $primitive; # bless() to make $primitive a hybrid
|
378
|
|
|
|
|
|
|
$hybrid = new Object::Hybrid $primitive => \%args; # same, but with named arguments
|
379
|
|
|
|
|
|
|
$hybrid = new Object::Hybrid $primitive => %args; # same
|
380
|
|
|
|
|
|
|
$hybrid = new Object::Hybrid $primitive => $class; # same, but with explicit $class to tie() to or bless() into
|
381
|
|
|
|
|
|
|
$hybrid = new Object::Hybrid $primitive => $class, \%args; # same, but with named arguments
|
382
|
|
|
|
|
|
|
$hybrid = new Object::Hybrid $primitive => $class, %args; # same
|
383
|
|
|
|
|
|
|
$hybrid = new Object::Hybrid $class; # same, but $hybrid is constructed for a given class
|
384
|
|
|
|
|
|
|
$hybrid = new Object::Hybrid $class, \%args; # same, but with named arguments
|
385
|
|
|
|
|
|
|
$hybrid = new Object::Hybrid $class, %args; # same
|
386
|
|
|
|
|
|
|
$hybrid = new Object::Hybrid \%args; # same, but with named arguments
|
387
|
|
|
|
|
|
|
$hybrid = new Object::Hybrid %args; # same
|
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
Or corresponding direct method call notation for any of the above can be used, for example:
|
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
$hybrid = Object::Hybrid->new($primitive); # etc.
|
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
The new() constructor promote()s $primitive to hybrid and returns it. It is roughly equivalent to:
|
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
sub new { shift; return promote(@_) }
|
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
Refer to promote() documentation.
|
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
Note that new() do not construct object of Object::Hybrid class, even not $hybrid->isa('Object::Hybrid'), so beware.
|
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
=head1 tie() method
|
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
$tied = Object::Hybrid->tie( $primitive, $tieclass, @args); # for %$primitive same as...
|
404
|
|
|
|
|
|
|
$tied = tie(%$primitive, $tieclass, @args); # ... except $primitive also gets promote()d to hybrid
|
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
=head1 Class() method
|
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
Object::Hybrid relies on Class::Tag to store and query inheritable meta-data of hybrid classes, and uses Object::Hybrid::Class as tagger class in Class::Tag's terms. The Class() method always returns the name of that tagger class. This is primarily for convenience of using it in expressions. Refer to L for currently supported tags and their semantics.
|
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
package Foo;
|
411
|
|
|
|
|
|
|
use Object::Hybrid::Class; # tags Foo as standalone hybrid class
|
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
package Bar;
|
414
|
|
|
|
|
|
|
use Object::Hybrid::Class 'mutable_class'; # tags Bar as mutable hybrid class
|
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
Object::Hybrid->Class eq 'Object::Hybrid::Class'; # true
|
417
|
|
|
|
|
|
|
Object::Hybrid->Class->is( 'Foo'); # true
|
418
|
|
|
|
|
|
|
Object::Hybrid->Class->mutable_class('Foo'); # false
|
419
|
|
|
|
|
|
|
Object::Hybrid->Class->mutable_class('Bar'); # true
|
420
|
|
|
|
|
|
|
Object::Hybrid->Class->is( 'Bar'); # false
|
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
=head1 is() method
|
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
promote $hybrid;
|
425
|
|
|
|
|
|
|
Object::Hybrid->is( $hybrid); # true
|
426
|
|
|
|
|
|
|
Object::Hybrid->Class->is( $hybrid); # same
|
427
|
|
|
|
|
|
|
Object::Hybrid->is( $not_hybrid); # false
|
428
|
|
|
|
|
|
|
Object::Hybrid->Class->is($not_hybrid); # same
|
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
=head1 ref_*() methods
|
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
These are utility methods useful to sort things out. Generally, all ref_foo() (ref_*()) methods return boolean that tells whether its argument is reference or not, but exact boolean value depends of the value of 'foo' suffix:
|
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
=head2 ref_type() method
|
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
Object::Hybrid->ref_type({}) eq 'HASH'; # true
|
437
|
|
|
|
|
|
|
Object::Hybrid->ref_type(bless {}, 'Foo') eq 'HASH'; # true
|
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
and so on...
|
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
=head2 ref_isa() method
|
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
$obj = bless {}, 'Foo';
|
444
|
|
|
|
|
|
|
Object::Hybrid->ref_isa($obj); # true
|
445
|
|
|
|
|
|
|
Object::Hybrid->ref_isa($obj) eq $obj; # true
|
446
|
|
|
|
|
|
|
Object::Hybrid->ref_isa($obj, 'Foo') eq $obj; # true
|
447
|
|
|
|
|
|
|
Object::Hybrid->ref_isa($obj, 'Bar'); # false
|
448
|
|
|
|
|
|
|
Object::Hybrid->ref_isa({}); # false
|
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
and so on...
|
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
This method is useful to try some unknown $thing at hands that it is too uncertain to call $thing->isa('Foo') on it. It returns true, more specifically passes through its argument (for use in expressions and chained calls) if reference is blessed and, if second argument defined, isa() of second argument type (exact type can be obtained with ref(ref_isa($var)) instead). Otherwise returns false. More specifically, returns 0 for blessed references, '' for non-blessed references and undef for non-references.
|
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
=head2 ref_tied() method
|
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
$tied = tie %$primitive, 'Foo';
|
457
|
|
|
|
|
|
|
Object::Hybrid->ref_tied($primitive) eq $tied; # true
|
458
|
|
|
|
|
|
|
Object::Hybrid->ref_tied({}) eq '0'; # true
|
459
|
|
|
|
|
|
|
Object::Hybrid->ref_tied(sub{}) eq ''; # true, since sub{} is not tie()able
|
460
|
|
|
|
|
|
|
Object::Hybrid->ref_tied('string') eq ''; # true, since 'string' is not tie()able
|
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
and so on...
|
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
=head1 Subclassing Object::Hybrid
|
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
Subclassing Object::Hybrid and overriding new() in the subclass will automatically override promote() exported by that subclass, so there is no need to explicitly redefine promote() in subclass.
|
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
=head1 Perltie classes
|
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
Hybrid objects are out of the box compatible with any valid tieclass.
|
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
However, to support workarounds for "gaps" in perltie implementation, tieclasses may need to meet additional requirements - those are some of requirements that hybrid classes already comply with, intended specifically to work around perltie implementation gaps, namely: L"Complete perltie API"> and L"self() method">. Currently tie()ing filehandles is still incomplete in Perl, so these requirements mainly apply to tiehandle classes. The most simple tiehandle classes, like Tie::StdHandle (loaded by "use Tie::Handle"), already comply with this requirements, as for them default self() and other methods provided by default hybrid class are good enough. A bit more complex tiehandle classes need just to implement self() method. If defining self() is not possible in case of more complex tiehandle classes, additional SYSOPEN(), TRUNCATE(), FLOCK(), FCNTL(), STAT() and FTEST() methods may need to be implemented as workarounds by tiehandle class.
|
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
Since tie() do not pass primitive to be tie()d to TIE*() constructor, TIE*() cannot be made to optionally promote() that primitive. Instead, tieclass can expose promote() as one of its methods allowing user to promote primitives or expose Object::Hybrid->tie() method analog for built-in tie() that both tie()s and promote() given primitive. This, however, should probably not be dove via subclassing.
|
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
=head1 Operator overloading
|
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
Custom hybrid classes can be used for overloading operators on promote()d primitives. However, unfortunately hybrid classes with overloaded operators currently can only be used to promote() non-tied() primitives. This is because currently overload pragma is broken - bless()ing tie()d primitive into such class will implicitly untie() it. Should this be fixed in the future, operator overloading can be used without this limitation.
|
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
However, even used on non-tied() primitives operator overloading is very powerful and can have some interesting (and possibly even useful) applications. In particular, overloading of dereference operators allows to achieve effects somewhat similar to using tie(), like "back-door" state similar to that of tied() objects. It is even possible to have "hybrid primitive" that is simultaneously hash, scalar, array, subroutine and glob (however such hybrid class may violate equivalence requirement as FETCH(0) need to be equivalent to $hybrid->{0} and $hybrid->[0] at the same time).
|
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
=head1 Performance
|
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
The performance preferences for hash hybrids are (in order from fastest to slowest):
|
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
$non_blessed->{foo} # ~ 1_700_000/s
|
487
|
|
|
|
|
|
|
$nontied_hybrid->{foo}; # ~ 1_700_000/s
|
488
|
|
|
|
|
|
|
$nontied_hybrid->FETCH('foo'); # ~ 300_000/s
|
489
|
|
|
|
|
|
|
$nontied_hybrid->fast->FETCH('foo'); # ~ 200_000/s (a bit slower despite fast())
|
490
|
|
|
|
|
|
|
tied(%$tied_hybrid)->FETCH('foo'); # ~ 230_000/s
|
491
|
|
|
|
|
|
|
$tied_hybrid->fast->FETCH('foo'); # ~ 150_000/s
|
492
|
|
|
|
|
|
|
$tied_hybrid->{foo}; # ~ 60_000/s
|
493
|
|
|
|
|
|
|
$tied_hybrid->FETCH('foo'); # ~ 25_000/s
|
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
This results are based on bench/benchmark_hash.pl script available in the Object::Hybrid distribution and assume immutable hybrid class (the default, see further in this documentation) and Tie::StdHash-like simple FETCH() on the {foo => 1} hash.
|
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
Above results suggests that tied()-conditional access switching expression is (currently) the fastest solution that is to be used in hot paths (tight loops, etc.) and it requires no promote()ing of primitive to hybrid:
|
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
tied(%$primitive) ?
|
500
|
|
|
|
|
|
|
tied(%$primitive)->FETCH('foo') # ~230_000/s
|
501
|
|
|
|
|
|
|
: $primitive->{foo}; # ~1_700_000/s
|
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
However, using this construct repeatedly may be too cumbersome, so out of hot paths (tight loops, etc.) promote()ing to hybrid can be used to simplify code while retaining and enhancing its portability across various tie()d and non-tie()d primitives.
|
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
For hybrid interfaces performance varies widely depending on whether it is a tied or non-tied respectively:
|
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
$hybrid->{foo}; # ~ 60_000 - 1_700_000/s
|
508
|
|
|
|
|
|
|
$hybrid->FETCH('foo'); # ~ 25_000 - 300_000/s
|
509
|
|
|
|
|
|
|
$hybrid->fast->FETCH('foo'); # ~ 150_000 - 200_000/s
|
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
Consequently, use of any of these should be decided based on the projected use mix of tied vs. non-tied primitives.
|
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
=head1 TODO
|
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
Currently tests cover only tiehashes and tiehandles, there should be tests for other types as well.
|
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
As soon as (and if) Object::Hybrid interface stabilizes enough, its version is to jump to 1.0.
|
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
=head1 SEE ALSO
|
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
The C |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
Objects of standard IO::Handle class and its subclasses (such as IO::File or IO::Socket) are hybrid objects according to general hybrid object definition used by Object::Hybrid, but they are incompatible with promote()d hybrids. However, it should be possible to promote() IO::Handle object to become compatible hybrid.
|
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
=head1 SUPPORT
|
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
Send bug reports, patches, ideas, suggestions, feature requests or any module-related information to L. They are welcome and each carefully considered.
|
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
In particular, if you find certain portions of this documentation either unclear, complicated or incomplete, please let me know, so that I can try to make it better.
|
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
If you have examples of a neat usage of Object::Hybrid, drop a line too.
|
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
=head1 AUTHOR
|
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
Alexandr Kononoff (L)
|
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE
|
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
Copyright (c) 2010 Alexandr Kononoff (L). All rights reserved.
|
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
This program is free software; you can use, redistribute and/or modify it either under the same terms as Perl itself or, at your discretion, under following Simplified (2-clause) BSD License terms:
|
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
546
|
|
|
|
|
|
|
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
=cut
|
551
|
|
|
|
|
|
|
|
552
|
1
|
|
|
1
|
|
5
|
no warnings;
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
37
|
|
553
|
|
|
|
|
|
|
|
554
|
1
|
|
|
1
|
|
518
|
use Object::Hybrid::Class (); # Object::Hybrid itself is not a hybrid class
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
2924
|
|
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
sub CLASS_MUTABLE () { 'Object::Hybrid::CLASS' }
|
557
|
|
|
|
|
|
|
sub CLASS () { 'Object::Hybrid::CLASS' }
|
558
|
|
|
|
|
|
|
sub CLASS_HASH () { 'Object::Hybrid::HASH2' }
|
559
|
|
|
|
|
|
|
sub CLASS_HASH_NONTIED () { 'Object::Hybrid::HASH' }
|
560
|
|
|
|
|
|
|
sub CLASS_SCALAR () { 'Object::Hybrid::SCALAR2' }
|
561
|
|
|
|
|
|
|
sub CLASS_SCALAR_NONTIED () { 'Object::Hybrid::SCALAR' }
|
562
|
|
|
|
|
|
|
sub CLASS_ARRAY () { 'Object::Hybrid::ARRAY2' }
|
563
|
|
|
|
|
|
|
sub CLASS_ARRAY_NONTIED () { 'Object::Hybrid::ARRAY' }
|
564
|
|
|
|
|
|
|
sub CLASS_HANDLE () { 'Object::Hybrid::GLOB2' }
|
565
|
|
|
|
|
|
|
sub CLASS_HANDLE_NONTIED () { 'Object::Hybrid::GLOB' }
|
566
|
|
|
|
|
|
|
sub CLASS_AUTOPROMO () { 'Object::Hybrid::AUTOPROMOTE' }
|
567
|
|
|
|
|
|
|
sub FRONTAL () { 'FRONTAL' }
|
568
|
|
|
|
|
|
|
sub Class () { 'Object::Hybrid::Class' }
|
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
sub frontclass_name {
|
571
|
51
|
|
|
51
|
0
|
1951
|
my (undef, $class, $primitive) = @_;
|
572
|
51
|
50
|
66
|
|
|
281
|
return join '_', $class, ref $primitive ? _ref_type($primitive) : $primitive||(), FRONTAL
|
573
|
|
|
|
|
|
|
}
|
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
my %class4type = (
|
576
|
|
|
|
|
|
|
HASH => CLASS_HASH,
|
577
|
|
|
|
|
|
|
ARRAY => CLASS_ARRAY,
|
578
|
|
|
|
|
|
|
SCALAR => CLASS_SCALAR,
|
579
|
|
|
|
|
|
|
GLOB => CLASS_HANDLE,
|
580
|
|
|
|
|
|
|
);
|
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
my %nontied_class4type = (
|
583
|
|
|
|
|
|
|
HASH => CLASS_HASH_NONTIED,
|
584
|
|
|
|
|
|
|
ARRAY => CLASS_ARRAY_NONTIED,
|
585
|
|
|
|
|
|
|
SCALAR => CLASS_SCALAR_NONTIED,
|
586
|
|
|
|
|
|
|
GLOB => CLASS_HANDLE_NONTIED,
|
587
|
|
|
|
|
|
|
);
|
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
my %AD;
|
590
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
__PACKAGE__->import(qw(promote));
|
592
|
|
|
|
|
|
|
sub import {
|
593
|
2
|
|
|
2
|
|
9
|
my $self = shift;
|
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
# interface...
|
596
|
2
|
50
|
|
|
|
13
|
my $opt
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
597
|
|
|
|
|
|
|
= @_ > 1 ? {@_}
|
598
|
|
|
|
|
|
|
: !@_ ? { }
|
599
|
|
|
|
|
|
|
: ref $_[0] eq 'HASH' ? $_[0]
|
600
|
|
|
|
|
|
|
: { feature => [$_[0]] };
|
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
# normalize %$opt...
|
603
|
2
|
|
|
|
|
3
|
foreach my $list (qw(feature export autopromote)) {
|
604
|
|
|
|
|
|
|
next
|
605
|
6
|
100
|
|
|
|
10
|
if !exists $opt->{$list};
|
606
|
|
|
|
|
|
|
ref $opt->{$list} eq 'ARRAY'
|
607
|
|
|
|
|
|
|
or $opt->{$list}
|
608
|
2
|
50
|
|
|
|
6
|
= [$opt->{$list}];
|
609
|
|
|
|
|
|
|
}
|
610
|
|
|
|
|
|
|
|
611
|
2
|
|
|
|
|
2
|
my @goto;
|
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
# process features first...
|
614
|
2
|
50
|
|
|
|
5
|
foreach my $feature (ref $opt->{feature} eq 'ARRAY' ? @{$opt->{feature}} : $opt->{feature}) {
|
|
2
|
|
|
|
|
3
|
|
615
|
2
|
50
|
|
|
|
5
|
if ($feature eq 'promote') { push @{$opt->{export}}, $feature }
|
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
4
|
|
616
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
# mutually exclusive features...
|
618
|
2
|
50
|
|
|
|
7
|
if ($feature eq 'autobox') {
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
619
|
|
|
|
|
|
|
#_load_class($self->CLASS);
|
620
|
0
|
|
|
|
|
0
|
_load_class($self->CLASS_HASH);
|
621
|
0
|
|
|
|
|
0
|
_load_class($self->CLASS_SCALAR);
|
622
|
0
|
|
|
|
|
0
|
_load_class($self->CLASS_ARRAY);
|
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
require
|
625
|
0
|
|
|
|
|
0
|
autobox;
|
626
|
|
|
|
|
|
|
autobox::import( ref($self)||$self,
|
627
|
|
|
|
|
|
|
HASH => $opt->{$feature}||$self->CLASS_HASH, # method instead of constant, for subclassing...
|
628
|
|
|
|
|
|
|
SCALAR => $opt->{$feature}||$self->CLASS_SCALAR,
|
629
|
|
|
|
|
|
|
#GLOB => $opt->{$feature}||$self->CLASS_HANDLE, # not supported by autobox
|
630
|
0
|
|
0
|
|
|
0
|
ARRAY => $opt->{$feature}||$self->CLASS_ARRAY, );
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
631
|
|
|
|
|
|
|
}
|
632
|
|
|
|
|
|
|
elsif ($feature eq 'autopromote') {
|
633
|
|
|
|
|
|
|
require
|
634
|
0
|
|
|
|
|
0
|
autobox;
|
635
|
0
|
|
0
|
|
|
0
|
autobox::import( ref($self)||$self,
|
636
|
|
|
|
|
|
|
HASH => CLASS_AUTOPROMO,
|
637
|
|
|
|
|
|
|
SCALAR => CLASS_AUTOPROMO,
|
638
|
|
|
|
|
|
|
#GLOB => CLASS_AUTOPROMO, # not supported by autobox
|
639
|
|
|
|
|
|
|
ARRAY => CLASS_AUTOPROMO, );
|
640
|
0
|
|
|
|
|
0
|
my $autoload
|
641
|
|
|
|
|
|
|
= __PACKAGE__ . '::AUTOLOAD';
|
642
|
0
|
|
|
|
|
0
|
*{ CLASS_AUTOPROMO . '::AUTOLOAD' } = sub{
|
643
|
0
|
|
0
|
0
|
|
0
|
$self->new($_[0], @{$opt->{$feature}}||());
|
644
|
0
|
|
|
|
|
0
|
$$autoload =~ s/^.*:://;
|
645
|
0
|
0
|
|
|
|
0
|
goto &{ $_[0]->can($$autoload)
|
|
0
|
|
|
|
|
0
|
|
646
|
|
|
|
|
|
|
or croak(_cant_locate_object_method($_[0], $$autoload)) };
|
647
|
0
|
|
|
|
|
0
|
};
|
648
|
|
|
|
|
|
|
}
|
649
|
0
|
|
|
|
|
0
|
elsif ($feature eq 'HASTE') { $opt->{$feature} = 1; }
|
650
|
|
|
|
|
|
|
}
|
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
# process options...
|
653
|
2
|
50
|
|
|
|
4
|
if ($opt->{HASTE}) {
|
654
|
0
|
|
|
|
|
0
|
$Object::Hybrid::HASTE = 1;
|
655
|
|
|
|
|
|
|
}
|
656
|
|
|
|
|
|
|
|
657
|
2
|
50
|
|
|
|
6
|
if ($opt->{export}) {
|
658
|
2
|
|
|
|
|
1
|
my @symbols;
|
659
|
2
|
50
|
|
|
|
4
|
foreach my $symbol (@{$opt->{export}||[]}) {
|
|
2
|
|
|
|
|
4
|
|
660
|
2
|
50
|
|
|
|
3
|
if ( $symbol eq 'promote' ) {
|
661
|
2
|
|
|
|
|
12
|
*{join '::', scalar caller, $symbol}
|
662
|
2
|
|
|
52
|
|
5
|
= sub{ unshift @_, $self; goto &{ $self->can(qw(new)) } };
|
|
52
|
|
|
|
|
17060
|
|
|
52
|
|
|
|
|
59
|
|
|
52
|
|
|
|
|
299
|
|
663
|
|
|
|
|
|
|
}
|
664
|
0
|
|
|
|
|
0
|
else { push @symbols, $symbol }
|
665
|
|
|
|
|
|
|
}
|
666
|
|
|
|
|
|
|
|
667
|
2
|
50
|
33
|
|
|
6
|
if (@symbols
|
|
|
|
33
|
|
|
|
|
668
|
2
|
|
33
|
|
|
15
|
or @{(ref($self)||$self).'::EXPORT'}
|
669
|
2
|
|
|
|
|
7
|
or @{ __PACKAGE__.'::EXPORT'}) {
|
670
|
|
|
|
|
|
|
require
|
671
|
0
|
|
|
|
|
0
|
Exporter;
|
672
|
0
|
0
|
|
|
|
0
|
Exporter::export_to_level(1, $self, @symbols) or
|
673
|
|
|
|
|
|
|
Exporter::export_to_level(1, __PACKAGE__, @symbols); # "inheritance" of export, subclasses can define their own @EXPORTs,
|
674
|
|
|
|
|
|
|
}
|
675
|
|
|
|
|
|
|
}
|
676
|
|
|
|
|
|
|
|
677
|
2
|
50
|
|
|
|
16
|
if (@goto) {
|
678
|
0
|
|
|
|
|
0
|
@_ = @goto;
|
679
|
0
|
|
|
|
|
0
|
goto &{shift(@_)};
|
|
0
|
|
|
|
|
0
|
|
680
|
|
|
|
|
|
|
}
|
681
|
|
|
|
|
|
|
}
|
682
|
|
|
|
|
|
|
|
683
|
54
|
|
|
54
|
0
|
215
|
sub is { Object::Hybrid::Class->is($_[1]) }
|
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
sub new {
|
686
|
113
|
50
|
|
113
|
0
|
19782
|
@_>1 or croak("Error: Nothing to promote");
|
687
|
113
|
|
|
|
|
188
|
my $self = shift;
|
688
|
113
|
|
|
|
|
147
|
my $primitive = $_[0]; # keep $_[0] to retain alias and autovivification ability...
|
689
|
113
|
|
|
|
|
129
|
my ($args, $class);
|
690
|
113
|
50
|
|
|
|
507
|
ref $_[1] eq 'HASH' ? $args : $class = splice @_, 1, 1 unless @_ - 2*int(@_/2); # @_ is odd
|
|
|
100
|
|
|
|
|
|
691
|
113
|
|
|
|
|
483
|
%$args = (%$args, @_[1..$#_]);
|
692
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
# be idempotent...
|
694
|
113
|
50
|
100
|
|
|
590
|
return $primitive
|
695
|
|
|
|
|
|
|
if ref $primitive eq ($class||CLASS);
|
696
|
|
|
|
|
|
|
|
697
|
113
|
50
|
66
|
|
|
227
|
! _ref_isa($primitive)
|
698
|
|
|
|
|
|
|
or $self->Class->is( $primitive)
|
699
|
|
|
|
|
|
|
or $class = ref $primitive;
|
700
|
113
|
|
|
|
|
258
|
my $tied_primitive = _ref_tied($primitive);
|
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
#_load_class(CLASS) if $class and $class->isa(CLASS);
|
703
|
|
|
|
|
|
|
|
704
|
113
|
|
|
|
|
174
|
my $primitive_type;
|
705
|
|
|
|
|
|
|
|
706
|
113
|
100
|
|
|
|
197
|
if ($class) {
|
707
|
|
|
|
|
|
|
_can_tie($class, $primitive) # autovivifies $primitive
|
708
|
30
|
50
|
33
|
|
|
64
|
or $class4type{$primitive_type ||= _ref_type($primitive)}
|
|
|
|
66
|
|
|
|
|
|
|
|
33
|
|
|
|
|
709
|
|
|
|
|
|
|
or $self->Class->is($class)
|
710
|
|
|
|
|
|
|
or croak("Error: Wrong hybrid class $class, either not labled as such or defines no perltie methods for '$primitive' primitive");
|
711
|
|
|
|
|
|
|
}
|
712
|
|
|
|
|
|
|
|
713
|
113
|
50
|
|
|
|
269
|
ref $primitive
|
714
|
|
|
|
|
|
|
or croak("Error: No primitive to promote");
|
715
|
113
|
50
|
66
|
|
|
363
|
$class4type{$primitive_type ||= _ref_type($primitive)}
|
716
|
|
|
|
|
|
|
or croak("Error: Can't promote unsupported non-tie()able primitive $primitive");
|
717
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
my $mutable_class
|
719
|
|
|
|
|
|
|
= defined $args->{mutable_class}
|
720
|
|
|
|
|
|
|
? $args->{mutable_class}
|
721
|
113
|
50
|
|
|
|
545
|
: defined $self->Class->mutable_class($class)
|
|
|
100
|
|
|
|
|
|
722
|
|
|
|
|
|
|
? $self->Class->mutable_class($class)
|
723
|
|
|
|
|
|
|
: $tied_primitive;
|
724
|
|
|
|
|
|
|
|
725
|
113
|
100
|
|
|
|
188
|
if ($class) {
|
726
|
30
|
50
|
|
|
|
103
|
unless ( $self->Class->is($class) ) { # use $class as subclass...
|
727
|
30
|
|
|
|
|
84
|
my $make_class = join '', $self->frontclass_name($class, $primitive_type);
|
728
|
30
|
|
|
|
|
203
|
@{ $make_class . '::ISA' } or
|
729
|
3
|
|
|
|
|
49
|
@{ $make_class . '::ISA' }
|
730
|
|
|
|
|
|
|
=( $class
|
731
|
|
|
|
|
|
|
, $mutable_class ? ()
|
732
|
|
|
|
|
|
|
: $nontied_class4type{$primitive_type}||()
|
733
|
30
|
50
|
33
|
|
|
31
|
, $class4type{$primitive_type}||() );
|
|
|
100
|
33
|
|
|
|
|
734
|
30
|
|
|
|
|
59
|
$class = $make_class;
|
735
|
|
|
|
|
|
|
}
|
736
|
|
|
|
|
|
|
}
|
737
|
|
|
|
|
|
|
else {
|
738
|
83
|
100
|
|
|
|
155
|
if ( $mutable_class ) {
|
739
|
74
|
|
|
|
|
119
|
$class = $class4type{$primitive_type};
|
740
|
|
|
|
|
|
|
}
|
741
|
|
|
|
|
|
|
else {
|
742
|
9
|
|
|
|
|
30
|
my $make_class = $self->frontclass_name($class4type{$primitive_type});
|
743
|
9
|
|
|
|
|
71
|
@{ $make_class . '::ISA' } or
|
744
|
2
|
|
|
|
|
28
|
@{ $make_class . '::ISA' }
|
745
|
|
|
|
|
|
|
=( $nontied_class4type{$primitive_type}||()
|
746
|
9
|
100
|
33
|
|
|
11
|
, $class4type{$primitive_type}||() );
|
|
|
|
33
|
|
|
|
|
747
|
9
|
|
|
|
|
13
|
$class = $make_class;
|
748
|
|
|
|
|
|
|
}
|
749
|
|
|
|
|
|
|
}
|
750
|
|
|
|
|
|
|
_load_class($class4type{$primitive_type}
|
751
|
113
|
|
|
|
|
285
|
, $nontied_class4type{$primitive_type}); # custom hybrid class may subclass them, so load anyway
|
752
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
my
|
754
|
113
|
|
|
|
|
2959
|
$bless = $class->can('bless');
|
755
|
113
|
50
|
|
|
|
286
|
$bless ? $class->$bless($primitive)
|
756
|
|
|
|
|
|
|
: bless $primitive, $class;
|
757
|
|
|
|
|
|
|
|
758
|
113
|
|
66
|
|
|
1003
|
$_[0] ||= $primitive; # autovivify in case of method call (otherwise prototype constraint)
|
759
|
113
|
|
|
|
|
558
|
return $primitive
|
760
|
|
|
|
|
|
|
}
|
761
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
sub tie {
|
763
|
|
|
|
|
|
|
return undef
|
764
|
12
|
50
|
|
12
|
1
|
7730
|
if !ref $_[1];
|
765
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
my $tied
|
767
|
12
|
|
|
|
|
92
|
= $_[1] =~ m'(?:^|=)HASH' ? tie( %{$_[1]}, @_[2..$#_] )
|
768
|
0
|
|
|
|
|
0
|
: $_[1] =~ m'(?:^|=)SCALAR' ? tie( ${$_[1]}, @_[2..$#_] )
|
769
|
0
|
|
|
|
|
0
|
: $_[1] =~ m'(?:^|=)ARRAY' ? tie( @{$_[1]}, @_[2..$#_] )
|
770
|
12
|
0
|
|
|
|
87
|
: $_[1] =~ m'(?:^|=)GLOB' ? tie( *{$_[1]}, @_[2..$#_] )
|
|
0
|
0
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
771
|
|
|
|
|
|
|
: undef
|
772
|
|
|
|
|
|
|
or return undef;
|
773
|
|
|
|
|
|
|
|
774
|
12
|
|
|
|
|
113
|
Object::Hybrid->new($_[1]);
|
775
|
12
|
|
|
|
|
30
|
return $tied
|
776
|
|
|
|
|
|
|
}
|
777
|
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
sub _load_class {
|
779
|
113
|
|
|
113
|
|
198
|
foreach (@_) {
|
780
|
226
|
50
|
|
|
|
342
|
next if !$_;
|
781
|
226
|
100
|
|
|
|
481
|
if (ref $AD{$_} eq 'CODE') { &{$AD{$_}} }
|
|
113
|
|
|
|
|
111
|
|
|
113
|
|
|
|
|
205
|
|
782
|
|
|
|
|
|
|
else {
|
783
|
|
|
|
|
|
|
eval( !exists $AD{$_} ? "require $_"
|
784
|
1
|
50
|
0
|
1
|
0
|
5
|
: $AD{$_} );
|
|
1
|
0
|
0
|
1
|
0
|
1
|
|
|
1
|
0
|
0
|
42
|
0
|
3
|
|
|
1
|
100
|
|
0
|
|
7
|
|
|
1
|
50
|
|
10
|
|
2
|
|
|
1
|
50
|
|
5
|
|
6
|
|
|
113
|
0
|
|
109
|
|
2589
|
|
|
10
|
50
|
|
0
|
|
58
|
|
|
0
|
50
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
10
|
|
|
|
|
113
|
|
|
10
|
|
|
|
|
273
|
|
|
12
|
|
|
|
|
76
|
|
|
0
|
|
|
|
|
0
|
|
|
6
|
|
|
|
|
13
|
|
|
6
|
|
|
|
|
19
|
|
|
6
|
|
|
|
|
80
|
|
|
6
|
|
|
|
|
39
|
|
|
4
|
|
|
|
|
27
|
|
|
12
|
|
|
|
|
242
|
|
|
4
|
|
|
|
|
9
|
|
|
4
|
|
|
|
|
41
|
|
|
4
|
|
|
|
|
29
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
12
|
|
|
|
|
79
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
4
|
|
|
|
|
30
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
12
|
|
|
|
|
72
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
10
|
|
|
|
|
75
|
|
|
4
|
|
|
|
|
11
|
|
|
4
|
|
|
|
|
50
|
|
|
4
|
|
|
|
|
13
|
|
|
4
|
|
|
|
|
36
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
18
|
|
|
|
|
109
|
|
|
0
|
|
|
|
|
0
|
|
|
5
|
|
|
|
|
108
|
|
|
109
|
|
|
|
|
8540
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
785
|
113
|
|
|
|
|
294
|
undef $AD{$_}; # be idempotent
|
786
|
113
|
50
|
|
|
|
311
|
!$@ or croak("Error: Can't load $_: $@");
|
787
|
|
|
|
|
|
|
}
|
788
|
|
|
|
|
|
|
}
|
789
|
113
|
|
|
|
|
326
|
return( (grep $_, @_)[0] ) # same as $_[0] || $_[1] || ...
|
790
|
|
|
|
|
|
|
}
|
791
|
|
|
|
|
|
|
|
792
|
0
|
|
|
0
|
0
|
0
|
sub can_tie { shift; _can_tie(@_) }
|
|
0
|
|
|
|
|
0
|
|
793
|
|
|
|
|
|
|
sub _can_tie { # if not given, autovivifies (in place) primitive of type that can be tie()d with given class
|
794
|
|
|
|
|
|
|
#my ($tie_to, $primitive) = @_;
|
795
|
|
|
|
|
|
|
return undef
|
796
|
30
|
50
|
|
30
|
|
55
|
if !$_[0];
|
797
|
|
|
|
|
|
|
#or ref $_[0]
|
798
|
|
|
|
|
|
|
#and !_ref_isa($_[0]);
|
799
|
|
|
|
|
|
|
|
800
|
|
|
|
|
|
|
return ref $_[1]
|
801
|
26
|
|
|
|
|
82
|
? eval{ $_[0]->can( "TIE"._ref_type($_[1])) } ? $_[1] : undef
|
802
|
4
|
|
|
|
|
22
|
: eval{ $_[0]->can(qw(TIEHASH)) } ? \%{$_[1]}
|
|
4
|
|
|
|
|
20
|
|
803
|
0
|
|
|
|
|
0
|
: eval{ $_[0]->can(qw(TIESCALAR)) } ? \${$_[1]}
|
|
0
|
|
|
|
|
0
|
|
804
|
0
|
|
|
|
|
0
|
: eval{ $_[0]->can(qw(TIEARRAY)) } ? \@{$_[1]}
|
|
0
|
|
|
|
|
0
|
|
805
|
30
|
100
|
|
|
|
84
|
: eval{ $_[0]->can(qw(TIEHANDLE)) } ? \*{$_[1]}
|
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
806
|
|
|
|
|
|
|
: undef
|
807
|
|
|
|
|
|
|
}
|
808
|
|
|
|
|
|
|
|
809
|
0
|
|
|
0
|
1
|
0
|
sub ref_tied { shift; _ref_tied(@_) }
|
|
0
|
|
|
|
|
0
|
|
810
|
|
|
|
|
|
|
sub _ref_tied2 { # this is much faster at least for tied hashes, but for non-tied tries all variants (still may be faster)
|
811
|
0
|
0
|
|
0
|
|
0
|
return undef if !ref $_[0];
|
812
|
|
|
|
|
|
|
return eval{ tied( %{$_[0]} ) }
|
813
|
|
|
|
|
|
|
|| eval{ tied( ${$_[0]} ) }
|
814
|
|
|
|
|
|
|
|| eval{ tied( @{$_[0]} ) }
|
815
|
0
|
|
0
|
|
|
0
|
|| eval{ tied( *{$_[0]} ) }
|
816
|
|
|
|
|
|
|
|| undef
|
817
|
|
|
|
|
|
|
}
|
818
|
|
|
|
|
|
|
sub _ref_tied {
|
819
|
|
|
|
|
|
|
return undef
|
820
|
113
|
100
|
|
113
|
|
515
|
if !ref $_[0];
|
821
|
|
|
|
|
|
|
return $_[0] =~ m'(?:^|=)HASH' ? tied( %{$_[0]} )||0
|
822
|
|
|
|
|
|
|
: $_[0] =~ m'(?:^|=)SCALAR' ? tied( ${$_[0]} )||0
|
823
|
|
|
|
|
|
|
: $_[0] =~ m'(?:^|=)ARRAY' ? tied( @{$_[0]} )||0
|
824
|
109
|
50
|
100
|
|
|
542
|
: $_[0] =~ m'(?:^|=)GLOB' ? tied( *{$_[0]} )||0
|
|
|
50
|
0
|
|
|
|
|
|
|
50
|
0
|
|
|
|
|
|
|
100
|
100
|
|
|
|
|
825
|
|
|
|
|
|
|
: undef
|
826
|
|
|
|
|
|
|
}
|
827
|
|
|
|
|
|
|
|
828
|
0
|
|
|
0
|
1
|
0
|
sub ref_type { shift; _ref_type(@_) }
|
|
0
|
|
|
|
|
0
|
|
829
|
|
|
|
|
|
|
sub _ref_type {
|
830
|
139
|
50
|
|
139
|
|
250
|
return undef if !ref $_[0];
|
831
|
139
|
100
|
|
|
|
631
|
return $1 if $_[0] =~ /=(\w+)/;
|
832
|
83
|
|
|
|
|
486
|
return ref $_[0]
|
833
|
|
|
|
|
|
|
}
|
834
|
|
|
|
|
|
|
|
835
|
0
|
|
|
0
|
1
|
0
|
sub ref_isa { shift; _ref_isa(@_) }
|
|
0
|
|
|
|
|
0
|
|
836
|
|
|
|
|
|
|
sub _ref_isa {
|
837
|
113
|
100
|
|
113
|
|
299
|
return undef if !ref $_[0];
|
838
|
109
|
100
|
|
|
|
429
|
return '' if exists $class4type{ref $_[0]};
|
839
|
44
|
50
|
33
|
|
|
131
|
return 0 if defined $_[1] and !$_[0]->isa($_[1]);
|
840
|
44
|
|
|
|
|
332
|
return $_[0]
|
841
|
|
|
|
|
|
|
}
|
842
|
|
|
|
|
|
|
|
843
|
10
|
|
|
10
|
0
|
54
|
sub croak { require Carp; goto &Carp::croak; }
|
|
10
|
|
|
|
|
1636
|
|
844
|
|
|
|
|
|
|
|
845
|
739
|
100
|
|
739
|
|
9434
|
sub _alter_case { $_[0] =~ /[A-Z]/ ? lc($_[0]) : uc($_[0]) };
|
846
|
0
|
|
|
0
|
0
|
0
|
sub method_alias { _alter_case($_[1]) }
|
847
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
sub methods {
|
849
|
117
|
|
|
117
|
0
|
189
|
shift;
|
850
|
117
|
|
|
|
|
94
|
my $subs;
|
851
|
117
|
100
|
|
|
|
592
|
ref $_[0] eq 'HASH' ? ($subs) : %$subs = @_;
|
852
|
117
|
|
|
|
|
216
|
my $caller = caller;
|
853
|
117
|
|
|
|
|
412
|
foreach my $method (keys %$subs) {
|
854
|
|
|
|
|
|
|
# explicit aliases...
|
855
|
|
|
|
|
|
|
ref $subs->{$method} eq 'CODE'
|
856
|
|
|
|
|
|
|
or ref( $subs->{$method}
|
857
|
383
|
50
|
33
|
|
|
882
|
= $subs->{$subs->{$method}}) eq 'CODE'
|
858
|
|
|
|
|
|
|
or next;
|
859
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
# implicit altered-case aliases...
|
861
|
383
|
|
|
|
|
501
|
my $method2 = _alter_case($method);
|
862
|
383
|
|
|
|
|
304
|
my $goto;
|
863
|
383
|
|
|
|
|
2245
|
*{join '::', $caller, $method } =
|
864
|
383
|
|
|
|
|
320
|
*{join '::', $caller, $method2} = $subs->{$method};
|
|
383
|
|
|
|
|
2529
|
|
865
|
|
|
|
|
|
|
|
866
|
|
|
|
|
|
|
}
|
867
|
|
|
|
|
|
|
}
|
868
|
|
|
|
|
|
|
|
869
|
|
|
|
|
|
|
sub _cant_locate_object_method {
|
870
|
10
|
|
33
|
10
|
|
94
|
join '', "Object::Hybrid: Can't locate object method \""
|
|
|
|
33
|
|
|
|
|
871
|
|
|
|
|
|
|
, $_[1], "\" via package \""
|
872
|
|
|
|
|
|
|
, ref($_[0])||$_[0], "\" (perhaps you forgot to load \""
|
873
|
|
|
|
|
|
|
, ref($_[0])||$_[0], "\"?) "
|
874
|
|
|
|
|
|
|
}
|
875
|
|
|
|
|
|
|
|
876
|
|
|
|
|
|
|
my $CLASS_MUTABLE = <<'CLASS_MUTABLE';
|
877
|
|
|
|
|
|
|
|
878
|
|
|
|
|
|
|
$INC{ INCKEY_REPLACE } ||= 1;
|
879
|
|
|
|
|
|
|
package PACKAGE_REPLACE;
|
880
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
use Object::Hybrid::Class; # just labeling
|
882
|
|
|
|
|
|
|
|
883
|
|
|
|
|
|
|
sub can;
|
884
|
|
|
|
|
|
|
#sub isa;
|
885
|
|
|
|
|
|
|
|
886
|
|
|
|
|
|
|
Object::Hybrid->methods(
|
887
|
|
|
|
|
|
|
SELF => sub {
|
888
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
return $_[0]
|
890
|
|
|
|
|
|
|
},
|
891
|
|
|
|
|
|
|
fast => sub {
|
892
|
|
|
|
|
|
|
return TIED_REPLACE || $_[0];
|
893
|
|
|
|
|
|
|
|
894
|
|
|
|
|
|
|
# next is fast only for tie()d primitives, especially hashes as they come first in conditional, but traped exceptions within eval{} are slow things down unacceptably for plaing primitives...
|
895
|
|
|
|
|
|
|
return eval{ tied( %{$_[0]} ) }
|
896
|
|
|
|
|
|
|
|| eval{ tied( ${$_[0]} ) }
|
897
|
|
|
|
|
|
|
|| eval{ tied( @{$_[0]} ) }
|
898
|
|
|
|
|
|
|
|| eval{ tied( *{$_[0]} ) }
|
899
|
|
|
|
|
|
|
|| $_[0];
|
900
|
|
|
|
|
|
|
|
901
|
|
|
|
|
|
|
# next is hoplessly slow, even after inlining _ref_type()...
|
902
|
|
|
|
|
|
|
return $_[0] if !ref $_[0];
|
903
|
|
|
|
|
|
|
#my $type = Object::Hybrid::_ref_type( $_[0] );
|
904
|
|
|
|
|
|
|
my $type = $_[0] =~ /=(\w+)/ ? $1 : ref $_[0];
|
905
|
|
|
|
|
|
|
return $type eq 'HASH' ? tied( %{$_[0]} ) || $_[0]
|
906
|
|
|
|
|
|
|
: $type eq 'SCALAR' ? tied( ${$_[0]} ) || $_[0]
|
907
|
|
|
|
|
|
|
: $type eq 'ARRAY' ? tied( @{$_[0]} ) || $_[0]
|
908
|
|
|
|
|
|
|
: $type eq 'GLOB' ? tied( *{$_[0]} ) || $_[0]
|
909
|
|
|
|
|
|
|
: $_[0];
|
910
|
|
|
|
|
|
|
},
|
911
|
|
|
|
|
|
|
call => sub{
|
912
|
|
|
|
|
|
|
@_ > 1
|
913
|
|
|
|
|
|
|
or Object::Hybrid::croak("Error: Nothing to call");
|
914
|
|
|
|
|
|
|
|
915
|
|
|
|
|
|
|
local $Object::Hybrid::Portable = 1;
|
916
|
|
|
|
|
|
|
my $method = lc(splice(@_, 1, 1));
|
917
|
|
|
|
|
|
|
return shift->$method(@_) # is ok, except for caller()
|
918
|
|
|
|
|
|
|
},
|
919
|
|
|
|
|
|
|
);
|
920
|
|
|
|
|
|
|
|
921
|
|
|
|
|
|
|
#my $AUTOLOAD = \&AUTOLOAD;
|
922
|
|
|
|
|
|
|
sub AUTOLOAD {
|
923
|
|
|
|
|
|
|
package Object::Hybrid; # to not qualify _ref_tied(), _ref_type(), croak(), etc...
|
924
|
|
|
|
|
|
|
|
925
|
|
|
|
|
|
|
( my $METHOD = $PACKAGE_REPLACE::AUTOLOAD ) =~ s/^.*:://;
|
926
|
|
|
|
|
|
|
my $METHOD_is_lc = ($METHOD !~ /[A-Z]/);
|
927
|
|
|
|
|
|
|
my $SUB_METHOD;
|
928
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
#goto &{ *{ $PACKAGE_REPLACE::AUTOLOAD }
|
930
|
|
|
|
|
|
|
goto &{ *{ join '::', ref($_[0])||$_[0], $METHOD }
|
931
|
|
|
|
|
|
|
= $SUB_METHOD
|
932
|
|
|
|
|
|
|
= sub{
|
933
|
|
|
|
|
|
|
my $swap
|
934
|
|
|
|
|
|
|
; $swap = splice(@_, 0, 1, $swap)
|
935
|
|
|
|
|
|
|
if $swap = TIED_REPLACE;
|
936
|
|
|
|
|
|
|
|
937
|
|
|
|
|
|
|
$METHOD eq 'can' and my $can_method = $_[1];
|
938
|
|
|
|
|
|
|
|
939
|
|
|
|
|
|
|
my
|
940
|
|
|
|
|
|
|
$sub_method;
|
941
|
|
|
|
|
|
|
$sub_method
|
942
|
|
|
|
|
|
|
= !$can_method && $Object::Hybrid::HASTE
|
943
|
|
|
|
|
|
|
? return shift->$METHOD(@_)
|
944
|
|
|
|
|
|
|
: $_[0]->UNIVERSAL::can( $can_method||$METHOD )
|
945
|
|
|
|
|
|
|
|| $_[0]->UNIVERSAL::can(_alter_case($can_method||$METHOD))
|
946
|
|
|
|
|
|
|
if $swap
|
947
|
|
|
|
|
|
|
or $can_method;
|
948
|
|
|
|
|
|
|
|
949
|
|
|
|
|
|
|
if (!$sub_method) {
|
950
|
|
|
|
|
|
|
splice(@_, 0, 1, $swap) if $swap; # revert swap, if any
|
951
|
|
|
|
|
|
|
#my
|
952
|
|
|
|
|
|
|
#$subclass;
|
953
|
|
|
|
|
|
|
#$subclass = $class4type{_ref_type($_[0])}||'FOO', # instead inlining...
|
954
|
|
|
|
|
|
|
#$subclass = $class4type{!ref $_[0] ? undef : $_[0] =~ /=(\w+)/ ? $1 : ref $_[0]}||'FOO',
|
955
|
|
|
|
|
|
|
$sub_method
|
956
|
|
|
|
|
|
|
= SUBCLASS_REPLACE->UNIVERSAL::can( $can_method||$METHOD )
|
957
|
|
|
|
|
|
|
|| SUBCLASS_REPLACE->UNIVERSAL::can(_alter_case($can_method||$METHOD));
|
958
|
|
|
|
|
|
|
}
|
959
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
return $sub_method if $can_method;
|
961
|
|
|
|
|
|
|
|
962
|
|
|
|
|
|
|
$sub_method
|
963
|
|
|
|
|
|
|
or $METHOD_is_lc and $Object::Hybrid::Portable and return # lower-case aliases are fail-safe for compartibility
|
964
|
|
|
|
|
|
|
or croak( _cant_locate_object_method($_[0], $METHOD) );
|
965
|
|
|
|
|
|
|
|
966
|
|
|
|
|
|
|
$sub_method ne $SUB_METHOD # this case is hopefully excluded by above logic, but it may screw up
|
967
|
|
|
|
|
|
|
#and defined(&$sub_method) # here goto() to not defined(&method) is ok as it may be autoloadable in tied() class or otherwise
|
968
|
|
|
|
|
|
|
or croak( join '', "Undefined method \""
|
969
|
|
|
|
|
|
|
, $METHOD, "\" called via package \""
|
970
|
|
|
|
|
|
|
, ref($_[0])||$_[0], "\"");
|
971
|
|
|
|
|
|
|
|
972
|
|
|
|
|
|
|
goto &$sub_method
|
973
|
|
|
|
|
|
|
} };
|
974
|
|
|
|
|
|
|
}
|
975
|
|
|
|
|
|
|
|
976
|
|
|
|
|
|
|
CLASS_MUTABLE
|
977
|
|
|
|
|
|
|
|
978
|
|
|
|
|
|
|
sub _compile_class {
|
979
|
113
|
|
|
113
|
|
222
|
my ($CLASS_MUTABLE, $PACKAGE, $SUBCLASS, $TIED) = @_;
|
980
|
113
|
|
|
|
|
581
|
(my $INCKEY = $PACKAGE . '.pm') =~ s/::/\//g;
|
981
|
|
|
|
|
|
|
|
982
|
113
|
|
|
|
|
976
|
$CLASS_MUTABLE =~ s/PACKAGE_REPLACE/$PACKAGE/g;
|
983
|
113
|
|
|
|
|
877
|
$CLASS_MUTABLE =~ s/INCKEY_REPLACE/'$INCKEY'/g;
|
984
|
113
|
|
|
|
|
668
|
$CLASS_MUTABLE =~ s/SUBCLASS_REPLACE/$SUBCLASS/g;
|
985
|
113
|
|
|
|
|
677
|
$CLASS_MUTABLE =~ s/TIED_REPLACE/$TIED/g;
|
986
|
1
|
50
|
33
|
1
|
|
6
|
eval $CLASS_MUTABLE;
|
|
1
|
50
|
33
|
1
|
|
1
|
|
|
1
|
50
|
33
|
1
|
|
4
|
|
|
1
|
50
|
33
|
1
|
|
4
|
|
|
1
|
50
|
33
|
1
|
|
1
|
|
|
1
|
50
|
0
|
1
|
|
3
|
|
|
1
|
50
|
0
|
1
|
|
4
|
|
|
1
|
0
|
33
|
1
|
|
1
|
|
|
1
|
50
|
0
|
1
|
|
3
|
|
|
1
|
0
|
0
|
1
|
|
5
|
|
|
1
|
0
|
0
|
1
|
|
2
|
|
|
1
|
0
|
0
|
1
|
|
4
|
|
|
1
|
0
|
0
|
1
|
|
5
|
|
|
1
|
0
|
0
|
1
|
|
1
|
|
|
1
|
0
|
0
|
1
|
|
4
|
|
|
1
|
0
|
0
|
1
|
|
6
|
|
|
1
|
0
|
0
|
1
|
|
1
|
|
|
1
|
0
|
0
|
1
|
|
4
|
|
|
1
|
100
|
66
|
1
|
|
4
|
|
|
1
|
100
|
100
|
1
|
|
1
|
|
|
1
|
50
|
66
|
1
|
|
3
|
|
|
1
|
50
|
66
|
1
|
|
5
|
|
|
1
|
100
|
66
|
1
|
|
1
|
|
|
1
|
100
|
50
|
1
|
|
4
|
|
|
1
|
100
|
66
|
1
|
|
4
|
|
|
1
|
0
|
0
|
1
|
|
2
|
|
|
1
|
50
|
0
|
1
|
|
3
|
|
|
1
|
0
|
0
|
1
|
|
5
|
|
|
1
|
0
|
0
|
1
|
|
1
|
|
|
1
|
0
|
0
|
1
|
|
4
|
|
|
1
|
0
|
0
|
1
|
|
6
|
|
|
1
|
0
|
0
|
1
|
|
1
|
|
|
1
|
0
|
0
|
1
|
|
3
|
|
|
1
|
0
|
0
|
1
|
|
4
|
|
|
1
|
0
|
0
|
1
|
|
2
|
|
|
1
|
0
|
0
|
1
|
|
3
|
|
|
1
|
0
|
0
|
1
|
|
5
|
|
|
1
|
0
|
0
|
1
|
|
2
|
|
|
1
|
0
|
33
|
1
|
|
3
|
|
|
1
|
50
|
66
|
1
|
|
5
|
|
|
1
|
0
|
66
|
1
|
|
1
|
|
|
1
|
100
|
33
|
1
|
|
3
|
|
|
1
|
50
|
0
|
1
|
|
6
|
|
|
1
|
50
|
0
|
1
|
|
1
|
|
|
1
|
100
|
33
|
1
|
|
4
|
|
|
1
|
100
|
0
|
1
|
|
5
|
|
|
1
|
100
|
0
|
1
|
|
1
|
|
|
1
|
50
|
0
|
1
|
|
4
|
|
|
1
|
0
|
33
|
1
|
|
6
|
|
|
1
|
50
|
33
|
1
|
|
2
|
|
|
1
|
50
|
66
|
1
|
|
3
|
|
|
1
|
50
|
50
|
1
|
|
4
|
|
|
1
|
0
|
33
|
1
|
|
2
|
|
|
1
|
50
|
0
|
1
|
|
3
|
|
|
1
|
50
|
0
|
1
|
|
5
|
|
|
1
|
50
|
0
|
1
|
|
2
|
|
|
1
|
50
|
33
|
1
|
|
3
|
|
|
1
|
0
|
33
|
1
|
|
4
|
|
|
1
|
0
|
66
|
1
|
|
1
|
|
|
1
|
50
|
50
|
1
|
|
4
|
|
|
1
|
50
|
33
|
1
|
|
6
|
|
|
1
|
0
|
0
|
1
|
|
2
|
|
|
1
|
50
|
33
|
1
|
|
5
|
|
|
1
|
50
|
100
|
1
|
|
10
|
|
|
1
|
50
|
66
|
1
|
|
2
|
|
|
1
|
50
|
66
|
1
|
|
9
|
|
|
1
|
0
|
0
|
1
|
|
9
|
|
|
1
|
0
|
0
|
1
|
|
2
|
|
|
1
|
100
|
0
|
1
|
|
6
|
|
|
1
|
50
|
0
|
1
|
|
7
|
|
|
1
|
50
|
|
1
|
|
1
|
|
|
1
|
50
|
|
1
|
|
4
|
|
|
1
|
100
|
|
1
|
|
5
|
|
|
1
|
100
|
|
1
|
|
1
|
|
|
1
|
50
|
|
1
|
|
5
|
|
|
1
|
0
|
|
1
|
|
6
|
|
|
1
|
0
|
|
1
|
|
2
|
|
|
1
|
|
|
1
|
|
4
|
|
|
1
|
|
|
1
|
|
5
|
|
|
1
|
|
|
1
|
|
2
|
|
|
1
|
|
|
1
|
|
3
|
|
|
1
|
|
|
1
|
|
8
|
|
|
1
|
|
|
1
|
|
1
|
|
|
1
|
|
|
1
|
|
5
|
|
|
1
|
|
|
1
|
|
6
|
|
|
1
|
|
|
1
|
|
2
|
|
|
1
|
|
|
1
|
|
4
|
|
|
1
|
|
|
1
|
|
5
|
|
|
1
|
|
|
1
|
|
2
|
|
|
1
|
|
|
1
|
|
3
|
|
|
1
|
|
|
1
|
|
6
|
|
|
1
|
|
|
1
|
|
1
|
|
|
1
|
|
|
1
|
|
4
|
|
|
1
|
|
|
1
|
|
4
|
|
|
1
|
|
|
1
|
|
2
|
|
|
1
|
|
|
1
|
|
3
|
|
|
1
|
|
|
1
|
|
5
|
|
|
1
|
|
|
1
|
|
1
|
|
|
1
|
|
|
1
|
|
4
|
|
|
1
|
|
|
1
|
|
4
|
|
|
1
|
|
|
1
|
|
2
|
|
|
1
|
|
|
1
|
|
4
|
|
|
1
|
|
|
1
|
|
4
|
|
|
1
|
|
|
1
|
|
2
|
|
|
1
|
|
|
1
|
|
3
|
|
|
1
|
|
|
1
|
|
5
|
|
|
1
|
|
|
1
|
|
1
|
|
|
1
|
|
|
1
|
|
3
|
|
|
1
|
|
|
1
|
|
4
|
|
|
1
|
|
|
1
|
|
1
|
|
|
1
|
|
|
1
|
|
3
|
|
|
1
|
|
|
1
|
|
4
|
|
|
1
|
|
|
1
|
|
2
|
|
|
1
|
|
|
2
|
|
3
|
|
|
1
|
|
|
0
|
|
5
|
|
|
1
|
|
|
83
|
|
1
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
8
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
7
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
7
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
8
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
7
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
9
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
8
|
|
|
1
|
|
|
|
|
7
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
10
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
9
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
8
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
8
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
8
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
9
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
8
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
7
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
7
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
8
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
8
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
8
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
9
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
7
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
8
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
9
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
8
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
7
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
10
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
7
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
7
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
8
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
8
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
5
|
|
|
113
|
|
|
|
|
8915
|
|
|
2
|
|
|
|
|
9
|
|
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
2
|
|
|
2
|
|
|
|
|
9
|
|
|
2
|
|
|
|
|
22
|
|
|
3
|
|
|
|
|
4
|
|
|
3
|
|
|
|
|
3
|
|
|
3
|
|
|
|
|
17
|
|
|
3
|
|
|
|
|
9
|
|
|
3
|
|
|
|
|
4
|
|
|
3
|
|
|
|
|
55
|
|
|
3
|
|
|
|
|
8
|
|
|
3
|
|
|
|
|
10
|
|
|
3
|
|
|
|
|
30
|
|
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
10
|
|
|
3
|
|
|
|
|
9
|
|
|
3
|
|
|
|
|
65
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
83
|
|
|
|
|
93
|
|
|
83
|
|
|
|
|
66
|
|
|
83
|
|
|
|
|
342
|
|
|
83
|
|
|
|
|
149
|
|
|
83
|
|
|
|
|
61
|
|
|
83
|
|
|
|
|
889
|
|
|
83
|
|
|
|
|
138
|
|
|
9
|
|
|
|
|
17
|
|
|
9
|
|
|
|
|
98
|
|
|
83
|
|
|
|
|
169
|
|
|
78
|
|
|
|
|
123
|
|
|
74
|
|
|
|
|
154
|
|
|
74
|
|
|
|
|
199
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
19
|
|
|
12
|
|
|
|
|
322
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
84
|
|
|
|
|
5029
|
|
|
84
|
|
|
|
|
81
|
|
|
84
|
|
|
|
|
358
|
|
|
84
|
|
|
|
|
205
|
|
|
84
|
|
|
|
|
67
|
|
|
84
|
|
|
|
|
991
|
|
|
84
|
|
|
|
|
188
|
|
|
48
|
|
|
|
|
132
|
|
|
48
|
|
|
|
|
372
|
|
|
84
|
|
|
|
|
156
|
|
|
84
|
|
|
|
|
136
|
|
|
84
|
|
|
|
|
240
|
|
|
84
|
|
|
|
|
1170
|
|
|
12
|
|
|
|
|
15
|
|
|
12
|
|
|
|
|
13
|
|
|
12
|
|
|
|
|
38
|
|
|
12
|
|
|
|
|
31
|
|
|
12
|
|
|
|
|
13
|
|
|
12
|
|
|
|
|
49
|
|
|
12
|
|
|
|
|
24
|
|
|
12
|
|
|
|
|
18
|
|
|
12
|
|
|
|
|
134
|
|
|
12
|
|
|
|
|
23
|
|
|
12
|
|
|
|
|
80
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
24
|
|
|
4
|
|
|
|
|
12
|
|
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
36
|
|
|
4
|
|
|
|
|
9
|
|
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
69
|
|
|
4
|
|
|
|
|
12
|
|
|
4
|
|
|
|
|
83
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
234
|
|
|
|
|
13864
|
|
|
234
|
|
|
|
|
229
|
|
|
234
|
|
|
|
|
824
|
|
|
234
|
|
|
|
|
498
|
|
|
234
|
|
|
|
|
190
|
|
|
234
|
|
|
|
|
2382
|
|
|
234
|
|
|
|
|
508
|
|
|
134
|
|
|
|
|
262
|
|
|
134
|
|
|
|
|
771
|
|
|
234
|
|
|
|
|
881
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
987
|
113
|
50
|
|
|
|
488
|
!$@ or die($@);
|
988
|
|
|
|
|
|
|
}
|
989
|
|
|
|
|
|
|
|
990
|
|
|
|
|
|
|
$AD{CLASS_HASH()} = sub{ _compile_class($CLASS_MUTABLE, CLASS_HASH, CLASS_HASH_NONTIED, 'tied( %{$_[0]} )', ); };
|
991
|
|
|
|
|
|
|
$AD{CLASS_ARRAY()} = sub{ _compile_class($CLASS_MUTABLE, CLASS_ARRAY, CLASS_ARRAY_NONTIED, 'tied( @{$_[0]} )', ); };
|
992
|
|
|
|
|
|
|
$AD{CLASS_SCALAR()} = sub{ _compile_class($CLASS_MUTABLE, CLASS_SCALAR, CLASS_SCALAR_NONTIED, 'tied( ${$_[0]} )', ); };
|
993
|
|
|
|
|
|
|
$AD{CLASS_HANDLE()} = sub{ _compile_class($CLASS_MUTABLE, CLASS_HANDLE, CLASS_HANDLE_NONTIED, 'tied( *{$_[0]} )', ); };
|
994
|
|
|
|
|
|
|
|
995
|
|
|
|
|
|
|
$AD{ 'Object::Hybrid::HASH' } = <<'CLASS';
|
996
|
|
|
|
|
|
|
$INC{ "Object\/Hybrid\/HASH.pm" } ||= 1;
|
997
|
|
|
|
|
|
|
package Object::Hybrid::HASH;
|
998
|
|
|
|
|
|
|
|
999
|
|
|
|
|
|
|
use Object::Hybrid::Class; # just labeling
|
1000
|
|
|
|
|
|
|
|
1001
|
|
|
|
|
|
|
sub can { $_[0]->UNIVERSAL::can($_[1]) } # override slow can() of mutable CLASS_HASH
|
1002
|
|
|
|
|
|
|
|
1003
|
|
|
|
|
|
|
Object::Hybrid->methods({
|
1004
|
|
|
|
|
|
|
fast => sub { $_[0] },
|
1005
|
|
|
|
|
|
|
self => sub { $_[0] },
|
1006
|
|
|
|
|
|
|
TIEHASH => sub { bless {}, ref($_[0])||$_[0] },
|
1007
|
|
|
|
|
|
|
STORE => sub { $_[0]->{$_[1]} = $_[2] },
|
1008
|
|
|
|
|
|
|
FETCH => sub { $_[0]->{$_[1]} },
|
1009
|
|
|
|
|
|
|
FIRSTKEY => sub { my $a = scalar keys %{$_[0]}; each %{$_[0]} },
|
1010
|
|
|
|
|
|
|
NEXTKEY => sub { each %{$_[0]} },
|
1011
|
|
|
|
|
|
|
EXISTS => sub { exists $_[0]->{$_[1]} },
|
1012
|
|
|
|
|
|
|
DELETE => sub { delete $_[0]->{$_[1]} },
|
1013
|
|
|
|
|
|
|
CLEAR => sub { %{$_[0]} = () },
|
1014
|
|
|
|
|
|
|
SCALAR => sub { scalar %{$_[0]} },
|
1015
|
|
|
|
|
|
|
});
|
1016
|
|
|
|
|
|
|
|
1017
|
|
|
|
|
|
|
sub DESTROY {}
|
1018
|
|
|
|
|
|
|
|
1019
|
|
|
|
|
|
|
CLASS
|
1020
|
|
|
|
|
|
|
|
1021
|
|
|
|
|
|
|
$AD{ 'Object::Hybrid::SCALAR' } = <<'CLASS';
|
1022
|
|
|
|
|
|
|
$INC{ "Object\/Hybrid\/SCALAR.pm" } = 1;
|
1023
|
|
|
|
|
|
|
package Object::Hybrid::SCALAR;
|
1024
|
|
|
|
|
|
|
|
1025
|
|
|
|
|
|
|
use Object::Hybrid::Class; # just labeling
|
1026
|
|
|
|
|
|
|
|
1027
|
|
|
|
|
|
|
sub can { $_[0]->UNIVERSAL::can($_[1]) } # override slow can() of mutable CLASS_SCALAR
|
1028
|
|
|
|
|
|
|
|
1029
|
|
|
|
|
|
|
Object::Hybrid->methods({
|
1030
|
|
|
|
|
|
|
fast => sub { $_[0] },
|
1031
|
|
|
|
|
|
|
self => sub { $_[0] },
|
1032
|
|
|
|
|
|
|
TIESCALAR => sub {
|
1033
|
|
|
|
|
|
|
my $class = shift;
|
1034
|
|
|
|
|
|
|
my $instance = shift || undef;
|
1035
|
|
|
|
|
|
|
return bless \$instance => $class;
|
1036
|
|
|
|
|
|
|
},
|
1037
|
|
|
|
|
|
|
FETCH => sub { ${$_[0]} },
|
1038
|
|
|
|
|
|
|
STORE => sub { ${$_[0]} = $_[1] },
|
1039
|
|
|
|
|
|
|
});
|
1040
|
|
|
|
|
|
|
|
1041
|
|
|
|
|
|
|
sub DESTROY { undef ${$_[0]} }
|
1042
|
|
|
|
|
|
|
|
1043
|
|
|
|
|
|
|
CLASS
|
1044
|
|
|
|
|
|
|
|
1045
|
|
|
|
|
|
|
$AD{ 'Object::Hybrid::ARRAY' } = <<'CLASS';
|
1046
|
|
|
|
|
|
|
$INC{ "Object\/Hybrid\/ARRAY.pm" } ||= 1;
|
1047
|
|
|
|
|
|
|
package Object::Hybrid::ARRAY;
|
1048
|
|
|
|
|
|
|
|
1049
|
|
|
|
|
|
|
use Object::Hybrid::Class; # just labeling
|
1050
|
|
|
|
|
|
|
|
1051
|
|
|
|
|
|
|
sub can { $_[0]->UNIVERSAL::can($_[1]) } # override slow can() of mutable CLASS_ARRAY
|
1052
|
|
|
|
|
|
|
|
1053
|
|
|
|
|
|
|
Object::Hybrid->methods({
|
1054
|
|
|
|
|
|
|
fast => sub { $_[0] },
|
1055
|
|
|
|
|
|
|
self => sub { $_[0] },
|
1056
|
|
|
|
|
|
|
TIEARRAY => sub { bless [], $_[0] },
|
1057
|
|
|
|
|
|
|
FETCHSIZE => sub { scalar @{$_[0]} },
|
1058
|
|
|
|
|
|
|
STORESIZE => sub { $#{$_[0]} = $_[1]-1 },
|
1059
|
|
|
|
|
|
|
STORE => sub { $_[0]->[$_[1]] = $_[2] },
|
1060
|
|
|
|
|
|
|
FETCH => sub { $_[0]->[$_[1]] },
|
1061
|
|
|
|
|
|
|
CLEAR => sub { @{$_[0]} = () },
|
1062
|
|
|
|
|
|
|
POP => sub { pop(@{$_[0]}) },
|
1063
|
|
|
|
|
|
|
PUSH => sub { my $o = shift; push(@$o,@_) },
|
1064
|
|
|
|
|
|
|
SHIFT => sub { shift(@{$_[0]}) },
|
1065
|
|
|
|
|
|
|
UNSHIFT => sub { my $o = shift; unshift(@$o,@_) },
|
1066
|
|
|
|
|
|
|
EXISTS => sub { exists $_[0]->[$_[1]] },
|
1067
|
|
|
|
|
|
|
DELETE => sub { delete $_[0]->[$_[1]] },
|
1068
|
|
|
|
|
|
|
EXTEND => sub {},
|
1069
|
|
|
|
|
|
|
SPLICE => sub {
|
1070
|
|
|
|
|
|
|
my $ob = shift;
|
1071
|
|
|
|
|
|
|
my $sz = $ob->FETCHSIZE;
|
1072
|
|
|
|
|
|
|
my $off = @_ ? shift : 0;
|
1073
|
|
|
|
|
|
|
$off += $sz if $off < 0;
|
1074
|
|
|
|
|
|
|
my $len = @_ ? shift : $sz-$off;
|
1075
|
|
|
|
|
|
|
return splice(@$ob,$off,$len,@_);
|
1076
|
|
|
|
|
|
|
},
|
1077
|
|
|
|
|
|
|
});
|
1078
|
|
|
|
|
|
|
|
1079
|
|
|
|
|
|
|
sub DESTROY {}
|
1080
|
|
|
|
|
|
|
|
1081
|
|
|
|
|
|
|
CLASS
|
1082
|
|
|
|
|
|
|
|
1083
|
|
|
|
|
|
|
$AD{ 'Object::Hybrid::GLOB' } = <<'CLASS';
|
1084
|
|
|
|
|
|
|
$INC{ "Object\/Hybrid\/GLOB.pm" } ||= 1;
|
1085
|
|
|
|
|
|
|
package Object::Hybrid::GLOB;
|
1086
|
|
|
|
|
|
|
|
1087
|
|
|
|
|
|
|
use Object::Hybrid::Class; # just labeling
|
1088
|
|
|
|
|
|
|
|
1089
|
|
|
|
|
|
|
sub can { $_[0]->UNIVERSAL::can($_[1]) } # override slow can() of mutable CLASS_HANDLE
|
1090
|
|
|
|
|
|
|
|
1091
|
|
|
|
|
|
|
sub new {
|
1092
|
|
|
|
|
|
|
goto &{ $_[0]->can(qw(TIEHANDLE))
|
1093
|
|
|
|
|
|
|
||Object::Hybrid::croak("Method not defined: new() / TIEHANDLE()") }
|
1094
|
|
|
|
|
|
|
}
|
1095
|
|
|
|
|
|
|
|
1096
|
|
|
|
|
|
|
Object::Hybrid->methods({
|
1097
|
|
|
|
|
|
|
fast => sub { $_[0] },
|
1098
|
|
|
|
|
|
|
self => sub { $_[0] },
|
1099
|
|
|
|
|
|
|
TIEHANDLE => sub {
|
1100
|
|
|
|
|
|
|
my ($elf, $fh, @open_args) = @_;
|
1101
|
|
|
|
|
|
|
|
1102
|
|
|
|
|
|
|
if ($fh eq '') {
|
1103
|
|
|
|
|
|
|
$fh = \do { local *HANDLE };
|
1104
|
|
|
|
|
|
|
} else {
|
1105
|
|
|
|
|
|
|
eval{ $fh = *$fh }, !$@ or Object::Hybrid::croak("Not a GLOB reference");
|
1106
|
|
|
|
|
|
|
}
|
1107
|
|
|
|
|
|
|
|
1108
|
|
|
|
|
|
|
$fh->OPEN(@open_args) or Object::Hybrid::croak($!)
|
1109
|
|
|
|
|
|
|
if @open_args;
|
1110
|
|
|
|
|
|
|
|
1111
|
|
|
|
|
|
|
return bless $fh, ref($elf)||$elf
|
1112
|
|
|
|
|
|
|
},
|
1113
|
|
|
|
|
|
|
|
1114
|
|
|
|
|
|
|
OPEN => sub {
|
1115
|
|
|
|
|
|
|
defined $_[0]->FILENO
|
1116
|
|
|
|
|
|
|
and $_[0]->CLOSE;
|
1117
|
|
|
|
|
|
|
|
1118
|
|
|
|
|
|
|
@_ == 2
|
1119
|
|
|
|
|
|
|
? open($_[0], $_[1])
|
1120
|
|
|
|
|
|
|
: open($_[0], $_[1], $_[2]);
|
1121
|
|
|
|
|
|
|
},
|
1122
|
|
|
|
|
|
|
|
1123
|
|
|
|
|
|
|
WRITE2 => sub {
|
1124
|
|
|
|
|
|
|
my $fh = $_[0];
|
1125
|
|
|
|
|
|
|
print $fh substr($_[1],0,$_[2])
|
1126
|
|
|
|
|
|
|
},
|
1127
|
|
|
|
|
|
|
WRITE => sub { my $fh = shift; write $fh },
|
1128
|
|
|
|
|
|
|
PRINT => sub { my $fh = shift; print $fh @_ },
|
1129
|
|
|
|
|
|
|
PRINTF => sub { my $fh = shift; printf $fh @_ },
|
1130
|
|
|
|
|
|
|
|
1131
|
|
|
|
|
|
|
READ => sub { read $_[0], $_[1], $_[2] },
|
1132
|
|
|
|
|
|
|
READLINE => sub { my $fh = $_[0]; <$fh> },
|
1133
|
|
|
|
|
|
|
GETC => sub { getc $_[0] },
|
1134
|
|
|
|
|
|
|
|
1135
|
|
|
|
|
|
|
EOF => sub { eof $_[0] },
|
1136
|
|
|
|
|
|
|
TELL => sub { tell $_[0] },
|
1137
|
|
|
|
|
|
|
FILENO => sub { fileno $_[0] },
|
1138
|
|
|
|
|
|
|
SEEK => sub { seek $_[0], $_[1], $_[2] },
|
1139
|
|
|
|
|
|
|
CLOSE => sub { close $_[0] },
|
1140
|
|
|
|
|
|
|
BINMODE => sub { binmode $_[0] },
|
1141
|
|
|
|
|
|
|
|
1142
|
|
|
|
|
|
|
SYSOPEN => sub {
|
1143
|
|
|
|
|
|
|
eval {
|
1144
|
|
|
|
|
|
|
@_ >= 3 or Object::Hybrid::croak("Not enough arguments for sysopen()");
|
1145
|
|
|
|
|
|
|
@_ == 3 ? sysopen $_[0]->self, $_[1], $_[2] :
|
1146
|
|
|
|
|
|
|
@_ >= 4 ? sysopen $_[0]->self, $_[1], $_[2], $_[3] :();
|
1147
|
|
|
|
|
|
|
};
|
1148
|
|
|
|
|
|
|
!$@ or Object::Hybrid::croak($@);
|
1149
|
|
|
|
|
|
|
},
|
1150
|
|
|
|
|
|
|
FCNTL => sub {
|
1151
|
|
|
|
|
|
|
eval {
|
1152
|
|
|
|
|
|
|
@_ >= 3 or Object::Hybrid::croak("Not enough arguments for fcntl()");
|
1153
|
|
|
|
|
|
|
fcntl $_[0]->self, $_[1], $_[2];
|
1154
|
|
|
|
|
|
|
};
|
1155
|
|
|
|
|
|
|
!$@ or Object::Hybrid::croak($@);
|
1156
|
|
|
|
|
|
|
}, # TODO: same as for SYSOPEN()
|
1157
|
|
|
|
|
|
|
STAT => sub { stat $_[0]->self },
|
1158
|
|
|
|
|
|
|
FLOCK => sub { flock $_[0]->self, $_[1] },
|
1159
|
|
|
|
|
|
|
TRUNCATE => sub { truncate $_[0]->self, $_[1] },
|
1160
|
|
|
|
|
|
|
FTEST => sub {
|
1161
|
|
|
|
|
|
|
my $file = $_[0]->self;
|
1162
|
|
|
|
|
|
|
if ($_[1] =~ /^-\w$/) {
|
1163
|
|
|
|
|
|
|
eval "$_[1] \$file";
|
1164
|
|
|
|
|
|
|
!$@ or Object::Hybrid::croak($@);
|
1165
|
|
|
|
|
|
|
}
|
1166
|
|
|
|
|
|
|
else { Object::Hybrid::croak("Unknown argument to FTEST()") }
|
1167
|
|
|
|
|
|
|
},
|
1168
|
|
|
|
|
|
|
|
1169
|
|
|
|
|
|
|
});
|
1170
|
|
|
|
|
|
|
|
1171
|
|
|
|
|
|
|
#sub DESTROY;
|
1172
|
|
|
|
|
|
|
#sub UNTIE;
|
1173
|
|
|
|
|
|
|
sub DESTROY {}
|
1174
|
|
|
|
|
|
|
|
1175
|
|
|
|
|
|
|
CLASS
|
1176
|
|
|
|
|
|
|
|
1177
|
|
|
|
|
|
|
1;
|
1178
|
|
|
|
|
|
|
|