X-Git-Url: http://git.vpit.fr/?a=blobdiff_plain;ds=inline;f=lib%2FVariable%2FMagic.pm;h=141d966ab9f90ebe443deee6ba6dab8be1c3f2f5;hb=refs%2Ftags%2Fv0.46;hp=e09ef044d5bd767c239fdc74f5f50ab5bca4829b;hpb=4d390b48e044d734803433486830972416f03d59;p=perl%2Fmodules%2FVariable-Magic.git
diff --git a/lib/Variable/Magic.pm b/lib/Variable/Magic.pm
index e09ef04..141d966 100644
--- a/lib/Variable/Magic.pm
+++ b/lib/Variable/Magic.pm
@@ -5,59 +5,93 @@ use 5.008;
use strict;
use warnings;
-use Carp qw/croak/;
-
=head1 NAME
Variable::Magic - Associate user-defined magic to variables from Perl.
=head1 VERSION
-Version 0.29
+Version 0.46
=cut
our $VERSION;
BEGIN {
- $VERSION = '0.29';
+ $VERSION = '0.46';
}
=head1 SYNOPSIS
- use Variable::Magic qw/wizard cast dispell/;
+ use Variable::Magic qw;
+
+ { # A variable tracer
+ my $wiz = wizard set => sub { print "now set to ${$_[0]}!\n" },
+ free => sub { print "destroyed!\n" };
+
+ my $a = 1;
+ cast $a, $wiz;
+ $a = 2; # "now set to 2!"
+ } # "destroyed!"
- my $wiz = wizard set => sub { print STDERR "now set to ${$_[0]}!\n" };
- my $a = 1;
- cast $a, $wiz;
- $a = 2; # "now set to 2!"
- dispell $a, $wiz;
- $a = 3 # (nothing)
+ { # A hash with a default value
+ my $wiz = wizard data => sub { $_[1] },
+ fetch => sub { $_[2] = $_[1] unless exists $_[0]->{$_[2]}; () },
+ store => sub { print "key $_[2] stored in $_[-1]\n" },
+ copy_key => 1,
+ op_info => VMG_OP_INFO_NAME;
+
+ my %h = (_default => 0, apple => 2);
+ cast %h, $wiz, '_default';
+ print $h{banana}, "\n"; # "0", because the 'banana' key doesn't exist in %h
+ $h{pear} = 1; # "key pear stored in helem"
+ }
=head1 DESCRIPTION
-Magic is Perl way of enhancing objects.
-This mechanism let the user add extra data to any variable and hook syntaxical operations (such as access, assignation or destruction) that can be applied to it.
-With this module, you can add your own magic to any variable without the pain of the C API.
+Magic is Perl's way of enhancing variables.
+This mechanism lets the user add extra data to any variable and hook syntactical operations (such as access, assignment or destruction) that can be applied to it.
+With this module, you can add your own magic to any variable without having to write a single line of XS.
+
+You'll realize that these magic variables look a lot like tied variables.
+It's not surprising, as tied variables are implemented as a special kind of magic, just like any 'irregular' Perl variable : scalars like C<$!>, C<$(> or C<$^W>, the C<%ENV> and C<%SIG> hashes, the C<@ISA> array, C and C lvalues, L variables...
+They all share the same underlying C API, and this module gives you direct access to it.
-Magic differs from tieing and overloading in several ways :
+Still, the magic made available by this module differs from tieing and overloading in several ways :
=over 4
=item *
-Magic isn't copied on assignation (as for blessed references) : you attach it to variables, not values.
+It isn't copied on assignment.
+
+You attach it to variables, not values (as for blessed references).
=item *
-It doesn't replace the original semantics : magic callbacks trigger before the original action take place, and can't prevent it to happen.
+It doesn't replace the original semantics.
+
+Magic callbacks usually get triggered before the original action takes place, and can't prevent it from happening.
+This also makes catching individual events easier than with C, where you have to provide fallbacks methods for all actions by usually inheriting from the correct C class and overriding individual methods in your own class.
=item *
-It's mostly invisible at the Perl level : magical and non-magical variables cannot be distinguished with C[, C or another trick.
+It's type-agnostic.
+
+The same magic can be applied on scalars, arrays, hashes, subs or globs.
+But the same hook (see below for a list) may trigger differently depending on the the type of the variable.
=item *
-It's notably faster, since perl's way of handling magic is lighter by nature, and there's no need for any method resolution.
+It's mostly invisible at the Perl level.
+
+Magical and non-magical variables cannot be distinguished with C][, C or another trick.
+
+=item *
+
+It's notably faster.
+
+Mainly because perl's way of handling magic is lighter by nature, and because there's no need for any method resolution.
+Also, since you don't have to reimplement all the variable semantics, you only pay for what you actually use.
=back
@@ -69,20 +103,22 @@ The operations that can be overloaded are :
C
-This magic is invoked when the variable is evaluated (does not include array/hash subscripts and slices).
+This magic is invoked when the variable is evaluated.
+It is never called for arrays and hashes.
=item *
C
-This one is triggered each time the value of the variable changes (includes array/hash subscripts and slices).
+This one is triggered each time the value of the variable changes.
+It is called for array subscripts and slices, but never for hashes.
=item *
C
This magic is a little special : it is called when the 'size' or the 'length' of the variable has to be known by Perl.
-Typically, it's the magic involved when an array is evaluated in scalar context, but also on array assignation and loops (C, C] creates them when no signature is supplied.
- # Generate a signature
- my $sig = gensig;
-
-=head2 C
-
- getsig $wiz
-
-This accessor returns the magic signature of this wizard.
-
- # Get $wiz signature
- my $sig = getsig $wiz;
+ return $wiz;
+}
=head2 C
- cast [$@%&*]var, [$wiz|$sig], ...
+ cast [$@%&*]var, $wiz, ...
This function associates C<$wiz> magic to the variable supplied, without overwriting any other kind of magic.
-You can also supply the numeric signature C<$sig> instead of C<$wiz>.
-It returns true on success or when C<$wiz> magic is already present, C<0> on error, and C when no magic corresponds to the given signature (in case C<$sig> was supplied).
-All extra arguments specified after C<$wiz> are passed to the private data constructor.
+It returns true on success or when C<$wiz> magic is already present, and croaks on error.
+All extra arguments specified after C<$wiz> are passed to the private data constructor in C<@_[1 .. @_-1]>.
If the variable isn't a hash, any C callback of the wizard is safely ignored.
- # Casts $wiz onto $x. If $wiz isn't a signature, undef can't be returned.
+ # Casts $wiz onto $x, and pass '1' to the data constructor.
my $x;
- die 'error' unless cast $x, $wiz;
+ cast $x, $wiz, 1;
The C argument can be an array or hash value.
Magic for those behaves like for any other scalar, except that it is dispelled when the entry is deleted from the container.
@@ -434,64 +346,271 @@ If you want to overcome the possible deletion of the C<'TZ'> entry, you have no
=head2 C
- getdata [$@%&*]var, [$wiz|$sig]
+ getdata [$@%&*]var, $wiz
-This accessor fetches the private data associated with the magic C<$wiz> (or the signature C<$sig>) in the variable.
-C is returned when no such magic or data is found, or when C<$sig> does not represent a current valid magic object.
+This accessor fetches the private data associated with the magic C<$wiz> in the variable.
+It croaks when C<$wiz> do not represent a valid magic object, and returns an empty list if no such magic is attached to the variable or when the wizard has no data constructor.
- # Get the attached data.
- my $data = getdata $x, $wiz or die 'no such magic or magic has no data';
+ # Get the attached data, or undef if the wizard does not attach any.
+ my $data = getdata $x, $wiz;
=head2 C
- dispell [$@%&*]variable, [$wiz|$sig]
+ dispell [$@%&*]variable, $wiz
The exact opposite of L : it dissociates C<$wiz> magic from the variable.
-You can also pass the magic signature C<$sig> as the second argument.
-True is returned on success, C<0> on error or when no magic represented by C<$wiz> could be found in the variable, and C when no magic corresponds to the given signature (in case C<$sig> was supplied).
+This function returns true on success, C<0> when no magic represented by C<$wiz> could be found in the variable, and croaks if the supplied wizard is invalid.
+
+ # Dispell now.
+ die 'no such magic in $x' unless dispell $x, $wiz;
+
+=head1 CONSTANTS
+
+=head2 C
+
+Evaluates to true iff the 'copy' magic is available.
+
+=head2 C
+
+Evaluates to true iff the 'dup' magic is available.
+
+=head2 C
+
+Evaluates to true iff the 'local' magic is available.
+
+=head2 C
+
+When this constant is true, you can use the C callbacks on hashes.
+Initial VMG_UVAR capability was introduced in perl 5.9.5, with a fully functional implementation
+shipped with perl 5.10.0.
+
+=head2 C
+
+True for perls that don't call 'len' magic when you push an element in a magical array.
+Starting from perl 5.11.0, this only refers to pushes in non-void context and hence is false.
+
+=head2 C
+
+True for perls that don't call 'len' magic when you push in void context an element in a magical array.
+
+=head2 C
+
+True for perls that don't call 'len' magic when you unshift in void context an element in a magical array.
+
+=head2 C
+
+True for perls that call 'clear' magic when undefining magical arrays.
+
+=head2 C
+
+True for perls that don't call 'len' magic when taking the C of a magical scalar.
+
+=head2 C
+
+True for perls that call 'get' magic for operations on globs.
+
+=head2 C
+
+The perl patchlevel this module was built with, or C<0> for non-debugging perls.
+
+=head2 C
+
+True iff this module could have been built with thread-safety features enabled.
+
+=head2 C
+
+True iff this module could have been built with fork-safety features enabled.
+This will always be true except on Windows where it's false for perl 5.10.0 and below .
+
+=head2 C
- # Dispell now. If $wiz isn't a signature, undef can't be returned.
- die 'no such magic or error' unless dispell $x, $wiz;
+Value to pass with C to get the current op name in the magic callbacks.
+
+=head2 C
+
+Value to pass with C to get a C object representing the current op in the magic callbacks.
+
+=head1 COOKBOOK
+
+=head2 Associate an object to any perl variable
+
+This technique can be useful for passing user data through limited APIs.
+It is similar to using inside-out objects, but without the drawback of having to implement a complex destructor.
+
+ {
+ package Magical::UserData;
+
+ use Variable::Magic qw;
+
+ my $wiz = wizard data => sub { \$_[1] };
+
+ sub ud (\[$@%*&]) : lvalue {
+ my ($var) = @_;
+ my $data = &getdata($var, $wiz);
+ unless (defined $data) {
+ $data = \(my $slot);
+ &cast($var, $wiz, $slot)
+ or die "Couldn't cast UserData magic onto the variable";
+ }
+ $$data;
+ }
+ }
+
+ {
+ BEGIN { *ud = \&Magical::UserData::ud }
+
+ my $cb;
+ $cb = sub { print 'Hello, ', ud(&$cb), "!\n" };
+
+ ud(&$cb) = 'world';
+ $cb->(); # Hello, world!
+ }
+
+=head2 Recursively cast magic on datastructures
+
+C can be called from any magical callback, and in particular from C.
+This allows you to recursively cast magic on datastructures :
+
+ my $wiz;
+ $wiz = wizard data => sub {
+ my ($var, $depth) = @_;
+ $depth ||= 0;
+ my $r = ref $var;
+ if ($r eq 'ARRAY') {
+ &cast((ref() ? $_ : \$_), $wiz, $depth + 1) for @$var;
+ } elsif ($r eq 'HASH') {
+ &cast((ref() ? $_ : \$_), $wiz, $depth + 1) for values %$var;
+ }
+ return $depth;
+ },
+ free => sub {
+ my ($var, $depth) = @_;
+ my $r = ref $var;
+ print "free $r at depth $depth\n";
+ ();
+ };
+
+ {
+ my %h = (
+ a => [ 1, 2 ],
+ b => { c => 3 }
+ );
+ cast %h, $wiz;
+ }
+
+When C<%h> goes out of scope, this will print something among the lines of :
+
+ free HASH at depth 0
+ free HASH at depth 1
+ free SCALAR at depth 2
+ free ARRAY at depth 1
+ free SCALAR at depth 3
+ free SCALAR at depth 3
+
+Of course, this example does nothing with the values that are added after the C.
+
+=head1 PERL MAGIC HISTORY
+
+The places where magic is invoked have changed a bit through perl history.
+Here's a little list of the most recent ones.
+
+=over 4
+
+=item *
+
+B<5.6.x>
+
+I : 'copy' and 'dup' magic.
+
+=item *
+
+B<5.8.9>
+
+I : Integration of I (see below).
+
+I : Integration of I (see below).
+
+=item *
+
+B<5.9.3>
+
+I : 'len' magic is no longer called when pushing an element into a magic array.
+
+I : 'local' magic.
+
+=item *
+
+B<5.9.5>
+
+I : Meaningful 'uvar' magic.
+
+I : 'clear' magic wasn't invoked when undefining an array.
+The bug is fixed as of this version.
+
+=item *
+
+B<5.10.0>
+
+Since C is uppercased, C triggers 'copy' magic on hash stores for (non-tied) hashes that also have 'uvar' magic.
+
+=item *
+
+B<5.11.x>
+
+I : 'len' magic is no longer invoked when calling C with a magical scalar.
+
+I : 'len' magic is no longer called when pushing / unshifting an element into a magical array in void context.
+The C part was already covered by I.
+
+I : 'len' magic is called again when pushing into a magical array in non-void context.
+
+=back
=head1 EXPORT
-The functions L, L, L, L, L and L are only exported on request.
+The functions L, L, L and L are only exported on request.
All of them are exported by the tags C<':funcs'> and C<':all'>.
-The constants L, L, L, L, L, L and L are also only exported on request.
-They are all exported by the tags C<':consts'> and C<':all'>.
+All the constants are also only exported on request, either individually or by the tags C<':consts'> and C<':all'>.
=cut
-use base qw/Exporter/;
+use base qw;
our @EXPORT = ();
our %EXPORT_TAGS = (
- 'funcs' => [ qw/wizard gensig getsig cast getdata dispell/ ],
- 'consts' => [
- qw/SIG_MIN SIG_MAX SIG_NBR MGf_COPY MGf_DUP MGf_LOCAL VMG_UVAR/,
- qw/VMG_COMPAT_ARRAY_PUSH_NOLEN VMG_COMPAT_ARRAY_UNSHIFT_NOLEN_VOID VMG_COMPAT_ARRAY_UNDEF_CLEAR VMG_COMPAT_SCALAR_LENGTH_NOLEN/,
- qw/VMG_PERL_PATCHLEVEL/,
- qw/VMG_THREADSAFE/,
- qw/VMG_OP_INFO_NAME VMG_OP_INFO_OBJECT/
- ]
+ 'funcs' => [ qw ],
+ 'consts' => [ qw<
+ MGf_COPY MGf_DUP MGf_LOCAL VMG_UVAR
+ VMG_COMPAT_ARRAY_PUSH_NOLEN VMG_COMPAT_ARRAY_PUSH_NOLEN_VOID
+ VMG_COMPAT_ARRAY_UNSHIFT_NOLEN_VOID
+ VMG_COMPAT_ARRAY_UNDEF_CLEAR
+ VMG_COMPAT_SCALAR_LENGTH_NOLEN
+ VMG_COMPAT_GLOB_GET
+ VMG_PERL_PATCHLEVEL
+ VMG_THREADSAFE VMG_FORKSAFE
+ VMG_OP_INFO_NAME VMG_OP_INFO_OBJECT
+ > ],
);
our @EXPORT_OK = map { @$_ } values %EXPORT_TAGS;
$EXPORT_TAGS{'all'} = [ @EXPORT_OK ];
-END { _cleanup() }
-
=head1 CAVEATS
-If you store a magic object in the private data slot, the magic won't be accessible by L since it's not copied by assignation.
+If you store a magic object in the private data slot, the magic won't be accessible by L since it's not copied by assignment.
The only way to address this would be to return a reference.
If you define a wizard with a C callback and cast it on itself, this destructor won't be called because the wizard will be destroyed first.
+In order to define magic on hash members, you need at least L 5.10.0 (see L)
+
=head1 DEPENDENCIES
L 5.8.
+A C compiler.
+This module may happen to build with a C++ compiler as well, but don't rely on it, as no guarantee is made in this regard.
+
L (standard since perl 5), L (standard since perl 5.006).
Copy tests need L (standard since perl 5.005) and L (since 5.002).
@@ -528,7 +647,7 @@ Tests code coverage report is available at L