in reply to Overloading different instances differently.

Your solution should work. Its biggest drawback is that every object created clears Perl's cache of method lookups. Also it leaks memory - create a thousand objects and Perl now has a thousand classes that never go away.

Memoizing would solve both of those problems. Matching sub refs isn't that hard. The following code will turn your hash into a string suitable for memoizing.

use Scalar::Util qw(refaddr); sub overload_string_specification { my $overload = shift; join " ", map { ($_=>refaddr($overload->{$_})) } sort keys %$overload; }
Another solution is to overload all of the methods you may wish to be able to overload with methods that dynamically figure out the right function to call. If you only have a small number of methods that you'll be overloading this isn't a bad route. But if you could potentially wind up overloading all of the possible things in overload, then doing this basically means re-creating all of the dispatch logic in overload in your code. Which you may not want to do.

Replies are listed 'Best First'.
Re^2: Overloading different instances differently.
by plobsing (Friar) on Feb 23, 2008 at 01:40 UTC
    I could be wrong, but the memory problem doesn't seem to be unfixable.

    The packages the OP is creating would be small (because all the methods are inherited). Also you could manually get rid of the package on destruction using something akin to this:
    package FOO; sub asdf{shift ; reverse @_}; package main; # need an object so that functions get bound at runtime my $o = bless {}, 'FOO'; print $o->asdf(1..10); # ok undef %FOO::; print $o->asdf(1..10); # function cannot be found
    If you were extremely memory concious, you could put each instance's fields in the new package (why use another hashref?)

    If you wanted to be realy sneaky, you could have the object as a globref to it's own package and the decrement the refcount on the glob by one. When the instance goes out of scope, the glob's refcount drops to 0 and cleans itself up.

    At least thats the way I understand packages. Please enlighten me if I'm wrong.

    Udate: The code presented above doesn't work. Here's why: every time perl sees the name *Foo:: or %Foo:: in the source, it increments the ref count of it (similar in concept to closing over a value, but for globals). Therefore undef *Foo:: inadvertently creates another reference to the value it is trying to destruct.

    Here's the code I used to come to that conclusion.
    use strict; use Inline 'C'; $\ = "\n"; my $x; BEGIN { my $n = 100; $x = eval 'sub {' . ('%Foo::;' x $n) . '}'; } print refcount(*Foo::); # prints $n + 2 __END__ __C__ int refcount(SV* x) { return SvREFCNT(x); }
      You are assuming that your code destroys everything in a package properly. Let's test that:
      #! /usr/bin/perl -w use strict; my $s; print "Creating functions\n"; my $x = TellDestroy->new(sub {print $s}, "variable sub"); *Foo::function = TellDestroy->new(sub {print $s}, "glob sub"); print "Clearing variable\n"; undef $x; print "Clearing glob\n"; undef %Foo::; print "Exiting\n"; package TellDestroy; use Scalar::Util qw(refaddr); my %function_name; sub new { my ($class, $self, $name) = @_; $function_name{refaddr($self)} = $name; bless $self, $class; } sub DESTROY { my $self = shift; my $name = delete $function_name{ refaddr($self) }; print STDERR "$name is going away\n"; } __END__
      produces the following output for me on Perl 5.8.6 under Linux:
      Name "Foo::function" used only once: possible typo at check line 8. Creating functions Clearing variable variable sub is going away Clearing glob Exiting glob sub is going away
      So the function survived to global destruction. I take that to mean that Perl doesn't clean up packages the way you wanted it to.

        The long-time standard module Symbol to the rescue. Just change undef %Foo::; to delete_package('Foo');.

        #! /usr/bin/perl -w use strict; use Symbol 'delete_package'; my $s; print "Creating functions\n"; my $x = TellDestroy->new(sub {print $s}, "variable sub"); *Foo::function = TellDestroy->new(sub {print $s}, "glob sub"); print "Clearing variable\n"; undef $x; print "Clearing glob\n"; delete_package('Foo'); print "Exiting\n"; package TellDestroy; use Scalar::Util qw(refaddr); my %function_name; sub new { my ($class, $self, $name) = @_; $function_name{refaddr($self)} = $name; bless $self, $class; } sub DESTROY { my $self = shift; my $name = delete $function_name{ refaddr($self) }; print STDERR "$name is going away\n"; } __END__ Name "Foo::function" used only once: possible typo at ... Creating functions Clearing variable variable sub is going away Clearing glob glob sub is going away Exiting

        lodin

Re^2: Overloading different instances differently.
by kyle (Abbot) on Feb 22, 2008 at 22:23 UTC

    Thank you!

    Part of the point of this exercise is for the object to "look right" to overload::Method, if anyone cares to look. I don't want it to look like @{} is available if it's one of the unimplemented methods on that particular instance.

    Your point about leaking packages all over is well-taken. I suspect that "looks right" functionality is less important than that. Perhaps I'll implement both solutions and let the user specify which behavior they want (with details of the trade-offs in the documentation).