File Coverage

blib/lib/Object/Hybrid.pm
Criterion Covered Total %
statement 270 453 59.6
branch 120 312 38.4
condition 93 315 29.5
subroutine 37 66 56.0
pod 4 35 11.4
total 524 1181 44.3


line stmt bran cond sub pod time code
1             package Object::Hybrid;
2            
3             #use 5.006;
4            
5 1     1   20684 use strict qw[vars subs];
  1         2  
  1         227  
6             $Object::Hybrid::VERSION = '0.07';
7            
8             $Object::Hybrid::Class::VERSION = '0';
9             $Object::Hybrid::HASH::VERSION = '0';
10             $Object::Hybrid::ARRAY::VERSION = '0';
11             $Object::Hybrid::GLOB::VERSION = '0';
12             $Object::Hybrid::SCALAR::VERSION = '0';
13            
14             =head1 NAME
15            
16             Object::Hybrid - promote Perl primitives (hashes, scalars, arrays, and filehandles), either tie()d or not, to become hybrid objects
17            
18             =head1 WARNING
19            
20             Any specific interface that Object::Hybrid exposes may change (as it already did) until version 1.0 is reached.
21            
22             =head1 SYNOPSIS
23            
24             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.
25            
26             Promote $primitive to become hybrid object:
27            
28             use Object::Hybrid qw(promote); # declare promote() for use
29             promote $primitive;
30            
31             Or (especially if you prefer to not export anything) use new() constructor...
32            
33             use Object::Hybrid;
34             $hybrid = new Object::Hybrid $primitive; # $primitive becomes hybrid object
35             $hybrid = Object::Hybrid->new($primitive); # same
36            
37             NOTE: tie()d primitive must be tie()d before promote(). If it needs to be tie()d later, either re-promote() it or use tieable => 1 argument to promote() (see L).
38            
39             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:
40            
41             $primitive->{foo};
42             $primitive->FETCH('foo'); # same
43             $primitive->fetch('foo'); # same
44            
45             Also, in case of tie()d primitive instead of:
46            
47             tied(%$primitive)->method();
48            
49             just be tied()less:
50            
51             $primitive->method();
52            
53             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:
54            
55             tied(%$primitive) ?
56             tied(%$primitive)->FETCH('foo', @args)
57             : $primitive->{foo};
58            
59             just say:
60            
61             $primitive->FETCH('foo', @args);
62            
63             or faster:
64            
65             $primitive->fast->FETCH('foo', @args);
66            
67             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 trade-offs involved see L and L sections.
68            
69             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:
70            
71             promote $FH;
72            
73             $FH->stat();
74             $FH->ftest('-X');
75            
76             # same in indirect method notation:
77             STAT $FH;
78             FTEST $FH '-X';
79            
80             =head1 DESCRIPTION
81            
82             Some applications need to accept both plain primitives as well as tie()d primitives with additional (non-perltie) methods and parameters supported by 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 classes and exports promote() function that bless()es Perl's primitives (hash, scalar, array, or filehandle) into either default or user-specified (custom) hybrid classes 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, benefits 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 performance comparison of various interface options see L 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 performance comparison of various interface options see L and L 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) 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).
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 \@feature; # enables array of named features
166             use Object::Hybrid \%options; # most general form
167             use Object::Hybrid %options; # most general form
168            
169             The following features are supported:
170            
171             use Object::Hybrid 'promote';
172             use Object::Hybrid feature => 'promote'; # same
173             use Object::Hybrid feature => ['promote']; # same
174             use Object::Hybrid export => 'promote'; # same
175             use Object::Hybrid export => ['promote']; # same
176            
177             which exports (i.e. declares for use) the promote() function into caller's namespace.
178            
179             Next features depend on autobox pragma being installed (can be installed from CPAN archive):
180            
181             use Object::Hybrid 'autobox';
182             use Object::Hybrid feature => 'autobox'; # same
183             use Object::Hybrid feature => ['autobox']; # same
184             use Object::Hybrid autobox => Object::Hybrid->CLASS; # same, but can be custom hybrid class
185            
186             which will automatically promote() any primitive within the current scope, and "unpromote" them back beyond that scope. It is is equivalent to:
187            
188             use Object::Hybrid;
189             use autobox
190             HASH => Object::Hybrid->HASH_STATIC,
191             SCALAR => Object::Hybrid->SCALAR_STATIC,
192             ARRAY => Object::Hybrid->ARRAY_STATIC;
193            
194             And closely related is:
195            
196             use Object::Hybrid 'autopromote';
197             use Object::Hybrid feature => 'autopromote'; # same
198             use Object::Hybrid feature => ['autopromote']; # same
199             use Object::Hybrid autopromote => Object::Hybrid->CLASS; # same, but can be custom hybrid class
200            
201             which makes any method call on non-blessed primitive in the lexical scope to automatically promote() that primitive.
202            
203             =head1 promote() function
204            
205             promote $primitive; # bless() $primitive to make it hybrid object
206             promote $primitive => \%args; # same, but with named arguments
207             promote $primitive => %args; # same
208             promote $primitive => $class; # same, but with explicit $class to tie() to or bless() into
209             promote $primitive => $class, \%args; # same, but with named arguments
210             promote $primitive => $class, %args; # same
211            
212             The function promote() is an exportable constructor of hybrid objects.
213            
214             In case $primitive is tied(), the tied() object is used as object interface of the hybrid (see L), unless custom hybrid $class is specified (see L).
215            
216             In any case, promote() never (re)tie()s primitive, only bless()es it.
217            
218             Exception is raised if $primitive specified is of type not currently supported by Object::Hybrid, no or not defined $primitive is specified.
219            
220             The return value is $primitive itself bless()ed to become a hybrid object (return value may be useful if promote() is used in expressions).
221            
222             The class that $primitive is bless()ed into is generated based on the type of primitive, whether it is tied(), custom $class and "mutable" option, if any are specified. User should not assume anything about resulting ref($primitive) class, except:
223            
224             Object::Hybrid->Class->is(ref($primitive));
225             ref($primitive)->isa($class); # in case $class was specified
226            
227             If promote() is called on a hybrid object, it gets re-bless()ed according to arguments provided. This may be useful for performance optimization in some cases, since performance depends on the way primitive is promote()d.
228            
229             If promote() is called on non-hybrid object, the exception is raised, except when rebless => 1 option is provided. Later essentially is a "doorway" for converting objects into hybrids in rare case it may be needed (almost always a bad idea). (One such likely case is promote()ing some hybrid object incompatible with Object::Hybrid, e.g. IO::Object, using its class as custom hybrid class.)
230            
231             The "tieable" boolean option specifies whether untied primitive can be tie()d after promote(). If primitive gets tie()d, its object interface immediately changes accordingly. However, for (much) better performance it is recommended to re-promote() such hybrid after tie()ing it instead of using tieable => 1 option (unless promote() cost is an issue).
232            
233             The "mutable" boolean option commands whether the tied or tieable (tieable => 1) hybrid can be untie()d or re-tie()d after it has been tied. If tieable hybrid gets tie()d or untie()d, its object interface immediately changes accordingly. However, for (much) better performance it is recommended to re-promote() such hybrid after untie()ing or re-tie()ing it instead of using tieable => 1 option (unless promote() cost is an issue).
234            
235             The "caller" boolean option commands whether the method calls on tied or tieable (tieable => 1) hybrid preserve caller(). This may need to be enabled in rare case that methods of unaware tied class (may) rely on caller(). By default, for hybrids that neither "mutable" (caller may not be preserved) nor "caller" (caller is always preserved) were set true upon promote(), the global $Object::Hybrid::CALLER = 1 can be used to preserve caller. In later case local($Object::Hybrid::CALLER) allows to exercise fine-granularity control over whether caller() is preserved for specific methods:
236            
237             {
238             local $Object::Hybrid::CALLER = 1;
239             $hybrid->foo();
240             }
241            
242             Note that use of either "mutable", "tieable" (in case of non-tied primitives), "caller" options or any combination of them may (some combinations - significantly) reduce performance of hybrid object interface.
243            
244             The tie => $value option can be used to initialize tied() attribute of the hybrid object - see tied() attribute.
245            
246             =head2 Custom hybrid $class
247            
248             If custom hybrid $class is specified, then $primitive is bless()ed into hybrid class that inherits from $class.
249            
250             If and only if $class overrides methods of the hybrid object, then L become requirements for $class to comply with. Otherwise there are no requirements for the class, except it should make sense to use it as hybrid class, i.e. according to L class should represent its bless()ed primitive (e.g. IO::Handle and subclasses may well be used as custom hybrid classes). In particular, calling promote() with the $class name of the empty class is equivalent to calling promote() without specifying custom $class, i.e. it will work. For lengthy discussion of hybrid class requirements refer to L.
251            
252             If custom $class is type-specific for given $primitive, the type-conditional expression for $class may need to be used by caller.
253            
254             The alternative to using custom hybrid class is subclassing Object::Hybrid. Usually this way allows much more efficient implementations of hybrids, but it is more involved as as well.
255            
256             =head1 Properties of hybrid objects
257            
258             The following are the properties of hybrid objects:
259            
260             =head2 Equivalent perltie API
261            
262             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:
263            
264             $hybrid->{foo};
265             $hybrid->FETCH('foo'); # same
266            
267             For performance comparison of various interface options see L section.
268            
269             =head2 Complete perltie API
270            
271             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:
272            
273             promote $FH;
274            
275             $FH->stat();
276             $FH->ftest('-X');
277            
278             # same with indirect method calls...
279             STAT $FH;
280             FTEST $FH '-X';
281            
282             Thus, to avoid problems with gaps in tiehandle implementation simply always call samename methods on hybrids instead of corresponding built-in functions.
283            
284             =head2 Delegation to tied() object
285            
286             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):
287            
288             $hybrid->STORE($value, @args);
289            
290             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.
291            
292             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.
293            
294             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.
295            
296             Note that delegation for standard perltie methods almost always works (no fallback), because normally tieclass does implement perltie methods.
297            
298             =head2 Method aliases
299            
300             The promote()ing of primitive to become hybrid object provides altered-case aliases for all its methods (including lowercase aliases for all perltie methods).
301            
302             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:
303            
304             seek $FH, 0, 0; # function call (coma after $FH, no extra arguments)
305             SEEK $FH 0, 0, @args; # indirect method call (no coma after $FH, @args extended interface)
306             $FH->seek(0, 0, @args); # direct method call (@args extended interface)
307            
308             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.
309            
310             Setting C (local()ized to some scope) 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 is an optional feature that should be used with care, after first testing code without it.
311            
312             In contrast, upper-case aliases are not similarly fail-safe under C, 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:
313            
314             {
315             local $Object::Hybrid::Portable = 1;
316            
317             $hybrid->non_existing_method(); # will not fail due to "can't find method"
318             $hybrid->NON_EXISTING_METHOD(); # fatal "can't find method" error
319            
320             $hybrid->maybe_existing_method(); # will not fail due to "can't find method"
321             $hybrid->MAYBE_EXISTING_METHOD(); # may be a fatal "can't find method" error
322            
323             $filehandle_hybrid->fetch(); # will not fail due to "can't find method"
324             $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
325             }
326            
327             =head2 call()
328            
329             $hybrid->call(method => @args);
330            
331             is the short form of:
332            
333             {
334             local $Object::Hybrid::Portable = 1;
335             $hybrid->method(@args);
336             }
337            
338             Except in case of call() &$method sees real caller one level deeper in the stack (which may be unexpected by methods that use caller()) and character case of method()'s name is irrelevant.
339            
340             =head2 fast()
341            
342             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:
343            
344             $hybrid->fast->FETCH('a'); # for tied() $hybrid is much faster than...
345             $hybrid->FETCH('a');
346            
347             $hybrid->fast->can('foo'); # for tied() $hybrid is much faster than...
348             $hybrid->can('foo');
349            
350             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.
351            
352             =head2 self() method
353            
354             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.
355            
356             Many tied() objects (like Tie::ExtraHash) transparently delegate operations on tie()d primitive to real primitive encapsulated somewhere inside that tied() object, using that object just to store some additional state. If this is the case, tied() class may define self() as accessor for that underlying primitive to directly 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).
357            
358             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.
359            
360             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.
361            
362             For example, since there is no yet perltie support for stat(), -X tests called on tiehandle 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:
363            
364             promote $FH;
365            
366             stat $FH->self;
367             -X $FH->self;
368            
369             # or nearly same using methods (default implementations of these methods also use self() under the hood):
370             STAT $FH;
371             FTEST $FH '-X';
372             $FH->stat();
373             $FH->ftest('-X');
374            
375             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.
376            
377             The Hybrid::Object makes use of self() only if it is defined by tieclass of tie()d primitive, so that hybrids do not depend on tieclass implementing self() method.
378            
379             =head2 tied() method
380            
381             Returns what built-in tied() would return called on hybrid. The only difference is that it is a method and reference type of the hybrid need not to be known.
382            
383             =head2 Optional bless() method
384            
385             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.
386            
387             =head2 C<< Object::Hybrid->Class >>
388            
389             Hybrid objects can be recognized with the following test:
390            
391             Object::Hybrid->Class->is($hybrid);
392            
393             =head1 new() method
394            
395             $hybrid = new Object::Hybrid $primitive; # bless() to make $primitive a hybrid
396             $hybrid = new Object::Hybrid $primitive => \%args; # same, but with named arguments
397             $hybrid = new Object::Hybrid $primitive => %args; # same
398             $hybrid = new Object::Hybrid $primitive => $class; # same, but with explicit $class to tie() to or bless() into
399             $hybrid = new Object::Hybrid $primitive => $class, \%args; # same, but with named arguments
400             $hybrid = new Object::Hybrid $primitive => $class, %args; # same
401            
402             Or corresponding direct method call notation for any of the above can be used, for example:
403            
404             $hybrid = Object::Hybrid->new($primitive); # etc.
405            
406             The new() constructor promote()s $primitive to hybrid and returns it. It is roughly equivalent to:
407            
408             sub new { shift; return promote(@_) }
409            
410             Refer to promote() documentation.
411            
412             Note that new() do not construct object of Object::Hybrid class, even not $hybrid->isa('Object::Hybrid'), so beware.
413            
414             =head1 tie() method
415            
416             $tied = Object::Hybrid->tie( $primitive, $tieclass, @args); # for %$primitive same as...
417             $tied = tie(%$primitive, $tieclass, @args); # ... except $primitive also gets promote()d to hybrid
418            
419             =head1 Class() method
420            
421             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.
422            
423             package Foo;
424             use Object::Hybrid::Class; # tags Foo as standalone hybrid class
425            
426             Object::Hybrid->Class eq 'Object::Hybrid::Class'; # true
427             Object::Hybrid->Class->is('Foo'); # true
428             Object::Hybrid->Class->is('Bar'); # false
429            
430             =head1 is() method
431            
432             The is () is an equivalent of Object::Hybrid->Class->is():
433            
434             promote $hybrid;
435             Object::Hybrid->is( $hybrid); # true
436             Object::Hybrid->Class->is( $hybrid); # same
437             Object::Hybrid->is( $not_hybrid); # false
438             Object::Hybrid->Class->is($not_hybrid); # same
439            
440             =head1 ref_*() methods
441            
442             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:
443            
444             =head2 ref_type() method
445            
446             Object::Hybrid->ref_type({}) eq 'HASH'; # true
447             Object::Hybrid->ref_type(bless {}, 'Foo') eq 'HASH'; # true
448            
449             and so on...
450            
451             =head2 ref_isa() method
452            
453             $obj = bless {}, 'Foo';
454             Object::Hybrid->ref_isa($obj); # true
455             Object::Hybrid->ref_isa($obj) eq $obj; # true
456             Object::Hybrid->ref_isa($obj, 'Foo') eq $obj; # true
457             Object::Hybrid->ref_isa($obj, 'Bar'); # false
458             Object::Hybrid->ref_isa({}); # false
459            
460             and so on...
461            
462             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.
463            
464             =head2 ref_tied() method
465            
466             $tied = tie %$primitive, 'Foo';
467             Object::Hybrid->ref_tied($primitive) eq $tied; # true
468             Object::Hybrid->ref_tied({}) eq '0'; # true
469             Object::Hybrid->ref_tied(sub{}) eq ''; # true, since sub{} is not tie()able
470             Object::Hybrid->ref_tied('string') eq ''; # true, since 'string' is not tie()able
471            
472             and so on.
473            
474             =head1 Perltie classes
475            
476             Hybrid objects are out of the box compatible with any valid tieclass.
477            
478             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 and L. 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.
479            
480             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.
481            
482             =head1 Operator overloading
483            
484             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.
485            
486             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).
487            
488             =head1 Performance
489            
490             The performance preferences for hash hybrids are (in order from fastest to slowest, all figures are quoted for CPU-constrained virtual server, so only their comparison with each other matters):
491            
492             $non_blessed->{foo} # ~ 1_700_000/s
493             $nontied_hybrid->{foo}; # ~ 1_700_000/s
494             $nontied_hybrid->FETCH('foo'); # ~ 300_000/s
495             tied(%$tied_hybrid)->FETCH('foo'); # ~ 230_000/s
496             $nontied_hybrid->fast->FETCH('foo'); # ~ 200_000/s (slower despite fast())
497             $tied_hybrid->fast->FETCH('foo'); # ~ 150_000/s
498             $tied_hybrid->FETCH('foo'); # ~ 80_000/s
499             $tied_hybrid->{foo}; # ~ 60_000/s
500            
501             For comparison, the promote() performance (all including DESTROY() cost):
502            
503             bless $primitive => $hybrid_class # ~ 80_000/s (this is fast analog of promote())
504             promote($primitive) # ~ 35_000/s
505             promote($primitive => $class) # ~ 21_000/s
506            
507             These results are produced by bench/benchmark_hash.pl script available in the Object::Hybrid distribution, and assume default hybrid classes. Figures for promote($primitive => $class) realistically assume same $class value is used in benchmarked calls (otherwise it will be significantly slower), and also include DESTROY() costs. For fast promote()ing of same type of primitives, simply bless() to proper hybrid class:
508            
509             my $hybrid_class = ref promote({});
510             bless {}, $hybrid_class foreach (1..1000000);
511            
512             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:
513            
514             tied(%$primitive) ?
515             tied(%$primitive)->FETCH('foo') # ~230_000/s
516             : $primitive->{foo}; # ~1_700_000/s
517            
518             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.
519            
520             For hybrid interfaces performance varies widely depending on whether it is a tied or non-tied respectively:
521            
522             $hybrid->{foo}; # ~ 60_000 - 1_700_000/s
523             $hybrid->FETCH('foo'); # ~ 80_000 - 300_000/s
524             $hybrid->fast->FETCH('foo'); # ~ 150_000 - 200_000/s
525            
526             Consequently, use of any of these should be decided based on the projected use mix of tied vs. non-tied primitives.
527            
528             =head1 TODO
529            
530             Currently tests cover only tiehashes and tiehandles, there should be tests for other types as well.
531            
532             As soon as (and if) Object::Hybrid interface stabilizes enough, its version is to jump to 1.0.
533            
534             =head1 SEE ALSO
535            
536             The C pragma is another way of doing somewhat similar, but not the same things, so autobox and Object::Hybrid are not mutually substitutive. However, if C pragma is installed, then either "autobox" or "autopromote" feature can be enabled - see L.
537            
538             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.
539            
540             =head1 SUPPORT
541            
542             Send bug reports, patches, ideas, suggestions, feature requests or any module-related information to L. They are welcome and each carefully considered.
543            
544             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.
545            
546             If you have examples of a neat usage of Object::Hybrid, drop a line too.
547            
548             =head1 AUTHOR
549            
550             Alexandr Kononoff (L)
551            
552             =head1 COPYRIGHT AND LICENSE
553            
554             Copyright (c) 2010 Alexandr Kononoff (L). All rights reserved.
555            
556             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:
557            
558             Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
559            
560             * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
561             * 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.
562            
563             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.
564            
565             =cut
566            
567 1     1   4 no warnings;
  1         8  
  1         28  
