Packages and returning errors

B

Bigus

Hi

I'm attempting to put together my first package, but there's something
pretty fundamental that I haven't sussed out:

Say you are using the DBI module and you've just performed an query and you
want to see if it returned any errors, you could do something like:

if ( $db->errstr ) {print "That failed - ".$db->errstr;}

What I don't get is what exactly is "errstr"? I guess it's a function but
how would it work if I have a bit of code that goes:

if ( $blah < $blahh ) {
$rtnError = "Below practical size";
return;
}
and I then want to test $instance->rtnError in the same way as DBI.

Thanks
Bigus
 
B

Brian McCauley

Bigus said:
I'm attempting to put together my first package, but there's something
pretty fundamental that I haven't sussed out:

Say you are using the DBI module and you've just performed an query and you
want to see if it returned any errors, you could do something like:

if ( $db->errstr ) {print "That failed - ".$db->errstr;}

What I don't get is what exactly is "errstr"? I guess it's a function but
how would it work if I have a bit of code that goes:

if ( $blah < $blahh ) {
$rtnError = "Below practical size";
return;
}
and I then want to test $instance->rtnError in the same way as DBI.

The last error message is conceptually no different from any other bit
of instance data. However you are storing the rest of your instance
data, store the error the same way. Create an accesor...

sub rtnError {
my $self = shift;
# Do whatever is needed to retrice the last error from $self
# for example if this class is based on a blessed hash you could
# say something like...
$self->{last_error};
}

