I've created a module Magic::Attach. It allows you to attach one referant to another.
The purpose of which is to allow a sub or any other type of object to attach some private data
to a reference. This could be for lazy evaluation of arguments or whatever purpose you can think of.
When you attach a ref to another ref, you can optionally specify a nameid to avoid collisions.
Multiple attachments are fine but other modules that implemented their own magic using the '~'
user extension type may clobber us if they are less careful.
This is my first real forray into XS so I'm sure there are things that could be done better.
Comments and criticisms welcome.
It works with 5.6.1 and above (possibly with earlier versions
but I haven't confirmed this.) The naming of the routines may change. (Would bind/bound/unbind be better suited?)
My other thought is if this is a generally useful module, perhaps the "optional nameid" shouldn't
be optional (Currently it defaults to Magic::Attach). I worry that if this finds use and the
default nameid are used in two different places it will lead to very, very subtle bugs. I can't speak for anyone else but before embarking on this, I would never think to look at "magic vtables" for problems.
I'd really appreciate any feedback on the implementation, usefulness, etc of this.
$attached = attach_var($target_ref, $attach, [$optional_name]);
$attached = attached($target_ref, [ $optional_name ]);
$attached = unattach_var($target_ref, [ $optional_name ]);
# A few trivial examples.
sub elements(\@$){
my ($ref,$size) = @_;
$size = 1 unless $size;
my $info = attached($ref,'elements') || attach_var($ref,[0,$size],
+'elements');
my @ret = ();
if( $info->[0] >= @$ref ) {
unattach_var($ref,'elements');
return;
}else{
@ret = @{$ref}[$info->[0]..($info->[0]+$info->[1] -1)];
$info->[0]+= $info->[1];
}
@ret;
}
while( my @els = elements(@large_array,4) ){
# Process non destructively
}
# Lazily generate a span of integers.
sub int_span($$){
my $info = attached(\$_[0],'INT_SPAN') || attach_var(\$_[0],[$_[0]
+,$_[1] ],'INT_SPAN');
unattach_var(\$_[0],'INT_SPAN') and return if $info->[0] > $info
+->[1];
return $info->[0]++;
}
while(my $i = int_span(1,1000000000)){
print $i,"\n";
}
Thanks,
-Lee
"To be civilized is to deny one's nature."
update
The "magic" refered to is internal to the perl interpreter. See "Magic Variables" in
perlguts for more information.
update Jan 21 one more example.
# Persistant subroutine data
sub somesub {
my $p_args = attached(\&somesub,'somesub') || attach_var(\&somesub
+,{called=>0},'somesub');
++$p_args->{called};
# The persistant arg hash could be keyed on the caller info if des
+ired
# for context sensitivity
}