http://www.perlmonks.org?node_id=859439

perl-diddler has asked for the wisdom of the Perl Monks concerning the following question:

Just when I thought I had some clue about how things work, I go and run into problems, proving that I'm overlooking the obvious or not remembering as well as I thought I did...

I have (what I thought) was a fairly straight forward set of classes that are all subclasses of the one above (no multiple inheritance issues at this point).

I'm storing data object data in a blessed hash that's created in the most 'base' class's 'new' routine. That's blessed in the base class and returned 'up' the call stack to the successive, derived-class, 'new' routines, where they may add their own data (as more hash entries in the same hash). They then bless and return the has w/any new entries to the next class in line.

So classes 'URL' => URL::Storable => URL::Fetchable are subclasses in that order from least specific to 'derived'.

In URL, code looks like (abbreviated):

package URL; {... sub new { my $package=shift; my $argsp = $_[0]; my $up= { host => '', path => '', }; foreach (keys %$up, @arg_aliases) { $up->{$compose_argmap{_tl( $_)}}=$argsp->{$_} if exists $argsp-> +{$_}; } bless $up, $package; } }
in derived classes, the 'new' looks like:
package URL::Storable; { our @ISA=qw(URL); sub new { my $package=shift; my $argsp = $_[0]; # setting a "per-class" var, not per-instance if (exists $argsp->{save_prefix}) { $save_prefix=$argsp->{save_prefix}; } my $this=new URL(@_); # (process this packages args....) bless $this, $package; #bless and return } }
And so on... If the above is the 1st derived class, then on the 2nd derived class (derived from the 1st derived class, not the base class of the 1st), gets an error when I try to bless it:

Attempt to bless into a reference at filename.pl line xxx.

Is what I am doing, reasonable for a smallish project (at this point, its all in 1 file). If it is, then is there common 'gotcha', that would cause this?

Isn't this 'a' normal way of doing classing with each level plugging in it's local-object data, then blessing it at the current class level, and returning it. Then any call on an object would (in theory), go from the most derived object look in it's "ISA" for the next, and go to the next level?

If this all seems straightforward so far, and nothing looks amiss, I can start more wholesale copying of code into this question, but I didn't want to go into unnecessary detail for something that seems pretty straight forward, where I'm feeling I must be overlooking something obvious OR have some basic misunderstanding about how things work somewhere, but since the code 'look's fairly simple, I'm checking my understanding first, since I'm not sure why, for example, it would fail when I bless in the 2nd level derivation class, as opposed to in the first-level derivation class, where I'm first blessing that returned 'object' from the base class, URL, after having added an arg to it.

Ideas or 'clue-sticks' welcomed...
thanks... (Note, fixed example typo resulting from starting with a demo case, then copying in actual code (re: $up (in code for url pointer), vs. $url used in demo case).

Replies are listed 'Best First'.
Re: blessed confusion
by jethro (Monsignor) on Sep 08, 2010 at 23:53 UTC

    Clue 1: If you google for the error message or call "perldoc perldiag" on the command line you will find out that the error message means that your second argument to bless (your $package above) is a reference instead of a string with a class name in it. Check where the error occurs (line xxx) and retrace the program back to where $package gets its value. My guess is that you forgot to change something when you copied the code to create the 2nd derived class

    Clue 2: in your URL class you bless $url to your class but never initialize it with anything. Maybe you had $up in mind (which gets initialized). Something similar seems to be happening in your derived class as well

    Clue 3: Generous use of Data::Dumper to print out your data structures will give you insight in what you constructed. Check your variables whether they contain what you expect them to contain.

    Clue 4: "perldoc perlobj" has lots of examples of the optimal use of object initialization (lines 40 to 90 for example). As you can see you have it almost right, only some bugs need to be corrected it seems

      !!

      Talk about misdirecting error messages.

      It should have said I was trying to bless _with_ a 'non-classname'. When I see 'bless $reference,$name', I see it as _value_ in '$name' being written into the 'package-type' label for the object. So the 'ref' is receiving (being blessed) with the value. Error message then says that something is wrong with the 'target' of my blessing...so...

      Once I new the problem was with the 'classname' and not the target, problem was quickly found as prior level called 'new' as in:

      $this=URL::Fetchable::Open(@_);
      instead of:
      $this=URL::Fetchable->Open(@_);
      Thanks MUCH for pointing at the error message. I should have looked it up rather than expecting the wording to make sense. Now I have an idea why IBM et al, use ABEND3207. :-)

        Error message then says that something is wrong with the 'target' of my blessing...so...

        Just like a person is blessed into a church, a value is blessed into a class. What you call the target of the bless is on the left of "bless into".

        An alternate phrasing would be "Attempt to use reference for class name".

Re: blessed confusion
by chromatic (Archbishop) on Sep 08, 2010 at 23:51 UTC

    Moose (I know, but hold on) separates object creation from object initialization. When you construct a new Moose object, its constructor calls for you a method (BUILDALL, I believe but haven't confirmed before posting) which calls every BUILD method in the appropriate class hierarchy. Each BUILD method performs the appropriate initialization.

    bless only occurs once.

    If you can't migrate to Moose right now, you could do something similar. Move the responsibility for blessing an object into the parentmost new and create an initialize() method in each class where that's useful. Within new, after blessing, call initialize. Be sure to call the parent implementation:

    sub initialize { my $self = shift; $self->SUPER::initialize( @_ ); ... }
      Wait, wait, wait...'bless only once'...?? Um...I don't think so...

      Each object's creation code must return a blessed reference to indicate that the returned 'object' is now part of that class (has been blessed into that class) -- meaning that the reference can be now called on any method in the new class.

      W/o the blessing @ each level, you couldn't use the reference to call subclass methods, as it's not a pointer of any class (yet), until it's done being initialized into that class.

      So something like 'cacheable' wouldn't be able to use the 'path' method of 'url' to derive a cacheable's path. Example:

      package url (methods:new 'host' 'path') package url::cacheable sub new { $package=shift; $up=new url(@_); # host & path passed in args $up->path($cpath) if ($cpath=_canonical_path($up->path)) ne $path; bless $up, $package; }
      So when $up comes back from 'new url', it's a ref to a url-blessed object and is used as such. But not until the path of the 'url' object has been tested as (and possibly set to) '_canonical_path', is it suitable to be blessed as a 'cachceable' object.

      Am I missing something?

        Am I missing something?

        Inside Perl 5, a reference only has one slot to associate it with a class. You can replace that association by blessing the reference into a different class, but there's only ever one class associated with a reference.

        I don't know what object system you're thinking of, but this is how Perl 5 has worked since the beginning. Test it and see:

        package Grandkid; use parent 'Kid'; sub new { bless {}, shift } package Kid; use parent 'Parent'; package Parent; sub inherited_method { 'Yep, inherited!' } package main; use Test::More 'no_plan'; my $gkid = Grandkid->new(); isa_ok( $gkid, 'Grandkid' ); isa_ok( $gkid, 'Kid' ); isa_ok( $gkid, 'Parent' ); is( $gkid->inherited_method(), 'Yep, inherited!', 'method inherited fr +om grandparent' ); done_testing();

        W/o the blessing @ each level, you couldn't use the reference to call subclass methods,

        No, only the subclass's blessing is required. In fact, all the other blessings are removed by it. chromatic is simply suggesting that you bless it correctly the first time instead of correcting the incorrect blessing with a second blessing.

Re: blessed confusion
by GrandFather (Saint) on Sep 09, 2010 at 00:00 UTC

    You want a clue-stick? Consider:

    use strict; use warnings; package Base; sub new { my ($class, %args) = @_; return bless \%args, $class; } sub DoIt { my ($self) = @_; printf "In the base DoIt for an object of class %s\n", ref $self; } sub DoOther { my ($self) = @_; printf "In the base DoOther for an object of class %s\n", ref $sel +f; } package Derived; use parent 'Base'; sub new { my ($class, %args) = @_; return $class->SUPER::new (%args); } sub DoIt { my ($self) = @_; printf "In the derived DoIt for an object of class %s\n", ref $sel +f; $self->SUPER::DoIt (); } sub DoExtra { my ($self) = @_; printf "In the derived DoExtra for an object of class %s\n", ref $ +self; } package main; my $obj = Derived->new (); $obj->DoIt (); $obj->DoOther (); $obj->DoExtra ();

    Prints:

    In the derived DoIt for an object of class Derived In the base DoIt for an object of class Derived In the base DoOther for an object of class Derived In the derived DoExtra for an object of class Derived

    Note in particular that use parent is used to hook up the inheritance chain and that the derived constructor 'calls up' for the the base class's blessing.

    True laziness is hard work
Re: blessed confusion
by bluescreen (Friar) on Sep 08, 2010 at 23:47 UTC

    the problem is in the URL package you have $url in the bless call and it is not defined at that point, it should be $up

    Strong recomendation:

    use strict; use warnings;

    For all code: packages, scripts, ...

      Those are already in use.

      The code was combination made-up for example (started that way), then got lazy and copied in some actual lines resulting in '$url' (in my example), being blessed one place, while the actual code used '$up' (url pointer). For the example, I thought url was more clear, though in my code, use of 'p' suffixes is done enough to make the use of '$u' in class '_U_rl', sufficient to me to mean 'url_pointer'.

Re: blessed confusion
by ikegami (Patriarch) on Sep 09, 2010 at 02:58 UTC

    You got that error because $package doesn't contain the string 'URL' or 'URL::Storable'. It contains a reference. Try running your script using

    perl -MCarp::Always script.pl

    to find which call to new is bad.

    Carp::Always

      Slightly more convenient for me is to have the call-back dumped for warnings as well as errors and to make it conditional on the prog's development status. So the following is already in this prog (as it's in my 'new program template'...
      my $Devel=1; ... if ($Devel) { use Carp qw(cluck confess); $SIG{__WARN__} = $SIG{__DIE__} = sub { &dumpfromstart; $Carp::CarpLevel=1; confess @_ } }
      The 'dumpfromstart' is specific to this program, as it uses Data::Dumper to try to dump out data from the starting object and the current if it is different and non-null.

      For me, it's easier if every program starts with initializations like above than having to type invocations on the command line when there are problems.

        Carp::Always does extend warnings.

        >perl -MCarp::Always -we"sub f { print undef } f()" Use of uninitialized value in print at -e line 1 main::f() called at -e line 1

        It's also conditional (on a command line option), which is surely better than changing the source code to do debugging.

        Either way, you didn't report back any findings. Did you solve your problem?