Строка перегруженной переменной считается определенной независимо от того, что

#perl

#perl

Вопрос:

В моем скрипте есть следующие строки:

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

Я, естественно, ожидал бы, что при передаче undef это вернет предупреждение Invalid specification в массив, при этом второй элемент будет undef . Вместо этого проверка пройдена, и я получаю сообщение консоли, предупреждающее меня Use of uninitialized value $spec in string о следующей строке.

$spec это объект с перегрузкой строк и чисел, и, к сожалению, написан так, что попытка проверить правдивость в этой конкретной подпрограмме ( if ($spec) например, путем) приводит к глубокой рекурсии и segfault.

Хотя меня интересует, почему именно это происходит, меня больше интересует, как заставить это перестать происходить. Я хочу устранить предупреждение консоли, предпочтительнее без no warnings qw/uninitialized/ . Возможно ли это, и если да, то как мне это сделать?

Комментарии:

1. Попробуйте $spec //= '' или $spec = defined $spec ? "$spec" : ''

2. Отличный вопрос.

Ответ №1:

Вы говорите, что $spec это объект с перегрузкой строки.

Если это так, то вам нужно принудительно преобразовать ее в строковую форму, прежде чем проверять, определена ли она:

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

Исправление для каждого ysth

Как указано в ysth в StrVal, не вызывает перегруженную строку:

перегрузка::StrVal(arg)

Выдает строковое значение аргумента, как при отсутствии перегрузки stringify . Если вы используете это, чтобы получить адрес ссылки (полезно для проверки, указывают ли две ссылки на одно и то же), то вам может быть лучше использовать Scalar::Util::refaddr() , что быстрее.

Поэтому, чтобы добиться этого, попробуйте его другое предложение:

«$ spec» перехватывает предупреждения и обнаруживает неинициализированное предупреждение var. Лучше добавить метод в класс для проверки любого случая, возвращающего undef.

Следующий демонстрирует этот подход:

 #!/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;
    }
}
 

Вывод:

 1..2
ok 1 - $obj_str_defined is defined
ok 2 - $obj_str_undef is undef
 

Комментарии:

1. Я не думаю, что это то, что здесь требуется : overload::StrVal(arg) Gives the string value of arg as in the absence of stringify overloading. . То есть она вернет ссылку на объект в строковой форме, например, Foo=HASH(0xdeadbeef)

2. @ysth Я думаю, вы правы. Знаете ли вы правильный метод принудительного формирования строки без использования конкатенации?

3. «$ spec» перехватывает предупреждения и обнаруживает неинициализированное предупреждение var. Лучше добавить метод в класс для проверки любого случая, возвращающего undef.

4. @ysth расширил исправление. Спасибо, что указали на это.