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

I have a third-party library that uses shell-style return codes (0 == success, anything else == error). An example chunk of code might be:

my $libobj = Third::Party::Library->new(...); my $item = $libobj->NewItem(...); $item->frobinize(...) and die($libobj->GetErrorCode); $item->blatinate(...) and die($libobj->GetErrorCode);

The ... and ... just does not seem very perlish. Are there other patterns that are more compact, perlish, and easy to maintain? It does seem as compact and readable as can be, but the ... and ... just seems wrong.

Any thoughts?

--MidLifeXis

Replies are listed 'Best First'.
Re: Pattern for a shell-style call + error response
by rsmah (Scribe) on May 04, 2007 at 19:37 UTC
    I suggest setting up a NO_ERR constant that equals 0 and then doing:
    $item->frobinize(...) == NO_ERR 
        or die($libobj->GetErrorCode);
    
Re: Pattern for a shell-style call + error response
by BrowserUk (Patriarch) on May 04, 2007 at 21:18 UTC

    Since you appear to want to die whenever an error occurs, how about using an assertion?

    #! perl -slw use strict; sub good{ 0 } sub bad{ 1 } sub assert { return unless $_[ 0 ]; die join( ' : ', caller, $_[ 1 ] ) . "\n"; } assert good(), 'Good() produced an error'; assert bad(), 'Bad() produced an error'; __END__ C:\test>junk5 main : C:\test\junk5.pl : 13 : Bad() produced an error

    If the error string is always available from the library, you might move that into the sub to save some typing, though it would be easier if GetErrorCode() can be called as a class method rather than an instance method.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Pattern for a shell-style call + error response
by snoopy (Curate) on May 05, 2007 at 02:12 UTC
    You could subclass the library and convert the status codes:
    #!/usr/bin/perl use strict; use warnings; package Third::Party::Library::Call; use base 'Third::Party::Library'; foreach my $method (qw/bazinate barify/) { no strict 'refs'; my $subref = __PACKAGE__->can($method) || die "Unknown method: $method"; *$method = sub {my $stat = &$subref(@_); return !$stat; }; }
    Then things do look a little more perlish:
    my $libobj = Third::Party::Library::Call->newObj; $libobj->bazinate or die $libobj->GetErrorCode;
Re: Pattern for a shell-style call + error response
by BrowserUk (Patriarch) on May 04, 2007 at 19:23 UTC

    I guess you could do

    my $libobj = Third::Party::Library->new(...); my $item = $libobj->NewItem(...); ! $item->frobinize(...) or die($libobj->GetErrorCode); ! $item->blatinate(...) or die($libobj->GetErrorCode);

    but that hardly seems like an improvement.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Pattern for a shell-style call + error response
by Fletch (Bishop) on May 04, 2007 at 19:29 UTC

    It contravenes PBP numbers 63-65, but I'm still partial to die $libobj->GetErrorCode unless $item->frobinize( . . . ) == 0.

      PBP numbers 63-65

      Not having PBP, I don't know what 63-65 might be, but I do know that your way violates perlstyle:

      open FOO, $foo or die "Can't open $foo: $!";
      is better than
      die "Can't open $foo: $!" unless open FOO, $foo;
      because the second way hides the main point of the statement in a modifier. On the other hand
      print "Starting analysis\n" if $verbose;
      is better than
      $verbose and print "Starting analysis\n";
      because the main point isn't whether the user typed -v or not.
      A word spoken in Mind will reach its own level, in the objective world, by its own weight

        If you're going to break the rules, might as well break them all . . . . :)

        My take has always been that using this makes it clear when abnormal termination is the main point (i.e. I expect this condition to be true, so take notice that we can terminate the program at this line).

        I understand why PBP and perlstyle recommend against it; however as I said I'm partial to it. Take it or leave it as you may.

Re: Pattern for a shell-style call + error response
by ash (Monk) on May 05, 2007 at 12:06 UTC
    One common method in other languages is:
    my $ret = $item->frobinize(...); die "Error occured." if $ret;
    It's clean and simple. I like it better than 'and'.

    -- 
    asksh <ask@0x61736b.net>

      The advantage of this method is that you get a handle on $ret, which will usually have some meaning. So you can print it in an error message, or something.
Re: Pattern for a shell-style call + error response
by Moron (Curate) on May 07, 2007 at 11:36 UTC
    If it really conforms to POSIX exit codes, then you need much more complex handling than just inverting with "not", e.g.
    CheckExitCode( $item -> frobinize(...)) or die($libobj->GetErrorCode); sub CheckExitCode { if ($_[0] == -1) { print STDERR "POSIX: failed to execute\n"; return 0; } elsif ($_[0] & 127) { printf STDERR "POSIX: child died with signal %d, %s coredump\n", ($_[0] & 127), ($_[0] & 128) ? 'with' : 'without'; return 0; } elsif ( !$_[0] ) { printf STDERR "POSIX: child exited with value %d\n", $_[0] >> 8; return 0; } 1; }
    __________________________________________________________________________________

    ^M Free your mind!