MeowChow has asked for the wisdom of the Perl Monks concerning the following question:

Hi PerlMonks,

My question concerns tied object classes. I would like to know if there is a way to prevent the DESTROY method from being called twice (once for the object, and once for the underlying hash that is tied) for objects such as the one I've created below. I know I can check "tied $this" within DESTROY, but this is truly a vile hack of a solution. Essentially what I would like to know is if there is a way to seperate the "magical" destructor for the tied object from the class destructor:

package SillyClass; require Tie::Hash; @ISA = (Tie::Hash); sub new { my $proto = shift; my $class = ref ($proto) || $proto; my $this = bless { }, $class; tie %{$this}, $class; return $this; } sub TIEHASH { my $self = shift; my $node = { color => 'blue', age => 12, pet => 'dog', }; return bless $node, $self; } sub DESTROY { my $self = shift; print "Calling DESTROY by $self\n"; } #------end class definition------ package main; my $fred = SillyClass->new(); undef $fred; ## DESTROY gets called twice, once by "$fred" and by + "tied $fred"
You may notice that this a wierd way of building a tied object. I don't know if this is common programming practice (specifically, the tie %{$this}, $class; line right within the object constructor), but it seems to work quite well. I build classes this way in order to allow access to an object's attributes through the object->{attribute} notation, while reserving the ability to mediate this access in the same way that attribute accessor/mutator methods would. Thus, I retain the straightforward hash-key object attribute syntax while also enjoying all the "object-oriented encapsulation" benefits of the the method call syntax.

So I suppose I'm also asking another question: is there anything "wrong" about doing things in this way in the first place? Thanks very much for your help.

Replies are listed 'Best First'.
Re: DESTROY for tied classes
by chromatic (Archbishop) on Dec 05, 2000 at 05:56 UTC
    In your constructor, you bless a hash into the current class, storing it in $this. Then, you tie the dereferenced hash into the current class.

    I think that's double-blessing. Good for the pious, but tricky for programs. Here's my suggestion:

    sub new { my $proto = shift; my $class = ref ($proto) || $proto; my %this; return tie(%this, $class); }
    Let's make things a little more explicit by tieing a hash blessed into a different class. Here's the new class and a slightly different constructor:
    package FunClass; sub new { my $class = shift; my $self = {}; bless($self, $class); } sub DESTROY { my $self = shift; print "Destroying $self!\n"; } sub new { my $proto = shift; my $class = ref ($proto) || $proto; my $this = FunClass->new(); tie %{$this}, $class; return $this; }
    And the results?
    Destroying FunClass=HASH(0x80f173c)! Calling DESTROY by SillyClass=HASH(0x80f600c)

      I'd recommend using a separate class for tie from the one used for object methods. I wrote Win32::TieRegistry using one package for both tieing and blessing, and it mostly worked nice with a bunch of cases of:

      my $self= shift(@_); $self= tied(%$self) if tied(%$self);
      But I'll probably be converting to using Win32::TieRegistry for the object methods and Win32::TieRegistry::Key for the tied hashes. Although this allows some efficiency improvements, the main reason for my desire to change is "bug"s in Perl's destructor semantics.

      Prior to Perl5.6, the main bug was that any object stored in a global variable won't be destroyed until the "global destruction" phase of the Perl interpreter shutdown procedure. Unfortunately, during global destruction, all objects are destroyed in an arbitrary order.

      This means that the real object might be destroyed before the "pretend" object that is tied to the real object. This means that tied(%$pretend) properly detects that the pretend object is tied and returns the object that it is tied to. But that (real) object has been destroyed so it is returned as undef, which you cannot distinguish from tied returning undef because %$pretend isn't tied.

      So your DESTROY will think that $pretend isn't tied and try to treat it like the real object. Doing, for example, $pretend->{handle} will trigger a tied fetch which will call FETCH on the underlying object which is now undef so we get the equivalent of undef->FETCH("handle"), which generates a nasty error.

      It appears that Perl5.6 made this situation even worse so that it happens in more cases. I'm in the process of debugging that so I can "fix" Win32::TieRegistry so that it doesn't complain in spite of this "bug" in Perl.

      So having a Win32::TieRegistry::DESTROY separate from Win32::TieRegistry::Key::DESTROY can be very handy.

      I think it was a poor design that tie and bless use different constructors (TIEHASH vs. new) but the same destructor. The tie destructors should be DESTROYHASH or UNTIEHASH, etc.

      Now, I think the way you are doing this is beyond what I did with Win32::TieRegistry, that is, a "self-tie" where you don't have two objects, one tied to the other, but instead have a single object that is tied to itself. This is supposed to work but has always made me nervous. Well, it was recently badly broken and I don't think it has been fixed yet. It will probably eventually be fixed, but I still suggest you just avoid doing that.

              - tye (but my friends call me "Tye")
      Thanks for the help. Next time, I'll count my blessings :)