568            
569 1     1   4 use Scalar::Util qw(reftype refaddr weaken blessed);
  1         6  
  1         156  
570            
571 1     1   518 use Object::Hybrid::Class (); # Object::Hybrid itself is not a hybrid class
  1         2  
  1         3993  
572            
573             sub HASH_UNTIED () { 'Object::Hybrid::HASH' }
574             sub HASH_STATIC () { 'Object::Hybrid::HASH_STATIC' }
575             sub HASH_STATIC_CALLER () { 'Object::Hybrid::HASH_STATIC_CALLER' }
576             sub HASH_MUTABLE () { 'Object::Hybrid::HASH_MUTABLE' }
577             sub HASH_MUTABLE_CALLER () { 'Object::Hybrid::HASH_MUTABLE_CALLER' }
578            
579             sub SCALAR_UNTIED () { 'Object::Hybrid::SCALAR' }
580             sub SCALAR_STATIC () { 'Object::Hybrid::SCALAR_STATIC' }
581             sub SCALAR_STATIC_CALLER () { 'Object::Hybrid::SCALAR_STATIC_CALLER' }
582             sub SCALAR_MUTABLE () { 'Object::Hybrid::SCALAR_MUTABLE' }
583             sub SCALAR_MUTABLE_CALLER () { 'Object::Hybrid::SCALAR_MUTABLE_CALLER' }
584            
585             sub ARRAY_UNTIED () { 'Object::Hybrid::ARRAY' }
586             sub ARRAY_STATIC () { 'Object::Hybrid::ARRAY_STATIC' }
587             sub ARRAY_STATIC_CALLER () { 'Object::Hybrid::ARRAY_STATIC_CALLER' }
588             sub ARRAY_MUTABLE () { 'Object::Hybrid::ARRAY_MUTABLE' }
589             sub ARRAY_MUTABLE_CALLER () { 'Object::Hybrid::ARRAY_MUTABLE_CALLER' }
590            
591             sub GLOB_UNTIED () { 'Object::Hybrid::GLOB' }
592             sub GLOB_STATIC () { 'Object::Hybrid::GLOB_STATIC' }
593             sub GLOB_STATIC_CALLER () { 'Object::Hybrid::GLOB_STATIC_CALLER' }
594             sub GLOB_MUTABLE () { 'Object::Hybrid::GLOB_MUTABLE' }
595             sub GLOB_MUTABLE_CALLER () { 'Object::Hybrid::GLOB_MUTABLE_CALLER' }
596            
597             sub CLASS_AUTOPROMO () { 'Object::Hybrid::AUTOPROMOTE' }
598             sub FRONTAL () { 'FRONTAL' }
599             sub Class () { 'Object::Hybrid::Class' }
600 77     77 0 270 sub CLASS_DEFAULT () { undef }
601            
602             sub frontclass_name {
603 15     15 0 117 my (undef, $class, $primitive) = @_;
604 15 50 33     85 return join '_', $class, ref $primitive ? reftype($primitive) : $primitive||(), FRONTAL
605             }
606            
607             my %class4type = (
608             HASH => HASH_STATIC,
609             ARRAY => ARRAY_STATIC,
610             SCALAR => SCALAR_STATIC,
611             GLOB => GLOB_STATIC,
612             );
613            
614             my %untied_class4type = (
615             HASH => HASH_UNTIED,
616             ARRAY => ARRAY_UNTIED,
617             SCALAR => SCALAR_UNTIED,
618             GLOB => GLOB_UNTIED,
619             );
620            
621             sub class4type {
622 0     0 0 0 my $self = shift;
623 0         0 my $accessor = $self->name4type(@_);
624 0   0     0 return $self->$accessor||()
625             }
626            
627             sub name4type { # the only required parameter is 'reftype' - can be ref() or reference of corresponding type
628 15     15 0 21 my ($self, $type);
629 15         102 ($self, %$type) = @_;
630            
631 15 50 33     89 push my @accessor
    50          
632             , ref $type->{reftype}
633             ? reftype $type->{reftype}
634             : $type->{reftype}
635             if $type->{reftype}
636             or croak("No 'reftype' parameter specified");
637            
638 15 100       29 if (!$type->{tieable}) {
639 7         16 push @accessor, 'UNTIED';
640             }
641             else {
642 8 50       16 push @accessor, $type->{mutable} ? 'MUTABLE' : 'STATIC';
643 8 50       21 push @accessor, 'CALLER'
644             if $type->{caller};
645             }
646            
647 15         31 my $accessor = join '_', @accessor;
648            
649 15         51 return $accessor
650             }
651            
652             __PACKAGE__->import(qw(promote));
653             sub import {
654 2     2   10 my $self = shift;
655            
656             # interface...
657 2 50       19 my $opt
    50          
    50          
    50          
658             = @_ > 1 ? {@_}
659             : !@_ ? { }
660             : ref $_[0] eq 'HASH' ? $_[0]
661             : ref $_[0] eq 'ARRAY' ? { feature => [$_[0]] }
662             : { feature => [$_[0]] };
663            
664             # normalize %$opt...
665 2         5 foreach my $list (qw(feature export autopromote)) {
666             next
667 6 100       16 if !exists $opt->{$list};
668 2 50       9 ref $opt->{$list} eq 'ARRAY'
669             or $opt->{$list}
670             = [$opt->{$list}];
671             }
672            
673 2         4 my @goto;
674            
675             # process features first...
676 2 50       7 foreach my $feature (ref $opt->{feature} eq 'ARRAY' ? @{$opt->{feature}} : $opt->{feature}) {
  2         4  
677 2 50       6 if ($feature eq 'promote') { push @{$opt->{export}}, $feature }
  2         2  
  2         6  
678            
679             # mutually exclusive features...
680 2 50       11 if ($feature eq 'autobox') {
    50          
    50          
681             #load_type($self->CLASS);
682 0         0 $self->load_type('HASH_STATIC');
683 0         0 $self->load_type('SCALAR_STATIC');
684 0         0 $self->load_type('ARRAY_STATIC');
685            
686             require
687 0         0 autobox;
688 0   0     0 autobox::import( ref($self)||$self,
      0        
      0        
      0        
689             HASH => $opt->{$feature}||$self->HASH_STATIC, # method instead of constant, for subclassing...
690             SCALAR => $opt->{$feature}||$self->SCALAR_STATIC,
691             #GLOB => $opt->{$feature}||$self->GLOB_STATIC, # not supported by autobox
692             ARRAY => $opt->{$feature}||$self->ARRAY_STATIC, );
693             }
694             elsif ($feature eq 'autopromote') {
695             require
696 0         0 autobox;
697 0   0     0 autobox::import( ref($self)||$self,
698             HASH => CLASS_AUTOPROMO,
699             SCALAR => CLASS_AUTOPROMO,
700             #GLOB => CLASS_AUTOPROMO, # not supported by autobox
701             ARRAY => CLASS_AUTOPROMO, );
702 0         0 my $autoload
703             = __PACKAGE__ . '::AUTOLOAD';
704 0         0 *{ CLASS_AUTOPROMO . '::AUTOLOAD' } = sub{
705 0   0 0   0 $self->new($_[0], @{$opt->{$feature}}||());
706 0         0 $$autoload =~ s/^.*:://;
707 0 0       0 goto &{ $_[0]->can($$autoload)
  0         0  
708             or croak(_cant_locate_object_method($_[0], $$autoload)) };
709 0         0 };
710             }
711 0         0 elsif ($feature eq 'SAFE') { $opt->{$feature} = 1; }
712             }
713            
714             # process options...
715 2 50       7 if ($opt->{export}) {
716 2         3 my @symbols;
717 2 50       2 foreach my $symbol (@{$opt->{export}||[]}) {
  2         8  
718 2 50       53 if ( $symbol eq 'promote' ) {
719 2         13 my $promote = $self->can(qw(new));
720 2         13 *{join '::', scalar caller, $symbol}
721             #= sub{ unshift @_, $self; goto &$promote };
722 2     52   21 = sub{ $self->$promote(@_) };
  52         409081  
723             }
724 0         0 else { push @symbols, $symbol }
725             }
726            
727 2 50 33     9 if (@symbols
      33        
728 2   33     20 or @{(ref($self)||$self).'::EXPORT'}
  2         8  
729             or @{ __PACKAGE__.'::EXPORT'}) {
730             require
731 0         0 Exporter;
732 0 0       0 Exporter::export_to_level(1, $self, @symbols) or
733             Exporter::export_to_level(1, __PACKAGE__, @symbols); # "inheritance" of export, subclasses can define their own @EXPORTs,
734             }
735             }
736            
737 2 50       21 if (@goto) {
738 0         0 @_ = @goto;
739 0         0 goto &{shift(@_)};
  0         0  
740             }
741             }
742            
743 54     54 0 272 sub is { Object::Hybrid::Class->is($_[1]) }
744            
745             my %PromoclassCache;
746             my %PromoclassCache2;
747             my %PromoclassCache3;
748             my %HybridClass;
749             my %DefaultClass;
750            
751             *promote = \&new; # method, not function
752             sub new {
753 113 100   113 0 16012 my $primitive_is_tied = (
754             my $primitive_tied = _ref_tied($_[1]) ) ? 1 : 0;
755            
756 113 100 100     235 { return bless $_[1]
  113         462  
757             , $PromoclassCache2{ref $_[1]}->[$primitive_is_tied]||next
758             if @_ == 2; } # fast shortcircuit
759            
760 107 50       233 @_>1 or croak("Error: Nothing to promote");
761            
762 107         141 my $self = shift;
763 107         134 my $primitive = $_[0];
764            
765 107         117 my ($class, $args);
766             ( undef
767 107 100       626 , ref $_[1] eq 'HASH' ? ( $args)
    50          
    50          
768             : ref $_[2] eq 'HASH' ? ($class, $args)
769             : @_ % 2 ? %$args
770             : ($class, %$args) ) = @_;
771            
772 107   33     1017 $class ||= ($DefaultClass{ref($self)||$self} ||= $self->CLASS_DEFAULT);
      33        
      66        
773            
774 107 100 100     123 { return bless $primitive
  107         593  
775             , $PromoclassCache3{ref $primitive}->{$class}[$primitive_is_tied]||next
776             if !%$args; } # slower shortcircuit
777            
778 101 100 100     387 my $hybrid_is_tieable = $args->{tieable}||$primitive_is_tied ? 1 : 0;
779 101 50       210 my $hybrid_is_mutable = $args->{mutable} ? 1 : 0;
780 101 50       183 my $hybrid_is_caller = $args->{caller} ? 1 : 0;
781 101 50 66     240 my $no_opt = 1
      33        
782             if !$hybrid_is_tieable
783             and !$hybrid_is_mutable
784             and !$hybrid_is_caller;
785            
786 101   100     96 { return bless $primitive
  101         834  
787             , $PromoclassCache{ref $primitive}->{$class}[$hybrid_is_tieable][
788             $hybrid_is_mutable][
789             $hybrid_is_caller ]||next } # slower shortcircuit
790            
791 15 50       41 ref $primitive or croak("Can't promote() non-reference value $primitive");
792 15 50 66     133 !blessed($primitive) or $args->{rebless}
      33        
      66        
793             or $HybridClass{ref $primitive }
794             and delete $Object::Hybrid::Cache4object{refaddr($primitive)}, 1
795             or croak("Can't promote() object $primitive");
796            
797 15         33 my $primitive_ref = ref $primitive;
798 15         42 my $primitive_reftype = reftype($primitive);
799            
800 15 50       38 $class4type{$primitive_reftype}
801             or croak("Error: promote()ing $primitive_reftype primitive is not supported");
802            
803 15         17 my $frontclass;
804 15         57 my $typename = $self->name4type(
805             reftype => $primitive_reftype,
806             tieable => $hybrid_is_tieable,
807             mutable => $hybrid_is_mutable,
808             caller => $hybrid_is_caller,
809             );
810 15         45 my $typeclass = $self->$typename;
811 15 100       31 if ($class) {
812 9 50       54 unless ( $self->Class->is($class) ) { # use $class as subclass...
813 9         26 $frontclass = $self->frontclass_name($class, $primitive_reftype);
814 3         60 @{ $frontclass . '::ISA' } or
  9         60  
815 9 100 33     14 @{ $frontclass . '::ISA' } = ( $class, $typeclass||() );
816             }
817 6         9 } else { $frontclass = $typeclass; }
818            
819             # custom hybrid class may subclass $typeclass or subclass of Object::Hybrid may override $typename() methods, so call load_type() anyway
820 15         52 $self->load_type($typename);
821            
822             my
823 15         222 $bless = $frontclass->can('bless');
824 15 50       54 $bless ? $frontclass->$bless($primitive)
825             : bless $primitive, $frontclass;
826            
827 15         35 $HybridClass{ ref $primitive} = ref $primitive;
828 15 100 100     90 $PromoclassCache2{$primitive_ref}->[ $primitive_is_tied] = ref $primitive if $no_opt and $class eq '';
829 15 100       44 $PromoclassCache3{$primitive_ref}->{$class}[$primitive_is_tied] = ref $primitive if $no_opt;
830 15         57 $PromoclassCache{ $primitive_ref}->{$class}[$hybrid_is_tieable][
831             $hybrid_is_mutable][
832             $hybrid_is_caller ] = ref $primitive;
833            
834 15 50       46 $primitive->shadow($args->{shadow})
835             if exists $args->{shadow};
836            
837 15         81 return $primitive
838             }
839            
840             sub tie {
841             return undef
842 12 50   12 1 11095 if !ref $_[1];
843            
844 12         100 my $tied
845 0         0 = reftype($_[1]) eq 'HASH' ? tie( %{$_[1]}, @_[2..$#_] ) # $_[1] =~ m'(?:^|=)HASH'
846 0         0 : reftype($_[1]) eq 'SCALAR' ? tie( ${$_[1]}, @_[2..$#_] ) # $_[1] =~ m'(?:^|=)SCALAR'
847 0         0 : reftype($_[1]) eq 'ARRAY' ? tie( @{$_[1]}, @_[2..$#_] ) # $_[1] =~ m'(?:^|=)ARRAY'
848 12 0       68 : reftype($_[1]) eq 'GLOB' ? tie( *{$_[1]}, @_[2..$#_] ) # $_[1] =~ m'(?:^|=)GLOB'
    0          
    0          
    50          
    50          
849             : undef
850             or return undef;
851            
852 12         113 Object::Hybrid->new($_[1]);
853 12         24 return $tied
854             }
855            
856             my %Loaded;
857             sub load_type {
858 17 100   17 0 49 $Loaded{$_[1]} and return undef;
859 4         11 $Loaded{$_[1]} = 1; # immediately - to avoid infinite recursion
860            
861 4         7 my ($self, $type) = @_;
862            
863 4 50       34 my $sub_type
    50          
864             = !ref $type ? $self->can("LOAD_$type")
865             : croak("Bad type $type")
866             or croak("No $type() method defined");
867            
868 4         14 my $class = $self->$sub_type($type);
869 4 50       13 $class = $self->$class( $type) if ref $class eq 'CODE';
870 4 50       9 !ref $class or croak("Unrecorgnized value '$class' returned by $type()");
871 4 50 33     201 ! $class or eval($class), !$@ or croak("Error: Can't eval() $type(): $@");
872            
873 4         12 return 1
874             }
875            
876 0     0 1 0 sub ref_tied { shift; _ref_tied(@_) }
  0         0  
877             sub _ref_tied2 { # this is much faster at least for tied hashes, but for non-tied tries all variants (still may be faster)
878 0 0   0   0 return undef if !ref $_[0];
879             return eval{ tied( %{$_[0]} ) }
880             || eval{ tied( ${$_[0]} ) }
881             || eval{ tied( @{$_[0]} ) }
882 0   0     0 || eval{ tied( *{$_[0]} ) }
883             || undef
884             }
885             sub _ref_tied {
886             #return undef if !ref $_[0]; # unlikely to be called on non-reference
887             return reftype($_[0]) eq 'HASH' ? tied( %{$_[0]} )||0 # $_[0] =~ m'(?:^|=)HASH'
888             : reftype($_[0]) eq 'SCALAR' ? tied( ${$_[0]} )||0 # $_[0] =~ m'(?:^|=)SCALAR'
889             : reftype($_[0]) eq 'ARRAY' ? tied( @{$_[0]} )||0 # $_[0] =~ m'(?:^|=)ARRAY'
890 113 50 100 113   504 : reftype($_[0]) eq 'GLOB' ? tied( *{$_[0]} )||0 # $_[0] =~ m'(?:^|=)GLOB'
    50 0        
    50 0        
    100 100        
891             : undef
892             }
893            
894 0     0 1 0 sub ref_type { reftype($_[1]) }
895             #sub ref_type { shift; reftype(@_) }
896             sub _ref_type {
897 0 0   0   0 return undef if !ref $_[0];
898 0 0       0 return $1 if $_[0] =~ /=(\w+)/;
899 0         0 return ref $_[0]
900             }
901            
902 0     0 1 0 sub ref_isa { shift; _ref_isa(@_) }
  0         0  
903             sub _ref_isa {
904 0 0   0   0 return undef if !ref $_[0];
905 0 0       0 return '' if exists $class4type{ref $_[0]};
906 0 0 0     0 return 0 if defined $_[1] and !$_[0]->isa($_[1]);
907 0         0 return $_[0]
908             }
909            
910 10     10 0 78 sub croak { require Carp; goto &Carp::croak; }
  10         1530  
911            
912 135 100   135   2363 sub _alter_case { $_[0] =~ /[A-Z]/ ? lc($_[0]) : uc($_[0]) };
913 0     0 0 0 sub method_alias { _alter_case($_[1]) }
914            
915             sub methods {
916 7     7 0 50 shift;
917 7         10 my $subs;
918 7 100       38 ref $_[0] eq 'HASH' ? ($subs) : %$subs = @_;
919 7         14 my $caller = caller;
920 7         27 foreach my $method (keys %$subs) {
921             # explicit aliases...
922 56 50 33     142 ref $subs->{$method} eq 'CODE'
923             or ref( $subs->{$method}
924             = $subs->{$subs->{$method}}) eq 'CODE'
925             or next;
926            
927             # implicit altered-case aliases...
928 56         73 my $method2 = _alter_case($method);
929 56         51 my $goto;
930 56         265 *{join '::', $caller, $method } =
  56         245  
931 56         64 *{join '::', $caller, $method2} = $subs->{$method};
932            
933             }
934             }
935            
936             sub _cant_locate_object_method {
937 10   33 10   93 join '', "Object::Hybrid: Can't locate object method \""
      33        
938             , $_[1], "\" via package \""
939             , ref($_[0])||$_[0], "\" (perhaps you forgot to load \""
940             , ref($_[0])||$_[0], "\"?) "
941             }
942            
943             {
944             package Object::Hybrid::BASE;
945            
946             Object::Hybrid->methods(
947             shadow => sub {
948 0 0   0   0 return $Object::Hybrid::Shadow4object{refaddr($_[0])} = $_[1] if @_ > 1;
949 0         0 return $Object::Hybrid::Shadow4object{refaddr($_[0])};
950             },
951             call => sub{
952             package Object::Hybrid;
953 2 50   2   10 @_ > 1 or croak("Error: Nothing to call");
954            
955 2         5 local $Object::Hybrid::Portable = 1;
956 2         10 my $method = lc(splice(@_, 1, 1));
957 2         29 return shift->$method(@_) # is ok, except for caller()
958             },
959             );
960            
961             our $AUTOLOAD;
962             sub AUTOLOAD {
963             package Object::Hybrid;
964 16 50 100 16   194 $Object::Hybrid::Portable and (split '::', $AUTOLOAD)[-1] !~ /[A-Z]/ and return # lower-case aliases are fail-safe for compartibility
      100        
965             or croak( _cant_locate_object_method($_[0], $AUTOLOAD) );
966             }
967            
968             sub DESTROY {
969             package Object::Hybrid;
970 25     25   29288 delete $Object::Hybrid::Shadow4object{refaddr($_[0])};
971             return
972 25         144 }
973             }
974            
975             my $CLASS_TEMPLATE = <<'CLASS_TEMPLATE';
976            
977             $INC{ INCKEY_REPLACE } ||= 1;
978             package PACKAGE_REPLACE;
979            
980             our @ISA = 'Object::Hybrid::BASE';
981            
982             use Object::Hybrid::Class; # just labeling
983             use Scalar::Util qw(reftype refaddr);
984            
985             sub can;
986             #sub isa;
987            
988             #sub FETCH { tied(%{$_[0]})->FETCH(@_[1..$#_]) } # 100_000 calls 0.94 CPU sec 106_157/s.
989             #sub FETCH { splice(@_, 0, 1, tied(%{$_[0]})); goto &{ $_[0]->can('FETCH') } } # 100_000 calls 2.08 CPU sec 48_007/s. goto() or plain ->() call are irrelevent - major cost is that of can().
990            
991             Object::Hybrid->methods(
992             tied => sub { TIED_REPLACE },
993             SELF => sub {
994            
995             =This is too much of the guess-work, it is safer to just return $_[0]
996             my $self;
997             return $self
998             if $self
999             = Object::Hybrid::_ref_tied($_[0])
1000             and reftype($_[0])
1001             eq reftype($self)
1002             and ref($self) =~ /Std/;
1003            
1004             !$self or Object::Hybrid::croak("tied() class defines nor valid self() method for $_[0] (self() returned $self)");
1005            
1006             =cut
1007            
1008             return $_[0]
1009             },
1010             fast => sub {
1011             return TIED_REPLACE || $_[0];
1012            
1013             return eval{ tied( %{$_[0]} ) }
1014             || eval{ tied( ${$_[0]} ) }
1015             || eval{ tied( @{$_[0]} ) }
1016             || eval{ tied( *{$_[0]} ) }
1017             || $_[0];
1018            
1019             return $_[0] if !ref $_[0];
1020             my $type = reftype($_[0]);
1021             return $type eq 'HASH' ? tied( %{$_[0]} ) || $_[0]
1022             : $type eq 'SCALAR' ? tied( ${$_[0]} ) || $_[0]
1023             : $type eq 'ARRAY' ? tied( @{$_[0]} ) || $_[0]
1024             : $type eq 'GLOB' ? tied( *{$_[0]} ) || $_[0]
1025             : $_[0];
1026             },
1027             call => sub{
1028             @_ > 1
1029             or Object::Hybrid::croak("Error: Nothing to call");
1030            
1031             local $Object::Hybrid::Portable = 1;
1032             my $method = lc(splice(@_, 1, 1));
1033             return shift->$method(@_) # is ok, except for caller()
1034             },
1035             );
1036            
1037             #my $AUTOLOAD = \&AUTOLOAD;
1038             sub AUTOLOAD {
1039             package Object::Hybrid; # to not qualify _ref_tied(), reftype(), croak(), etc...
1040            
1041             ( my $METHOD = $PACKAGE_REPLACE::AUTOLOAD ) =~ s/^.*:://;
1042             my $METHOD_is_lc = ($METHOD !~ /[A-Z]/);
1043             my $METHOD2 = _alter_case($METHOD);
1044             my $SUB_METHOD;
1045            
1046             my $FALLBACK_METHOD;
1047            
1048             my $SUB_METHOD_NAME = join '::', ref($_[0])||$_[0], $METHOD;
1049             my $SYMTABLE = \%{(ref($_[0])||$_[0]) . '::'};
1050            
1051             goto &{ *$SUB_METHOD_NAME
1052             = $SUB_METHOD
1053             = $METHOD eq 'can'
1054             ? sub{
1055            
1056             my $err;
1057             my $can_method2;
1058             my $can_method = $_[1];
1059             my $sub_method = ($_[0])->UNIVERSAL::can($can_method)
1060             || ($_[0])->UNIVERSAL::can($can_method2
1061             = _alter_case($can_method));
1062             eval{ $sub_method ||= (TIED_REPLACE||$_[0])->UNIVERSAL::can($can_method)
1063             or $sub_method ||= (TIED_REPLACE||$_[0])->UNIVERSAL::can($can_method2) }; $err=$@;
1064             eval{ $sub_method ||= (SUBCLASS_REPLACE->UNIVERSAL::can($can_method )
1065             || SUBCLASS_REPLACE->UNIVERSAL::can($can_method2)) };
1066            
1067             croak("BUG in can() of mutable hybrid class: " . $err||$@) if $err||$@;
1068            
1069             return $sub_method
1070             }
1071             : IS_MUTABLE_REPLACE && !KEEP_CALLER_REPLACE
1072             ? sub{
1073             my $err;
1074             { return wantarray
1075             ? ( eval{ TIED_REPLACE->$METHOD(@_[1..$#_]) } , $@ ? next :() )
1076             : ( scalar(eval{ TIED_REPLACE->$METHOD(@_[1..$#_]) }), $@ ? next :() )[0] };
1077            
1078             { return wantarray
1079             ? ( eval{ TIED_REPLACE->$METHOD2(@_[1..$#_]) } , $@ ? next :() )
1080             : ( scalar(eval{ TIED_REPLACE->$METHOD2(@_[1..$#_]) }), $@ ? next :() )[0] };
1081            
1082             { return wantarray
1083             ? ( eval{ ($FALLBACK_METHOD ||= SUBCLASS_REPLACE->UNIVERSAL::can($METHOD)
1084             || die("Cannot find method $METHOD() via $_[0]"))->(@_) } , $@ ? next :() )
1085             : ( scalar( eval{ ($FALLBACK_METHOD ||= SUBCLASS_REPLACE->UNIVERSAL::can($METHOD)
1086             || die("Cannot find method $METHOD() via $_[0]"))->(@_) }), $@ ? next :() )[0] };
1087            
1088             $err = $@;
1089            
1090             { return wantarray
1091             ? ( eval{ ($FALLBACK_METHOD ||= SUBCLASS_REPLACE->UNIVERSAL::can($METHOD2)
1092             || die("Cannot find method $METHOD() via $_[0]"))->(@_) } , $@ ? next :() )
1093             : ( scalar( eval{ ($FALLBACK_METHOD ||= SUBCLASS_REPLACE->UNIVERSAL::can($METHOD2)
1094             || die("Cannot find method $METHOD() via $_[0]"))->(@_) }), $@ ? next :() )[0] };
1095            
1096             $err ||= $@;
1097            
1098             delete $SYMTABLE->{$METHOD};
1099            
1100             $METHOD_is_lc and $Object::Hybrid::Portable and return; # lower-case aliases are fail-safe for compartibility
1101             #or croak( _cant_locate_object_method($_[0], $METHOD) );
1102            
1103             croak($err||"Failed to call $METHOD() on $_[0] (this is a BUG)")
1104             }
1105             : sub{
1106            
1107             my $cache = ($Object::Hybrid::Cache4object{refaddr($_[0])}->{$METHOD} ||= []) unless IS_MUTABLE_REPLACE;
1108            
1109             KEEP_CALLER_REPLACE || $Object::Hybrid::CALLER
1110             ? ( splice(@_, 0, 1, $cache->[0] )
1111             , goto &{$cache->[1]} ) # about twice slower, but preserves caller()
1112             : ( shift @_, return $cache->[1]->(
1113             $cache->[0], @_) )
1114             if !IS_MUTABLE_REPLACE and @$cache;
1115            
1116             my $sub_method;
1117             {
1118             my
1119             $swap;
1120             $swap = TIED_REPLACE and
1121             $swap = splice(@_, 0, 1, $swap);
1122            
1123             $swap and $sub_method
1124             = $_[0]->UNIVERSAL::can($METHOD)
1125             || $_[0]->UNIVERSAL::can($METHOD2)
1126             and last;
1127            
1128             $swap and splice(@_, 0, 1, $swap); # revert swap, if any
1129            
1130             $sub_method
1131             = SUBCLASS_REPLACE->UNIVERSAL::can($METHOD)
1132             || SUBCLASS_REPLACE->UNIVERSAL::can($METHOD2)
1133             and last;
1134            
1135             delete $SYMTABLE->{$METHOD};
1136            
1137             $METHOD_is_lc and $Object::Hybrid::Portable and return # lower-case aliases are fail-safe for compatibility
1138             or croak( _cant_locate_object_method($_[0], $METHOD) );
1139             }
1140            
1141             $sub_method ne $SUB_METHOD # this case is hopefully excluded by above logic, but it may screw up
1142             #and defined(&$sub_method) # here goto() to not defined(&method) is ok as it may be autoloadable in tied() class or otherwise
1143             or $sub_method = undef
1144             , croak( join '', "Undefined method \""
1145             , $METHOD, "\" called via package \""
1146             , ref($_[0])||$_[0], "\"");
1147            
1148             $cache->[0] = $_[0],
1149             $cache->[1] = $sub_method,
1150             #weaken($cache->[0]),
1151             #weaken($cache->[1]),
1152             unless IS_MUTABLE_REPLACE;
1153            
1154             goto &$sub_method
1155             } };
1156             }
1157            
1158             sub DESTROY {
1159             #delete $Object::Hybrid::Tied4object{ refaddr($_[0])};
1160             delete $Object::Hybrid::Shadow4object{refaddr($_[0])};
1161             delete $Object::Hybrid::Cache4object{ refaddr($_[0])};
1162             return
1163             }
1164            
1165             CLASS_TEMPLATE
1166            
1167             sub _compile_class {
1168 4     4   8 my ($self, $class_template, $type) = @_;
1169 4         11 $type = uc($type);
1170            
1171 4 50 33     53 $type =~ /(HASH|ARRAY|GLOB|SCALAR)/ and my $reftype = $1 or croak("Bad $type name");
1172            
1173 4         13 my $PACKAGE_REPLACE = $self->$type;
1174 4         25 ( my $INCKEY_REPLACE =
1175             $PACKAGE_REPLACE . '.pm' ) =~ s/::/\//g;
1176 4         8 my $SUBCLASS_REPLACE = "${reftype}_UNTIED";
1177 4 50       15 my $IS_MUTABLE_REPLACE = $type =~ /MUTABLE/ ? 1 : 0;
1178 4 50       13 my $KEEP_CALLER_REPLACE = $type =~ /CALLER/ ? 1 : 0;
1179 4 0       15 my $TIED_REPLACE
    50          
    50          
    100          
1180             = $reftype eq 'HASH' ? 'tied( %{$_[0]} )'
1181             : $reftype eq 'ARRAY' ? 'tied( @{$_[0]} )'
1182             : $reftype eq 'GLOB' ? 'tied( *{$_[0]} )'
1183             : $reftype eq 'SCALAR' ? 'tied( ${$_[0]} )'
1184             : croak("Bad $reftype reftype");
1185            
1186 4 100       16 $self->load_type($SUBCLASS_REPLACE)
1187             unless $type eq $SUBCLASS_REPLACE;
1188            
1189 4         71 $class_template =~ s/PACKAGE_REPLACE/$PACKAGE_REPLACE/g;
1190 4         56 $class_template =~ s/INCKEY_REPLACE/'$INCKEY_REPLACE'/g;
1191 4         48 $class_template =~ s/SUBCLASS_REPLACE/$SUBCLASS_REPLACE/g;
1192 4         44 $class_template =~ s/TIED_REPLACE/$TIED_REPLACE/g;
1193 4         25 $class_template =~ s/IS_MUTABLE_REPLACE/$IS_MUTABLE_REPLACE/g;
1194 4         30 $class_template =~ s/KEEP_CALLER_REPLACE/$KEEP_CALLER_REPLACE/g;
1195 1 50 66 1 0 10 eval $class_template;
  1 50 66 1   2  
  1 50 33 1   11  
  1 100 66 1   6  
  1 50 33 1   2  
  1 100 66 1   303  
  1 50 33 1   6  
  1 100 33 1   2  
  1 0 33 29   5  
  1 50 0 2   5  
  1 100 33 36   1  
  1 100 100 0   1459  
  1 50 100 0   9  
  1 0 66 0   2  
  1 50 66     9  
  1 100 66     14  
  1 100 50     3  
  1 50 0     1157  
  1 50 66     7  
  1 0 66     3  
  1 50 100     11  
  1 100 33     6  
  1 0 33     2  
  1 0 33     1749  
  4 0 66     537  
  29 0 66     180  
  29 0 100     61  
  29 0 0     73  
  29 0 33     42  
  29 0 50     96  
  29 0 33     24  
  29 0 66     101  
  29 0 33     36  
  29 0 0     283  
  3 0 0     5  
  3 0 0     5  
  3 0 0     29  
  3 0 0     5  
  3 0 0     9  
  3 0 0     5  
  3 50 0     4  
  3 0 0     48  
  3 50 0     13  
  3 50 0     42  
  81 0 0     573  
  81 0 0     319  
  0 0 0     0  
  50 0 0     50  
  50 0 0     47  
  50 0 0     56  
  50 0 0     40  
  50 0 0     335  
  50 0 0     583  
  6 0 0     17  
  6 0 0     64  
  4 0 0     10  
  4 0 0     30  
  46 0 0     142  
  46 0 0     90  
  46 0 0     224  
  2 100 0     12  
  2 50 0     5  
  2 0 0     7  
  2 0 0     5  
  2 0 0     8  
  2 0 0     3  
  2 0 0     10  
  2 0 0     4  
  2 50 0     20  
  134 0 0     17691  
  134 0 0     215  
  134 0 0     812  
  134 0 0     211  
  134 0       402  
  134         159  
  134         134  
  134         310  
  134         435  
  134         466  
  42         5992  
  42         101  
  0         0  
  42         140  
  42         42  
  42         51  
  42         46  
  42         206  
  42         396  
  6         15  
  6         68  
  0         0  
  0         0  
  42         149  
  42         93  
  42         295  
  36         125  
  36         150  
  36         241  
  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  
  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  
  4         10  
  4         37  
  4         8  
  4         50  
  4         9  
  4         46  
  12         78  
  10         89  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  4         21  
  6         12  
  6         21  
  6         91  
  6         37  
  0         0  
  0         0  
  0         0  
  0         0  
  12         84  
  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  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  4         35  
  12         380  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  12         304  
  10         91  
  10         303  
  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  
  18         6419  
  0         0  
  0         0  
  0         0  
  2         7  
  2         5  
  2         7  
  2         41  
  0         0  
  0         0  
  4         24  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  12         86  
  0         0  
  0         0  
  0         0  
  10         51  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
1196 4 50       34 !$@ or die($@);
1197             }
1198            
1199 1     1 0 5 sub LOAD_HASH_STATIC { $_[0]->_compile_class($CLASS_TEMPLATE, $_[1]); }
1200 0     0 0 0 sub LOAD_HASH_STATIC_CALLER { $_[0]->_compile_class($CLASS_TEMPLATE, $_[1]); }
1201 0     0 0 0 sub LOAD_HASH_MUTABLE { $_[0]->_compile_class($CLASS_TEMPLATE, $_[1]); }
1202 0     0 0 0 sub LOAD_HASH_MUTABLE_CALLER { $_[0]->_compile_class($CLASS_TEMPLATE, $_[1]); }
1203            
1204 0     0 0 0 sub LOAD_ARRAY_STATIC { $_[0]->_compile_class($CLASS_TEMPLATE, $_[1]); }
1205 0     0 0 0 sub LOAD_ARRAY_STATIC_CALLER { $_[0]->_compile_class($CLASS_TEMPLATE, $_[1]); }
1206 0     0 0 0 sub LOAD_ARRAY_MUTABLE { $_[0]->_compile_class($CLASS_TEMPLATE, $_[1]); }
1207 0     0 0 0 sub LOAD_ARRAY_MUTABLE_CALLER { $_[0]->_compile_class($CLASS_TEMPLATE, $_[1]); }
1208            
1209 1     1 0 6 sub LOAD_GLOB_STATIC { $_[0]->_compile_class($CLASS_TEMPLATE, $_[1]); }
1210 0     0 0 0 sub LOAD_GLOB_STATIC_CALLER { $_[0]->_compile_class($CLASS_TEMPLATE, $_[1]); }
1211 0     0 0 0 sub LOAD_GLOB_MUTABLE { $_[0]->_compile_class($CLASS_TEMPLATE, $_[1]); }
1212 0     0 0 0 sub LOAD_GLOB_MUTABLE_CALLER { $_[0]->_compile_class($CLASS_TEMPLATE, $_[1]); }
1213            
1214 0     0 0 0 sub LOAD_SCALAR_STATIC { $_[0]->_compile_class($CLASS_TEMPLATE, $_[1]); }
1215 0     0 0 0 sub LOAD_SCALAR_STATIC_CALLER { $_[0]->_compile_class($CLASS_TEMPLATE, $_[1]); }
1216 0     0 0 0 sub LOAD_SCALAR_MUTABLE { $_[0]->_compile_class($CLASS_TEMPLATE, $_[1]); }
1217 0     0 0 0 sub LOAD_SCALAR_MUTABLE_CALLER { $_[0]->_compile_class($CLASS_TEMPLATE, $_[1]); }
1218            
1219 1     1 0 7 sub LOAD_HASH_UNTIED { $_[0]->_compile_class(<<'CLASS', $_[1]);
1220            
1221             package PACKAGE_REPLACE;
1222             $INC{INCKEY_REPLACE} ||= 1;
1223             our @ISA = 'Object::Hybrid::BASE';
1224            
1225             use Object::Hybrid::Class; # just labeling
1226             use Scalar::Util qw(reftype refaddr);
1227            
1228             #sub can { $_[0]->UNIVERSAL::can($_[1]) }
1229            
1230             Object::Hybrid->methods({
1231             tied => sub { undef },
1232             fast => sub { $_[0] },
1233             self => sub { $_[0] },
1234             TIEHASH => sub { bless {}, ref($_[0])||$_[0] },
1235             STORE => sub { $_[0]->{$_[1]} = $_[2] },
1236             FETCH => sub { $_[0]->{$_[1]} },
1237             FIRSTKEY => sub { my $a = scalar keys %{$_[0]}; each %{$_[0]} },
1238             NEXTKEY => sub { each %{$_[0]} },
1239             EXISTS => sub { exists $_[0]->{$_[1]} },
1240             DELETE => sub { delete $_[0]->{$_[1]} },
1241             CLEAR => sub { %{$_[0]} = () },
1242             SCALAR => sub { scalar %{$_[0]} },
1243             });
1244            
1245             CLASS
1246            
1247             }
1248            
1249 0     0 0 0 sub LOAD_SCALAR_UNTIED { $_[0]->_compile_class(<<'CLASS', $_[1]);
1250            
1251             package PACKAGE_REPLACE;
1252             $INC{INCKEY_REPLACE} ||= 1;
1253             our @ISA = 'Object::Hybrid::BASE';
1254            
1255             use Object::Hybrid::Class; # just labeling
1256             use Scalar::Util qw(reftype refaddr);
1257            
1258             #sub can { $_[0]->UNIVERSAL::can($_[1]) }
1259            
1260             Object::Hybrid->methods({
1261             tied => sub { undef },
1262             fast => sub { $_[0] },
1263             self => sub { $_[0] },
1264             TIESCALAR => sub {
1265             my $class = shift;
1266             my $instance = shift || undef;
1267             return bless \$instance => $class;
1268             },
1269             FETCH => sub { ${$_[0]} },
1270             STORE => sub { ${$_[0]} = $_[1] },
1271             });
1272            
1273             CLASS
1274            
1275             }
1276            
1277 0     0 0 0 sub LOAD_ARRAY_UNTIED { $_[0]->_compile_class(<<'CLASS', $_[1]);
1278            
1279             package PACKAGE_REPLACE;
1280             $INC{INCKEY_REPLACE} ||= 1;
1281             our @ISA = 'Object::Hybrid::BASE';
1282            
1283             use Object::Hybrid::Class; # just labeling
1284             use Scalar::Util qw(reftype refaddr);
1285            
1286             #sub can { $_[0]->UNIVERSAL::can($_[1]) }
1287            
1288             Object::Hybrid->methods({
1289             tied => sub { undef },
1290             fast => sub { $_[0] },
1291             self => sub { $_[0] },
1292             TIEARRAY => sub { bless [], $_[0] },
1293             FETCHSIZE => sub { scalar @{$_[0]} },
1294             STORESIZE => sub { $#{$_[0]} = $_[1]-1 },
1295             STORE => sub { $_[0]->[$_[1]] = $_[2] },
1296             FETCH => sub { $_[0]->[$_[1]] },
1297             CLEAR => sub { @{$_[0]} = () },
1298             POP => sub { pop(@{$_[0]}) },
1299             PUSH => sub { my $o = shift; push(@$o,@_) },
1300             SHIFT => sub { shift(@{$_[0]}) },
1301             UNSHIFT => sub { my $o = shift; unshift(@$o,@_) },
1302             EXISTS => sub { exists $_[0]->[$_[1]] },
1303             DELETE => sub { delete $_[0]->[$_[1]] },
1304             EXTEND => sub {},
1305             SPLICE => sub {
1306             my $ob = shift;
1307             my $sz = $ob->FETCHSIZE;
1308             my $off = @_ ? shift : 0;
1309             $off += $sz if $off < 0;
1310             my $len = @_ ? shift : $sz-$off;
1311             return splice(@$ob,$off,$len,@_);
1312             },
1313             });
1314            
1315             CLASS
1316            
1317             }
1318            
1319 1     1 0 7 sub LOAD_GLOB_UNTIED { $_[0]->_compile_class(<<'CLASS', $_[1]);
1320            
1321             package PACKAGE_REPLACE;
1322             $INC{INCKEY_REPLACE} ||= 1;
1323             our @ISA = 'Object::Hybrid::BASE';
1324            
1325             use Object::Hybrid::Class; # just labeling
1326             use Scalar::Util qw(reftype refaddr);
1327            
1328             #sub can { $_[0]->UNIVERSAL::can($_[1]) }
1329            
1330             sub new {
1331             goto &{ $_[0]->can(qw(TIEGLOB))
1332             ||Object::Hybrid::croak("Method not defined: new() / TIEGLOB()") }
1333             }
1334            
1335             Object::Hybrid->methods({
1336             tied => sub { undef },
1337             fast => sub { $_[0] },
1338             self => sub { $_[0] },
1339             TIEGLOB => sub {
1340             my ($elf, $fh, @open_args) = @_;
1341            
1342             if ($fh eq '') {
1343             $fh = \do { local *GLOB };
1344             } else {
1345             eval{ $fh = *$fh }, !$@ or Object::Hybrid::croak("Not a GLOB reference");
1346             }
1347            
1348             $fh->OPEN(@open_args) or Object::Hybrid::croak($!)
1349             if @open_args;
1350            
1351             return bless $fh, ref($elf)||$elf
1352             },
1353            
1354             OPEN => sub {
1355             defined $_[0]->FILENO
1356             and $_[0]->CLOSE;
1357            
1358             @_ == 2
1359             ? open($_[0], $_[1])
1360             : open($_[0], $_[1], $_[2]);
1361             },
1362            
1363             WRITE2 => sub {
1364             my $fh = $_[0];
1365             print $fh substr($_[1],0,$_[2])
1366             },
1367             WRITE => sub { my $fh = shift; write $fh },
1368             PRINT => sub { my $fh = shift; print $fh @_ },
1369             PRINTF => sub { my $fh = shift; printf $fh @_ },
1370            
1371             READ => sub { read $_[0], $_[1], $_[2] },
1372             READLINE => sub { my $fh = $_[0]; <$fh> },
1373             GETC => sub { getc $_[0] },
1374            
1375             EOF => sub { eof $_[0] },
1376             TELL => sub { tell $_[0] },
1377             FILENO => sub { fileno $_[0] },
1378             SEEK => sub { seek $_[0], $_[1], $_[2] },
1379             CLOSE => sub { close $_[0] },
1380             BINMODE => sub { binmode $_[0] },
1381            
1382             SYSOPEN => sub {
1383             eval {
1384             @_ >= 3 or Object::Hybrid::croak("Not enough arguments for sysopen()");
1385             @_ == 3 ? sysopen $_[0]->self, $_[1], $_[2] :
1386             @_ >= 4 ? sysopen $_[0]->self, $_[1], $_[2], $_[3] :();
1387             };
1388             !$@ or Object::Hybrid::croak($@);
1389             },
1390             FCNTL => sub {
1391             eval {
1392             @_ >= 3 or Object::Hybrid::croak("Not enough arguments for fcntl()");
1393             fcntl $_[0]->self, $_[1], $_[2];
1394             };
1395             !$@ or Object::Hybrid::croak($@);
1396             }, # TODO: same as for SYSOPEN()
1397             STAT => sub { stat $_[0]->self },
1398             FLOCK => sub { flock $_[0]->self, $_[1] },
1399             TRUNCATE => sub { truncate $_[0]->self, $_[1] },
1400             FTEST => sub {
1401             my $file = $_[0]->self;
1402             if ($_[1] =~ /^-\w$/) {
1403             eval "$_[1] \$file";
1404             !$@ or Object::Hybrid::croak($@);
1405             }
1406             else { Object::Hybrid::croak("Unknown argument to FTEST()") }
1407             },
1408            
1409             });
1410            
1411             #sub UNTIE;
1412            
1413             CLASS
1414            
1415             }
1416            
1417             1;
1418