From what I understand inside-out classes structure "require one hash per declared attribute" (Perl Best Practices). I cannot see any obstacle and drawback if these hash structures are replaced with single hash reference.
Currently (Damian Conway):
package SomeClass; use Class::Std::Utils; { my %name_of; sub new { my ($class, $name) = @_; my $new_object = bless \do{my $anon_scalar}, $class; $name_of{ident $new_object} = $name; return $new_object; } sub get_name { my ($self) = @_; return $name_of{ident $self}; } }

My point of view:
package SomeClass; { # All I need: my $data; sub new { my ($class, $name) = @_; my $scalref; my $new = bless \$scalref, $class; my $addr = norm($new); $data->{$addr} = { name => $name, obj => $new, }; $new } sub get { my $self = shift; my $arg = shift; $self = norm($self); $data->{$self}{$arg}; } sub set { my $self = shift; my ($key, $val) = @_; $self = norm($self); $data->{$self}{$key} = $val; } sub norm { my $address = ($_[0] =~ /\(?x(\w+)/)[0] } }
And the usage:
#!/usr/bin/perl use strict; use SomeClass; my$_1 = SomeClass->new('ok, creation of obj 1'); my$_2 = SomeClass->new('ok, creation of obj 2'); print $_1->get('name'), $/; print $_2->get('name'), $/; $_1->set('name', 'object _1 new attr value'); $_2->set('name', 'object _2 new attr value'); print $_1->get('name'), $/; print $_2->get('name'), $/; print $_2->{'name'}, $/;


Replies are listed 'Best First'.
Re: Inside-out classes structure
by japhy (Canon) on Oct 02, 2005 at 02:37 UTC
    Same difference. You've just added ONE MORE hash to HOLD all the OTHER hashes. You've got a hash of hash references now.

    Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
    How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart
      $data->{object_addr} = { K1 => V1, K2 => V2, K3 => V3, }; is not the same as: <c> $K1{object_addr} = V1; $K2{object_addr} = V2; $K3{object_addr} = V3;
      IMHO.


        You are correct: strict can better protect you if you use the separate hashes.

        Be well,
        rir

Re: Inside-out classes structure
by rir (Vicar) on Oct 02, 2005 at 03:57 UTC
    I don't see the point of an extra level of indirection.

    Your norm() function replaces Scalar::Util::refaddr() poorly. ident() is an alias of refaddr. These comments are mine, this code is TheDamian's.

    sub refaddr($) { # prototypes are usually suspect, here a prototype # is reasonable because the usage of this routine is # very predictable. # Save the class; fail if we don't have an object my $pkg = ref($_[0]) or return undef; # rebless the object into a package that is supposed to # NOT to overload numeric coercion. This code is more # robust than yours but still fails if UNIVERSAL overloads # '0+'; to really be safe we'd need to # use CORE:: functions explicitly. # # I would have probably just used 'Scalar::Util' but # I'm the sort who goes looking for 'Scalar::Util::Fake', # you have to use something like "S::U::No_SuCh_PkG" # before I believe you mean it. bless $_[0], 'Scalar::Util::Fake'; # int() seems to have acquired a little magic when # applied to a reference. I hadn't noticed. my $i = int($_[0]); # restore the object to its package bless $_[0], $pkg; # return the decimal address $i; }
    Be well,
    rir

      In A Scalar::Util::refaddr Oddity, I discussed a failure mode of pure-perl refaddr($), and ambrus pointed out another in his reply to that node.

      1. An unblessed reference becomes blessed when refaddr($) is called on it.
      2. A reference to a literal constant causes refaddr($) to die with a "Modification of constant" error.

      A proposed fix:

      sub refaddr($) { my $pkg = ref($_[0]) or return; my $is_object = Scalar::Util::blessed($_[0]); bless $_[0], 'Scalar::Util::Fake' if $is_object; my $i = int($_[0]); bless $_[0], $pkg if $is_object; $i; }
      That probably works for anything. I hedge that statement a little because some XS wizard might find a way to bless a literal constant.

      After Compline,
      Zaxo

      I don't see the point of an extra level of indirection.

      I'm ignoring the fact that it should use refaddr instead of norm and that it uses a hashref for $data instead of just %data, as those are implementation details and not really about the data structure philosophy. The point is that for N properties, the object data are kept in one HoH data structure instead of N hashes. Whether this is a benefit (that outweighs the cost) depends.

      Consider each of the following:

      Initialization: given a hashref of initialization properties, the N-hashes way requires copying over each parameter. The HoH way can use the hashref that was passed in directly (storing it or copying it as appropriate).

      (Caveat: my examples are to illustrate the point -- this is not necessarily good/robust style as it doesn't validate parameters, assumes that no bogus initializers are passed, etc.)

      # N-hashes code adapted from docs to Class::Std sub BUILD { my ($self, $obj_ID, $arg_ref) = @_; $name{$obj_ID} = $arg_ref->{name} || $default_of{name}; $rank{$obj_ID} = $arg_ref->{rank} || $default_of{rank}; # repeat for N properties } # HoH way sub BUILD { my ($self, $obj_ID, $arg_ref) = @_; $data{$obj_ID} = { %default_of, %$arg_ref }; }

      Destruction: The same issue arises during DESTROY. From some of my benchmarking, explicit destruction of each entry in the data-storage hash is one of the most inefficient parts of the inside-out technique. That requires either N calls to delete for N properties, or a single delete of the object hashref in the %data hash.

      Thread-cloning: Inside-out objects are only thread-safe under 5.8 with the use of the CLONE subroutine. From my article Threads and fork and CLONE, oh my!), a subroutine like this is needed:

      Using the HoH approach, only the %data hash would need to be relocated, rather than N individual hash entries.

      All this might beg the question "why not use regular hash-based objects, then?" The answer, of course, is that the inside-out technique works on any type of blessed reference. For example, either of the N-hashes or HoH approach could be used to provide object properties to (or subclass) a class that is implemented as a blessed globref.

      Benchmarking:

      -xdg

      Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

        You raise good points. I haven't thought so hard about the issue as you. I have an interest as I am looking to abandon my usage of hash based objects. I approached sh1tn's post from a don't reinvent the wheel stance.

        Not copying the parameter hash seems to leave the state of our new object open to the whims of another reference. Our capsule is dissolved before it is created.

        I have used hash based objects a lot, in my situations the trampling and encapsulation issues have not been a problem. But constraining the keys of the hash is something I quickly found useful. TheDamian's code lets strict give that to us by replacing strings with lexicals (I like that). I would appreciate your solution for HOHObjects or your other thoughts on this aspect of the matter.

        Class::Std's prime purpose is to constrain clients to the interface. That makes me wonder for what purpose you would use something other than a scalar as an object here; to me it seems to expose implementation to save only one indirection.

        Be well,
        rir

Re: Inside-out classes structure
by xdg (Monsignor) on Oct 02, 2005 at 03:36 UTC

    I've been pondering moving to a similar structure for Object::LocalVars. It would give the same general benefits of inside-out classes, and it makes object destruction a little simpler -- just one reference to delete rather than deleting each attribute individually. Ditto for CLONE (which I don't think Class::Std handles properly yet.) I haven't gotten around to benchmarking this approach yet, though, so there is some risk that this is premature optimization.

    -xdg

    Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Re: Inside-out classes structure
by Anonymous Monk on Oct 03, 2005 at 10:11 UTC
    I cannot see any obstacle and drawback if these hash structures are replaced with single hash reference

    I can.

    • It's far more likely that you are using more objects than attributes. Your solution uses a hash per class per object, while the inside-out solution uses a hash per attribute. So, if you have 1,000 objects of a class hierarchy of 5 classes, using 20 attributes in total, your solution uses 5,000 hashes. The inside-out technique uses 20.
    • Your solution uses strings to store variables. Which gives you all the drawbacks of using symbolic references. It's even worse than using package variables, as there's a decent chance that 'use warnings' catches a typo in a package variable - but neither 'use warnings', nor 'use strict' will catch a typo in a hash key. The inside-out technique uses lexical variables only, giving you all the benefits of 'use strict'.
    These are drawbacks. It doesn't mean your solution (which is a known technique, often called 'fly-weight objects') doesn't work.
A reply falls below the community's threshold of quality. You may see it by logging in.