Re (tilly) 1: DESTROY for tied classes
by tilly (Archbishop) on Dec 05, 2000 at 06:23 UTC
    I am sorry, but I still don't see what you are winning with the tie.

    I see what you are losing - you have significantly complicated your life so you can slow down every access by an order of magnitude. But OO is not good just because it is OO. There is nothing magical about the method-call syntax that makes it better. It is just a different way of saying the same thing with a little polymorphic magic.

    Now there are many things that you could do which would make the tie make sense. For instance often you want to have a different implementation look like a tie. An example would be to have an object with a set of keys that it would use (so that typos of a key would trigger a warning). Another would be a hash that was stored persistently on disk. Yet another would be an interface into a C structure. In each of these cases the tie is winning you something specific - something that in all likelyhood has little directly to to with the capabilities you want to expose.

    In that case it makes no sense to intertwine the package that you tie the hash into with the package that has methods you are exporting. Indeed you drop this incestuous relationship as chromatic suggested and your problem just goes away.

    My suggestion therefore is to simply drop the tie. If at a later point you find a need for it, you can easily add it. Probably it will just be a matter of building your tie implementation and modifying the constructor.

    Remember that the point of modularity, interface hiding, and all that isn't that your first implementation will be gold-plated. Rather it is that you can get a working system and then be able to modify it in directions that experience shows are important...

      I don't yet know what I'm winning with the tie either, because it's use is as yet unforseen -- it's actually part of a sort of "class-generator" module for Perl, and the use for this feature will be up to those who use the module. I decided to throw this feature into the class generator because the class generator will also form the basis of a DBI persistence layer I'm working on, hence one of your suggested tie usages.

      But yes, with chromatic's suggestion, I can keep the tie and lose the "incestuous relationship", as you've humorously phrased it, so it's all good :)

        I would suggest still losing it. If you later decide you want the tie in certain submodules, consider the following trick:
        package A; sub new { my $proto = shift; my $class = ref($proto) || $proto; my $obj; %$obj = @_; return bless ($obj, $class); } sub TrialMethod { print "The method was called\n"; } package A::SubClass; @ISA = A; sub new { my $proto = shift; my $obj = $proto->SUPER::new(@_); tie (%$obj, 'Some::Tie::Class', $obj); return $obj; } package Some::Tie::Class; sub FETCH { print "A fetch happened\n"; } sub TIEHASH { my $class = shift; my %hash = %{ shift() }; return bless \%hash, $class; } package main; my $thing = new A::SubClass(qw(hello world)); $thing->TrialMethod(); $thing->{foo};
        See? The example is useless, but it shows that without taking the hit of assuming a tie implementation in your base class you have in no way affected your ability to create subclasses that can take advantage of a tie. You are already this polymorphic. Why force the speed hit?

        In fact the generic approach is more polymorphic than the tie implementation you are doing. Why? Because you moved key initializations into TIEHASH, so anyone who wants to override your tie with an interesting implementation needs to couple their TIEHASH more tightly with yours than they should need to.

        In fact this is a basic principle of OO design which is good to know. The point of OO is to provide encapsulated behaviour, aspects of which are easy to override. Therefore in your design do as little as you need to with as clean an interface as you can come up with, and plan on using the ease of overriding to later add on any features that could come in handy. Then anyone who doesn't use the flexibility, doesn't pay. But the potential is still intact.