--
\\ ( )
. _\\__[oo
.__/ \\ /\@
. l___\\
# ll l\\
###LL LL\\
 
A

Anno Siegel

Bigus said:
Hi

I'm attempting to put together my first package, but there's something
pretty fundamental that I haven't sussed out:

Say you are using the DBI module and you've just performed an query and you
want to see if it returned any errors, you could do something like:

if ( $db->errstr ) {print "That failed - ".$db->errstr;}

What I don't get is what exactly is "errstr"? I guess it's a function but
how would it work if I have a bit of code that goes:

It is a method (i.e. a special kind of subroutine) that is applicable to
the object $db. I'd guess it returns the last error that happened with
the database.
if ( $blah < $blahh ) {
$rtnError = "Below practical size";
return;
}
and I then want to test $instance->rtnError in the same way as DBI.

What is "$instance"? And why do you want to use a method to return
you own error strings? Mind you, there might be a reason, but offhand
I don't see it.

If you want that behavior, you'll have to build your own class with
am errstr method and take care that $instance is a correctly initialized
object in it.

Anno
 
T

Tore Aursand

The last error message is conceptually no different from any other bit
of instance data. However you are storing the rest of your instance
data, store the error the same way. Create an accesor... [...]

Amen. A little digression, though; I've learned to _never_ return only
the last error message, but _all_ the error messages (or warnings) in my
application(s).

Why? Well. There are times when the _last_ error message really is a
error caused by a previous error "longer up in the error stack" (or
whatever I should call it).

So - at least make sure that your error accessor can return an array, too;

sub errstr {
my $self = shift;
# blah
my @errors = @{ $self->{'_errors'} };
return ( wantarray ) ? @errors : $errors[-1];
}
 
B

Bigus

Brian McCauley said:
The last error message is conceptually no different from any other bit
of instance data. However you are storing the rest of your instance
data, store the error the same way. Create an accesor...

sub rtnError {
my $self = shift;
# Do whatever is needed to retrice the last error from $self
# for example if this class is based on a blessed hash you could
# say something like...
$self->{last_error};
}

I've just been reading the object-oriented tutorial
(http://www.perldoc.com/perl5.6/pod/perltoot.html) and looking through
various packages in my site/lib directory and it makes me realise how much
of Perl I really don't understand (or have difficulty grasping) and I've
been using it for 2 years :-(

Regarding the above rtnError sub, I don't understand what the "my $self =
shift;" line is for. The perldoc function guide says about shift (which is
not incidentally a function I've used before):

"Shifts the first value of the array off and returns it, shortening the
array by 1 and moving everything down. If there are no elements in the
array, returns the undefined value. If ARRAY is omitted, shifts the @_ array
within the lexical scope of subroutines and formats, and the @ARGV array at
file scopes or within the lexical scopes established by the eval '', BEGIN
{}, INIT {}, CHECK {}, and END {} constructs".

Errr, right.. it loses me at the "lexical scopes" point but the first bit
would seem to be the relevant bit in this case anyway. Since you are not
specifying anything after the shift keyword it would look for anything
contained in @_, which would be nothing if you were just calling the
rtnError method to return an error string to you defined elsewhere in the
module. So, what's the point of that line?

Another thing I don't get about shift is why one would want to use it
anyway. If an array of values are passed to a sub, shift will chop the first
one off as it returns it, which seems rather destructive, especially when
you could just as easily reference it with $_[0] and that would leave the @_
array intact.

The line "$self->{last_error}" - in terms of hashes, the dereferencer is
not something I've used before. I've used standard hashes like
"$self{last_error}", and hashes within hashes like
"$self{last_error}{blah}", but why is "$self->{last_error}" different than
"$self{last_error}"?

Back to my package (which I am currently thinking might be out of my depth,
and am considering going back to the idea of just writing a standard CGI
script instead!), I have the following code:

package GD::MyMod;
use strict;
use GD;

# new constructer expects 2 values passed
sub new {
if ( $_[0] < 100 or $_[1] < 100 ) {
my $error = "";
$error = "Specify values >= 100";
return;
}
else {
# do stuff
}
}

sub rtnError {
return $error;
}
1;

Then, in the CGI script that calls it, I have:

#!c:\perl\bin\perl.exe
use GD::MyMod;
$im = new GD::MyMod(20,20);
print "Content-type:text/html\n\n";

if($im->rtnError) {
print $im->rtnError;
}
else {
print "no error";
}

That generates an Apache 500 server error and the erro log shows:

"Global symbol "$error" requires explicit package name at blah"

the line it refers to is the print "no error"; one. So, if I add the line
"my $error = shift;" before it (I don't know what it does, but the OO
tutorial uses it in it's subs) then I don't get a server error but a blank
page and the error log says:

Can't call method "rtnError" on an undefined value at blah, where blah is
the line if($im->rtnError) in my CGI script.

So, it seems that somethings not right with the new sub.. any ideas what?

Thanks
Bigus
 
B

Brian McCauley

Bigus said:
I've just been reading the object-oriented tutorial
(http://www.perldoc.com/perl5.6/pod/perltoot.html) and looking through
various packages in my site/lib directory and it makes me realise how much
of Perl I really don't understand (or have difficulty grasping) and I've
been using it for 2 years :-(

Regarding the above rtnError sub, I don't understand what the "my $self =
shift;" line is for.
If an array of values are passed to a sub, shift will chop the first
one off as it returns it, which seems rather destructive, especially when
you could just as easily reference it with $_[0] and that would leave the @_
array intact.

Using $_[0] and so on is ugly and slow.

The only time one usually does that is if one wants to modify
arguments.

The only time you usually need to worry about keeping @_ intact is if
you are going use a goto.

The "self" argument is logically separate from the rest of @_ so to
make this clear I tend to write:

sub not_a_method {
my ( $arg1, $arg2 ) = @_;
# Do stuff
}

sub is_a_instance_method {
my $self = shift;
my ($arg1, $arg2 ) = @_;
# Do stuff
}

sub is_as_class_method {
my $class = shift;
my ($arg1, $arg2 ) = @_;
# Do stuff
}

Some other people prefer to use either list assignment or shift to
unpack the whole of @_ in all cases. It's a style thing.

There are also some other people prefer to use subscripts to unpack
the whole of @_ in all cases. That too is a style thing, a _poor_
style thing!
The perldoc function guide says about shift (which is
not incidentally a function I've used before):

In 2 years! Wow!
"Shifts the first value of the array off and returns it, shortening the
array by 1 and moving everything down. If there are no elements in the
array, returns the undefined value. If ARRAY is omitted, shifts the @_ array
within the lexical scope of subroutines and formats, and the @ARGV array at
file scopes or within the lexical scopes established by the eval '', BEGIN
{}, INIT {}, CHECK {}, and END {} constructs".

Errr, right.. it loses me at the "lexical scopes" point but the first bit
would seem to be the relevant bit in this case anyway. Since you are not
specifying anything after the shift keyword it would look for anything
contained in @_, which would be nothing if you were just calling the
rtnError method to return an error string to you defined elsewhere in the
module.

Bzzt! It would be nothing if you were just calling the rtnError
_subroutine_ but if you are calling an instance method then $_[0] is
the object on which the method is being called.
Another thing I don't get about shift is why one would want to use it
anyway.

Go back at read the OO tutorial again. If there are bits you don't
understand feel free to come here and ask for help. Comming here and
asking for help without reading the tutorial first is a waste of
everyone's time. The answers you get here will probably be inferior
to the tutorial. And it there's anything unclear in the tutorial it
won't help it get fixed.
The line "$self->{last_error}" - in terms of hashes, the dereferencer is
not something I've used before. I've used standard hashes like
"$self{last_error}", and hashes within hashes like
"$self{last_error}{blah}", but why is "$self->{last_error}" different than
"$self{last_error}"?

Ah, before you read the OO tutorial you need to read the reference
tutorial. You've come here asking for some coaching with your running
and you've just admitted you've never walked.
Back to my package (which I am currently thinking might be out of my
depth,

You are. Way out of your depth.
and am considering going back to the idea of just writing a standard CGI
script instead!),

"Chalk and cheese". (Actually I hate that metaphor - cheese and chalk
are both roughly hologeneous solids. They have a lot in common. I
think "Chalk and chastisement" would be a better metaphor).
I have the following code:

package GD::MyMod;
use strict;
use GD;

# new constructer expects 2 values passed
sub new {
if ( $_[0] < 100 or $_[1] < 100 ) {

$_[0] will be 'CG::MyGod' if new is called as a method of class
GD::MyMod.

Did you perhaps "forget" to enable warnings?

I'm sure we've explained to you before how rude it is to come here and
ask sentient entities to help you when you've not already asked your
computer.

Are you _trying_ to piss us off?
my $error = "";

Always use the natual representation of things. If you want to
represent the concept of "no value" in a scalar use the special value
undef. Rather helpfully when you declare a variable using my()
without an explicit initializtion it will be set to the natural
representation of "no value" for that type of variable.
$error = "Specify values >= 100";
return;
}

You are not saving that value anywhere that persists after the return
is executed.

Note your constructor does not construct anything if it gets an error.

You need to fix the scope of $error by moving its declaration outside
the subroutine.
else {
# do stuff

I hope that some of that "stuff" includes actually constructing a
CG::MyGod object. Otherwise new() is not a constuctor.
}
}

sub rtnError {
return $error;
}

$error is out of scope.

Did you perhaps "forget" to enable strictures?

I'm sure we've explained to you before how rude it is to come here and
ask sentient entities to help you when you've not already asked your
computer.

Are you _trying_ to piss us off?

You need to fix the scope of $error.
1;

Then, in the CGI script that calls it, I have:

#!c:\perl\bin\perl.exe
use GD::MyMod;
$im = new GD::MyMod(20,20);

It's probably bese not to get into the habit of using the indirect
ibject syntax. Oh, and you forgot to declare $im.

my $im = GD::MyMod->new(20,20);
print "Content-type:text/html\n\n";

if($im->rtnError) {
print $im->rtnError;
}
else {
print "no error";
}

That generates an Apache 500 server error and the erro log shows:

"Global symbol "$error" requires explicit package name at blah"

You should not be runnning your scripts under a web server until
you've at least syntax checked them.
the line it refers to is the print "no error"; one.

No way! Look again.
So, if I add the line "my $error = shift;" before it (I don't know
what it does, but the OO tutorial uses it in it's subs)

Don't just do random things. How did you expect $_[0] to contain your
error message?
then I don't get a server error but a blank page

I do not believe you.
and the error log says:

Can't call method "rtnError" on an undefined value at blah, where blah is
the line if($im->rtnError) in my CGI script.

Yep that is correct.
So, it seems that somethings not right with the new sub.. any ideas what?

You decided that your constuctor would not constuct an object if it
didn't like it's arguments. If it didn't constuct an object you
cannot use the object it constucted to look up the error because there
is no object.

You need to call the rtnError as a class method, just as you did with
new.

Having a class method to return your error is just one approach.
There are other equally good ones.

--
\\ ( )
. _\\__[oo
.__/ \\ /\@
. l___\\
# ll l\\
###LL LL\\
 
B

Ben Morrow

Brian McCauley said:
"Chalk and cheese". (Actually I hate that metaphor - cheese and chalk
are both roughly hologeneous solids. They have a lot in common. I
think "Chalk and chastisement" would be a better metaphor).

Thank you for that... I like :).

It's probably bese not to get into the habit of using the indirect
ibject syntax. Oh, and you forgot to declare $im.

my $im = GD::MyMod->new(20,20);

Might I ask why you say this? I use it all the time, as a matter of
style, because I prefer to be able to avoid parenthesising function
calls where possible (so I would use
my $im = new GD::MyMod 20, 20;
rather than with the parens): is there some gotcha with this I've not
realised?

Ben
 
S

Sam Holden

Thank you for that... I like :).



Might I ask why you say this? I use it all the time, as a matter of
style, because I prefer to be able to avoid parenthesising function
calls where possible (so I would use
my $im = new GD::MyMod 20, 20;
rather than with the parens): is there some gotcha with this I've not
realised?

perldoc perlobj

The section headed: "Indirect Object Syntax"

The phrase "the grief when it messes up just isn't worth the years of
debugging it will take you to track down such subtle bugs." might prompt
you to reconsider your choice of style.

The docs explain the problem better than I could, so I won't try
and do so.
 
M

Matthew Braid

Bigus said:
I've just been reading the object-oriented tutorial
(http://www.perldoc.com/perl5.6/pod/perltoot.html) and looking through
various packages in my site/lib directory and it makes me realise how much
of Perl I really don't understand (or have difficulty grasping) and I've
been using it for 2 years :-(

Regarding the above rtnError sub, I don't understand what the "my $self =
shift;" line is for. The perldoc function guide says about shift (which is
not incidentally a function I've used before):

When you call a method like $object->method(arg1, arg2, arg3...),
method's @_ looks like ($object, arg1, arg2, arg3...). Likewise, if you
call SomeClass->method(arg1, arg2, arg3...), @_ looks like ('SomeClass',
arg1, arg2, arg3...).

So my '$self = shift;' basically just gets the object that the method
was called on (or the package).
The line "$self->{last_error}" - in terms of hashes, the dereferencer is
not something I've used before. I've used standard hashes like
"$self{last_error}", and hashes within hashes like
"$self{last_error}{blah}", but why is "$self->{last_error}" different than
"$self{last_error}"?

All objects are blessed _references_. This means $self is not a hash -
its a reference to a hash. You can't do $self{blah} because that makes
perl look for a hash called %self (which probably doesn't exist in this
scope). $self->{blah} means 'get the scalar value of the key blah from
the hash referenced to by the scalar named self'. Simply put, if you've
got an array, hash or code reference you need to use the -> to use it
like an array, hash or subroutine call (respectively).
Back to my package (which I am currently thinking might be out of my depth,
and am considering going back to the idea of just writing a standard CGI
script instead!), I have the following code:

package GD::MyMod;
use strict;
use GD;

# new constructer expects 2 values passed
sub new {
if ( $_[0] < 100 or $_[1] < 100 ) {
my $error = "";
$error = "Specify values >= 100";
return;
}
else {
# do stuff
}
}

This will not do what you expect - $_[0] will most likely be the string
'GD::MyMod'. See the shift stuff above. Plus $error needs to be shifted
out of the new sub and put in the package's scope if you want other
functions in the package to see it.
sub rtnError {
return $error;
}

This does a little more than you probably expect. $error is a package
global (once you've made it one - at the moment its an unknown
variable), so its shared amongst all objects of type GD::MyMod (and is
even accessible directly from the class without instantiating an
object). I find error accessors like this useful, your mileage may vary.
That generates an Apache 500 server error and the erro log shows:

"Global symbol "$error" requires explicit package name at blah"

That's because you should have put

my $error = "";

outside the sub new. At least you appear to be use'ing strict!
the line it refers to is the print "no error"; one. So, if I add the line
"my $error = shift;" before it (I don't know what it does, but the OO
tutorial uses it in it's subs) then I don't get a server error but a blank
page and the error log says:

Your new sub returned undef. GD::MyMod's new should have a 'bless' in it
to create the new object and return that object. perldoc -f bless.

MB
 
B

Bigus

[..]
The "self" argument is logically separate from the rest of @_ so to
make this clear I tend to write:

sub not_a_method {
my ( $arg1, $arg2 ) = @_;
# Do stuff
}

sub is_a_instance_method {
my $self = shift;
my ($arg1, $arg2 ) = @_;
# Do stuff
}

sub is_as_class_method {
my $class = shift;
my ($arg1, $arg2 ) = @_;
# Do stuff
}

I like the look of that approach too and the penny has finally dropped about
the first parameter passed to the method being the object. Phew!

[..]
Go back at read the OO tutorial again. If there are bits you don't
understand feel free to come here and ask for help. Comming here and
asking for help without reading the tutorial first is a waste of
everyone's time. The answers you get here will probably be inferior
to the tutorial. And it there's anything unclear in the tutorial it
won't help it get fixed.

There's lots in there I don't understand, but then it is quite alot to take
in in one go. I tend to learn far better by starting a project and tackling
each obstacle as they arise. I can read a tutorial like that and it largely
goes in one ear and out the other (metaphorically), hence my jump in the
deep end approach. Sorry if it is a bit irritating, but it's the only way
that works for me.
Ah, before you read the OO tutorial you need to read the reference
tutorial. You've come here asking for some coaching with your running
and you've just admitted you've never walked.


You are. Way out of your depth.

Well, doing what I want the program to do is not a problem.. if I can just
get to grips with the method/object handling & calling / data returning..
hmmm.

[..]
# new constructer expects 2 values passed
sub new {
if ( $_[0] < 100 or $_[1] < 100 ) {

$_[0] will be 'CG::MyGod' if new is called as a method of class
GD::MyMod.

Did you perhaps "forget" to enable warnings?

I'm sure we've explained to you before how rude it is to come here and
ask sentient entities to help you when you've not already asked your
computer.

Are you _trying_ to piss us off?

No, sorry. I was partly trying to get into good programming habits and put
use strict; in the package, but not in the calling script and wasn't using
warnings. Changed that now.
Always use the natual representation of things. If you want to
represent the concept of "no value" in a scalar use the special value
undef. Rather helpfully when you declare a variable using my()
without an explicit initializtion it will be set to the natural
representation of "no value" for that type of variable.

Noted. Thanks.
You are not saving that value anywhere that persists after the return
is executed.

Note your constructor does not construct anything if it gets an error.

You need to fix the scope of $error by moving its declaration outside
the subroutine.

Yes, I see. I've changed the structure a bit so that it doesn't need a
global var, so the package now looks like this:

===================
package GD::MyMod;

use strict;
use warnings;
use GD;

sub new {
my $class = shift;
my ($width, $height ) = @_;
if ( $width < 100 or $height < 100 ) {
rtnError("Specify values >= 100");
}
else {
# do stuff
}
}

sub rtnError {
my $class = shift;
my $error_msg = shift;
return $error_msg;
}

1;
===================

Is that a better way of doing things than creating a global error var?
I hope that some of that "stuff" includes actually constructing a
CG::MyGod object. Otherwise new() is not a constuctor.

Yes, stuff will initiate a GD image object for subsequent manipulation..

[..]
It's probably bese not to get into the habit of using the indirect
ibject syntax. Oh, and you forgot to declare $im.

my $im = GD::MyMod->new(20,20);

I wondered why you could get away with new before the class name, but
various module documentation used that syntax, so I did, but I like this
more standard approach.
You should not be runnning your scripts under a web server until
you've at least syntax checked them.

I thought that any errors from strict or warnings would go to the browser
since I specified print content-type, but anyway, I'm testing it at the
command line now.

[..]
Yep that is correct.
what?

You decided that your constuctor would not constuct an object if it
didn't like it's arguments. If it didn't constuct an object you
cannot use the object it constucted to look up the error because there
is no object.

You need to call the rtnError as a class method, just as you did with
new.

Having a class method to return your error is just one approach.
There are other equally good ones.

So, how would one actualy define $im as the class instance object thingy? Is
that where this bless command comes in?

If I went with the package code above, then I'd presumably use different
calling ode anyway.. I'm going to try several things now, but if you happen
wot want to give me any clues, please do!

Anyway, many thanks for taking the time to write your reply.

Bigus
 
B

Ben Morrow

Bigus said:
Yes, I see. I've changed the structure a bit so that it doesn't need a
global var, so the package now looks like this:

===================
package GD::MyMod;

use strict;
use warnings;
use GD;

sub new {
my $class = shift;
my ($width, $height ) = @_;
if ( $width < 100 or $height < 100 ) {
rtnError("Specify values >= 100");
}
else {
# do stuff
}
}

sub rtnError {
my $class = shift;
my $error_msg = shift;
return $error_msg;
}

This is completely pointless. You are not saving the value of
$error_msg anywhere.

You probably meant something like this:

{
my $last_error;

sub rtnError {
my $class = shift;
my $error_msg = shift;
$error_msg and $last_error = $error_msg;
return $last_error;
}
}

which will return the last thing it was called with if it is called
with no args.
So, how would one actualy define $im as the class instance object thingy? Is
that where this bless command comes in?

Yes. Understanding bless is critical to writing OO Perl. Read perlboot
until you understand it (you will *need* to understand perlsub,
perlref and perlmod first).

Ben
 
B

Bigus

Ben Morrow said:
This is completely pointless. You are not saving the value of
$error_msg anywhere.

I got my logic wrong then.. I didn't think I'd need to save the error
outside of the subs because I was passing the error to rtnError with the
line:

rtnError("Specify values >= 100");


and then rtnError was sending it back to the calling program with the line:

return $error_msg;

You probably meant something like this:

{
my $last_error;

sub rtnError {
my $class = shift;
my $error_msg = shift;
$error_msg and $last_error = $error_msg;
return $last_error;
}
}

which will return the last thing it was called with if it is called
with no args.


Yes. Understanding bless is critical to writing OO Perl. Read perlboot
until you understand it (you will *need* to understand perlsub,
perlref and perlmod first).

OK thanks.

Bigus
 
B

Ben Morrow

Bigus said:
I got my logic wrong then.. I didn't think I'd need to save the error
outside of the subs because I was passing the error to rtnError with the
line:

rtnError("Specify values >= 100");


and then rtnError was sending it back to the calling program with the line:

return $error_msg;

.... so if you write '$err = $obj->rtnError("Hello world");' you will
get $err = "Hello world"; and if you then write '$err =
$obj->rtnError();' you will get $err = undef. The sub isn't doing
anything.

.... i.e. if you write '$obj->rtnError("Hello world"); ...
$err = $obj->rtnError();' then you will have $err = "Hello world",
which is I suspect what you want.

Ben
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,142
Messages
2,570,819
Members
47,367
Latest member
mahdiharooniir

Latest Threads

Top