-FUNCTIONS
- "wizard"
- wizard sig => ...,
- data => sub { ... },
- get => sub { my ($ref, $data [, $op]) = @_; ... },
- set => sub { my ($ref, $data [, $op]) = @_; ... },
- len => sub { my ($ref, $data, $len [, $op]) = @_; ... ; return $newlen; },
- clear => sub { my ($ref, $data [, $op]) = @_; ... },
- free => sub { my ($ref, $data [, $op]) = @_, ... },
- copy => sub { my ($ref, $data, $key, $elt [, $op]) = @_; ... },
- local => sub { my ($ref, $data [, $op]) = @_; ... },
- fetch => sub { my ($ref, $data, $key [, $op]) = @_; ... },
- store => sub { my ($ref, $data, $key [, $op]) = @_; ... },
- exists => sub { my ($ref, $data, $key [, $op]) = @_; ... },
- delete => sub { my ($ref, $data, $key [, $op]) = @_; ... },
- copy_key => $bool,
- op_info => [ 0 | VMG_OP_INFO_NAME | VMG_OP_INFO_OBJECT ]
-
- This function creates a 'wizard', an opaque type that holds the magic
- information. It takes a list of keys / values as argument, whose keys
- can be :
-
- * "sig"
-
- The numerical signature. If not specified or undefined, a random
- signature is generated. If the signature matches an already defined
- magic, then the existant magic object is returned.
-
- * "data"
-
- A code reference to a private data constructor. It is called each
- time this magic is cast on a variable, and the scalar returned is
- used as private data storage for it. $_[0] is a reference to the
- magic object and @_[1 .. @_-1] are all extra arguments that were
- passed to "cast".
-
- * "get", "set", "len", "clear", "free", "copy", "local", "fetch",
- "store", "exists" and "delete"
-
- Code references to the corresponding magic callbacks. You don't have
- to specify all of them : the magic associated with undefined entries
- simply won't be hooked. In those callbacks, $_[0] is always a
- reference to the magic object and $_[1] is always the private data
- (or "undef" when no private data constructor was supplied).
-
- Moreover, when you pass "op_info => $num" to "wizard", the last
- element of @_ will be the current op name if "$num ==
- VMG_OP_INFO_NAME" and a "B::OP" object representing the current op
- if "$num == VMG_OP_INFO_OBJECT". Both have a performance hit, but
- just getting the name is lighter than getting the op object.
-
- Other arguments are specific to the magic hooked :
-
- * "len"
-
- When the variable is an array or a scalar, $_[2] contains
- the non-magical length. The callback can return the new
- scalar or array length to use, or "undef" to default to the
- normal length.
-
- * "copy"
-
- $_[2] is a either a copy or an alias of the current key,
- which means that it is useless to try to change or cast
- magic on it. $_[3] is an alias to the current element (i.e.
- the value).
-
- * "fetch", "store", "exists" and "delete"
+COOKBOOK
+ 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<wizard cast getdata>;
+
+ 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!
+ }
+
+ Recursively cast magic on datastructures
+ "cast" can be called from any magical callback, and in particular from
+ "data". 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 %h goes out of scope, this prints 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 "cast".