Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

String overloaded variable is considered defined no matter what

Tags:

perl

I have the following lines in my script:

my $spec = shift;
if (!defined $spec) {
    return ("Invalid specification", undef);
}
$spec = "$spec" // '';

I would naturally expect this to, when passed undef, return the warning Invalid specification in the array, with the second item being undef. Instead, the check is passed, and I get a console message warning me about Use of uninitialized value $spec in string on the next line.

$spec is an object with string and number overloading, and is unfortunately written such that attempting to test for truthiness in this particular subroutine (by way of if ($spec) for instance) results in deep recursion and a segfault.

While I am interested in why, exactly, this is happening, I'm more interested in how to make it stop happening. I want to eliminate the console warning, preferable without no warnings qw/uninitialized/. Is this possible, and if so, how do I do it?

like image 207
Lilith Avatar asked Mar 11 '23 21:03

Lilith


1 Answers

You say that $spec is an object with string overloading.

If that's the case then you need to coerce it into String form before checking for it being defined:

if (! defined overload::StrVal($spec)) {

Correction per ysth

As ysth pointed out in the StrVal does not coerce the overloaded stringification:

overload::StrVal(arg)

Gives the string value of arg as in the absence of stringify overloading. If you are using this to get the address of a reference (useful for checking if two references point to the same thing) then you may be better off using Scalar::Util::refaddr() , which is faster.

Therefore to accomplish this, try his other suggestion of:

"$spec" trapping warnings and detecting the uninitialized var warning. Better to add a method to the class to test for whatever case returns undef.

The following demonstrates this approach:

#!/usr/bin/env perl

use strict;
use warnings;

use Test::More tests => 2;

my $obj_str_defined = StringOverloaded->new("has value");
my $obj_str_undef   = StringOverloaded->new(undef);

ok( is_overloaded_string_defined($obj_str_defined), qq{\$obj_str_defined is defined} );
ok( !is_overloaded_string_defined($obj_str_undef),  qq{\$obj_str_undef is undef} );

sub is_overloaded_string_defined {
    my $obj = shift;

    my $is_str_defined = 1;

    local $SIG{__WARN__} = sub {
        $is_str_defined = 0 if $_[0] =~ /Use of uninitialized value \$obj in string/;
    };

    my $throwaway_var = "$obj";

    return $is_str_defined;
}

{
    # Object with string overloading
    package StringOverloaded;

    use strict;
    use warnings;

    use overload (
        '""' => sub {
            my $self = shift;
            return $$self;    # Dereference
        },
        fallback => 1
    );

    sub new {
        my $pkg  = shift;
        my $val  = shift;
        my $self = bless \$val, $pkg;

        return $self;
    }
}

Output:

1..2
ok 1 - $obj_str_defined is defined
ok 2 - $obj_str_undef is undef
like image 135
Miller Avatar answered May 08 '23 10:05

Miller