Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I avoid these strange numeric warnings when using Variable::Magic?

Tags:

perl

To facilitate the transition from one system to another, I'm writing code that aims to be usable as a drop-in replacement to a blessed object, but that internally structures its data differently. This works well, except that there are parts of the code that touch the object's internal data directly.

Unless I want to modify the calling code (which I'd rather avoid) I need to find a way to re-route those assignments to the new internal structure of this class. So I reached for Variable::Magic:

use Test2::V0;
use Variable::Magic qw( cast wizard );

my $wrapper = {
    static  => { name => "static"  },
    dynamic => { name => "dynamic" },
};

cast $wrapper->{name}, wizard
    get => sub { ${$_[0]} = $wrapper->{dynamic}{name} },
    set => sub { $wrapper->{dynamic}{name} = ${$_[0]} };

is $wrapper->{name}, "dynamic";

$wrapper->{name} = "zoom";
is $wrapper->{name}, "zoom";

is $wrapper->{dynamic}{name}, "zoom";
is $wrapper->{static}{name}, "static";

done_testing;

The code above works in that all tests pass. It does what I want it to do. However, it prints the following:

Argument "dynamic" isn't numeric in list assignment at .../Test2/Tools/Compare.pm line 104.
# Seeded srand with seed '20250416' from local date.
ok 1
Argument "zoom" isn't numeric in scalar assignment at -e line 16.
Argument "zoom" isn't numeric in list assignment at .../Test2/Tools/Compare.pm line 104.
ok 2
ok 3
ok 4
1..4

How can I get rid of these warnings? Under normal circumstances I could use no warnings 'numeric', but since these warnings are being raised at the call site (see the ones raised in Test2::Tools::Compare, for example) this is not an option. Or is there a different way to achieve this that doesn't rely on Variable::Magic? An earlier version of this used Sentinel, but that did not survive the assignment.

Bonus points for explaining what is going on here. Why are these warnings being raised? And why are they complaining about a value not being numeric for non-numeric operations (list and scalar assignment)?

like image 426
jja Avatar asked Oct 19 '25 19:10

jja


2 Answers

Using Variable::Magic requires code that accesses the magic variable to correctly handle magic, and it requires a relatively slow Perl sub call each time the variable is accessed. By using Variable::Magic correctly, we can avoid the warnings (as shown by the earlier answer). But we can also solve the problem—and avoid the downsides of Variable::Magic—by avoiding Variable::Magic entirely.

You can make $wrapper->{name} a proxy for $wrapper->{dynamic}{name} using aliases.

use experimental qw( refaliasing );
\( $wrapper->{name} ) = \( $wrapper->{dynamic}{name} );

or

use Data::Alias qw( alias );
alias $wrapper->{name} = $wrapper->{dynamic}{name};
like image 95
ikegami Avatar answered Oct 22 '25 11:10

ikegami


The getter and setter is expected to return a integer as per the documentation for Variable::Magic:

The returned value [of the callbacks passed to set and get] is coerced into a signed integer, which is then passed straight to the perl magic API

However, the getter returns whatever value the proxied variable currently has, and the setter returns whatever new value was just given to the proxied variable.

When the getter is called when is fetches the value of the magic variable, it returns a string that doesn't look like a number. This string is coerced into a signed integer as documented, and this results in a warning (just like int("dynamic") would).

A similar results occurs when the setter is called when a new value is assigned to the magic variable.

The solution is to have the getter and setter return a number. Any number.

cast $wrapper->{name}, wizard
    get => sub { ${$_[0]} = $wrapper->{dynamic}{name}; 0 },
    set => sub { $wrapper->{dynamic}{name} = ${$_[0]}; 0 };

This makes the warnings go away while keeping the same behaviour as the original.

like image 31
jja Avatar answered Oct 22 '25 10:10

jja



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!