Perl passing argument into eval - perl

I'm facing an issue using eval function.
Indeed I have some function name inside a SQL database, my goal is to execute those functions within perl (after retrieve in SQL).
Here is what I'm doing, considering that $RssSource->{$k}{Proceed} contains "&test" as a string retrieved from SQL:
my $str2 = "ABCD";
eval "$RssSource->{$k}{Proceed}";warn if $#;
sub test
{
my $arg = shift;
print "fct TEST -> ", $row, "\n";
}
This is working correctly and display:
fct TEST ->
However I would like to be able to pass $str2 as an argument to $RssSource->{$k}{Proceed} but I don't know how, every syntax I tried return an error:
eval "$RssSource->{$k}{Proceed}$str2"
eval "$RssSource->{$k}{Proceed}($str2)"
eval "$RssSource->{$k}{Proceed}"$str2
eval "$RssSource->{$k}{Proceed}"($str2)
May someone tell me how to properly pass an argument to the evaluated function?
Thanks a lot for your help
Regards.
Florent

I'm going to assume that $RssSource->{$k}{Proceed} always contain name or &name, otherwise what you are asking doesn't make much sense.
my $func_name = $RssSource->{$k}{Proceed};
$func_name =~ s/&//;
my $func_ref = \&$func_name; # Works with strict on!
$func_ref->(#args);
If you want to add some error checking, the following will check if the sub can be called:
defined(&$func_ref)

If the string you are evaling always is a sub invocation, you can construct the eval string in one of these ways:
$RssSource->{$k}{Proceed} . '($str2)'
(most general), or
$RssSource->{$k}{Proceed} . "(\"$str2\")"
(inelegant)
Here are the problems your solutions ran into:
eval "$RssSource->{$k}{Proceed}$str2" evaluates to eval "&testABCD". This sub doesn't exist.
eval "$RssSource->{$k}{Proceed}($str2)" evaluates to "&test(ABCD)". Bareword not allowed.
eval "$RssSource->{$k}{Proceed}"$str2 A string has to be followed by some sort of operator, not another variable.
eval "$RssSource->{$k}{Proceed}"($str2) You are trying to call a string as a function. This is not supported in Perl.

If you can change the data in your database to contain just a function name, that is, test rather than &test, you can call a function by a symbolic reference, rather than using eval:
$fn="test";
&{$fn}("argument")

You do not need eval if, as you say, your database just contains function names. You can use them as symbolic references (but please remove the &). The modern way to do it would not be using the & to dereference it but to use the arrow operator:
{
no strict 'refs'; # hopefully you have strict on already...
$RssSource->{$k}{Proceed}->($str2);
}

Very similar to ikegami's answer, using the can method, which is more my taste. TIMTOWTDI.
my $func_name = $RssSource->{$k}{Proceed};
$func_name =~ s/&//;
my $func_ref = __PACKAGE__->can($func_name)
or die "No function named $func_name";
$func_ref->(#args);

Related

How to access a sub through symbol table for main:: and name of sub in a scalar variable

Suppose I have a function foo (or ::foo, or main::foo if you prefer), and I define
use strict;
my $sub_name = 'foo';
I want to invoke foo indirectly, as "the function whose name is stored in $sub_name". (For the sake of this example, assume that the invocation should pass the list 1, 2, 3 as arguments.)
I know that there's a way to do this by working with the symbol table for main:: directly, treating it like a hash-like data structure.
This symbol-table incantation is what I'm looking for.
I've done this sort of thing many times before, but I have not programmed Perl in many years, and I no longer remember the incantation.
(I'd prefer to do this without having to resort to no strict, but no biggie if that's not possible.)
I'd simply use a symbolic reference.
my $sub = \&$qualified_sub_name; # \&$symbol is except from strict 'refs'.
$sub->()
But you requested that we avoid using symbolic reference. That's way too complex. (It's also might not handle weird but legit misuse of colons.)
my $pkg = \%::;
my $sub_name = $qualified_sub_name;
$pkg = $pkg->{$1} while $sub_name =~ s/^(.*?::)//sg;
my $sub = $pkg->{$sub_name};
$sub = *{ $pkg->{$sub_name} }{CODE}
if ref(\$sub) eq 'GLOB'; # Skip if glob optimized away.
$sub->()
You can use can:
my $sub_name = 'foo';
my $coderef = main->can($sub_name);
$coderef->(#args);
As others have mentioned, you should note that this can return also methods like "can" or "isa".
Also, if $sub_name contains Some::Module::subname, this will also be called.
If you're not sure what's in $sub_name, you probably want a different approach.
Use this only if you have control over $sub_name and it can contain only expected values. (I assumed this, that's why I wrote this answer.)

perl: printing object properties

I'm playing a bit with the Net::Amazon::EC2 libraries, and can't find out a simple way to print object properties:
This works:
my $snaps = $ec2->describe_snapshots();
foreach my $snap ( #$snaps ) {
print $snap->snapshot_id . " " . $snap->volume_id . "\n";
}
But if I try:
print "$snap->snapshot_id $snap->volume_id \n";
I get
Net::Amazon::EC2::Snapshot=HASH(0x4c1be90)->snapshot_id
Is there a simple way to print the value of the property inside a print?
$snap->volume_id is not a property, it is a method call. While you could interpolate a method call inside a string, it is exceedingly ugly.
To get all the properties of an object you can use the module Data::Dumper, included with core perl:
use Data::Dumper;
print Dumper($object);
Not in the way you want to do it. In fact, what you're doing with $snap->snapshot_id is calling a method (as in sub). Perl cannot do that inside a double-quoted string. It will interpolate your variable $snap. That becomes something like HASH(0x1234567) because that is what it is: a blessed reference of a hash.
The interpolation only works with scalars (and arrays, but I'll omit that). You can go:
print "$foo $bar"; # scalar
print "$hash->{key}"; # scalar inside a hashref
print "$hash->{key}->{moreKeys}->[0]"; # scalar in an array ref in a hashref...
There is one way to do it, though: You can reference and dereference it inside the quoted string, like I do here:
use DateTime;
my $dt = DateTime->now();
print "${\$dt->epoch }"; # both these
print "#{[$dt->epoch]}"; # examples work
But that looks rather ugly, so I would not recommend it. Use your first approach instead!
If you're still interested in how it works, you might also want to look at these Perl FAQs:
What's wrong with always quoting "$vars"?
How do I expand function calls in a string?
From perlref:
Here's a trick for interpolating a subroutine call into a string:
print "My sub returned #{[mysub(1,2,3)]} that time.\n";
The way it works is that when the #{...} is seen in the double-quoted
string, it's evaluated as a block. The block creates a reference to an
anonymous array containing the results of the call to mysub(1,2,3) .
So the whole block returns a reference to an array, which is then
dereferenced by #{...} and stuck into the double-quoted string. This
chicanery is also useful for arbitrary expressions:
print "That yields #{[$n + 5]} widgets\n";
Similarly, an expression that returns a reference to a scalar can be
dereferenced via ${...} . Thus, the above expression may be written
as:
print "That yields ${\($n + 5)} widgets\n";
Stick with the first sample you showed. It looks cleaner and is easier to read.
I'm answering this because it took me a long time to find this and I feel like other people may benefit as well.
For nicer printing of objects use Data::Printer and p():
use DateTime;
use Data::Printer;
my $dt = DateTime->from_epoch( epoch => time );
p($dt);
The PERL translator has limited depth perception within quotes. Removing them should solve the problem. Or just load the real values into a simple variable that you can print within the quotes. Might need to do that if you have objects which contain pointers to other objects:
SwissArmyChainSaw =/= PureMagic:
print("xxx".$this->{whatever}."rest of string\n");
The problem is that $snap is being interpolated inside the string, but $snap is a reference. As perldoc perlref tells us: "Using a reference as a string produces both its referent's type, including any package blessing as described in perlobj, as well as the numeric address expressed in hex."
In other words, within a string, you can't dereference $snap. Your first try was the correct way to do it.
I agree with most comment, stick to concatenation for easy reading. You can use
say
instead of print to spare of using the "\n".

How to interpret a string as Perl code and execute it without eval

I understand that "string-eval" can be used for it. But I am looking for some alternative to achieve it without eval. Reason being "string-eval" fail the Perl critic and as per client policy I can not use it.
What are some alternative to do that without eval?
Here is what I am trying to do:
I am passing a dynamic generated code reference to a function. This function evaluates this code reference and assign the result to a variable.
Perl critic objects to string-eval because "interpreting a string as perl code and executing it" is generally a poor solution to any problem.
You can't work around it by just finding another function to do the same thing (well, you could, but that would be entirely missing the point).
Coderefs do not need eval to run. Simply dereference a coderef to invoke it. This is explained in perldoc perlref.
my $code = sub {
my ($name) = #_;
say "Hi, $name!";
};
$code->('rpg');
This works for me.
#!/usr/bin/perl
use strict;
use warnings;
sub exec_code {
my ($c) = #_;
return &{$c};
}
my $coderef = sub {
print "Hello, from sub";
};
exec_code($coderef);

perl encapsulate single variable in double quotes

In Perl, is there any reason to encapsulate a single variable in double quotes (no concatenation) ?
I often find this in the source of the program I am working on (writen 10 years ago by people that don't work here anymore):
my $sql_host = "something";
my $sql_user = "somethingelse";
# a few lines down
my $db = sub_for_sql_conection("$sql_host", "$sql_user", "$sql_pass", "$sql_db");
As far as I know there is no reason to do this. When I work in an old script I usualy remove the quotes so my editor colors them as variables not as strings.
I think they saw this somewhere and copied the style without understanding why it is so. Am I missing something ?
Thank you.
All this does is explicitly stringify the variables. In 99.9% of cases, it is a newbie error of some sort.
There are things that may happen as a side effect of this calling style:
my $foo = "1234";
sub bar { $_[0] =~ s/2/two/ }
print "Foo is $foo\n";
bar( "$foo" );
print "Foo is $foo\n";
bar( $foo );
print "Foo is $foo\n";
Here, stringification created a copy and passed that to the subroutine, circumventing Perl's pass by reference semantics. It's generally considered to be bad manners to munge calling variables, so you are probably okay.
You can also stringify an object or other value here. For example, undef stringifies to the empty string. Objects may specify arbitrary code to run when stringified. It is possible to have dual valued scalars that have distinct numerical and string values. This is a way to specify that you want the string form.
There is also one deep spooky thing that could be going on. If you are working with XS code that looks at the flags that are set on scalar arguments to a function, stringifying the scalar is a straight forward way to say to perl, "Make me a nice clean new string value" with only stringy flags and no numeric flags.
I am sure there are other odd exceptions to the 99.9% rule. These are a few. Before removing the quotes, take a second to check for weird crap like this. If you do happen upon a legit usage, please add a comment that identifies the quotes as a workable kludge, and give their reason for existence.
In this case the double quotes are unnecessary. Moreover, using them is inefficient as this causes the original strings to be copied.
However, sometimes you may want to use this style to "stringify" an object. For example, URI ojects support stringification:
my $uri = URI->new("http://www.perl.com");
my $str = "$uri";
I don't know why, but it's a pattern commonly used by newcomers to Perl. It's usually a waste (as it is in the snippet you posted), but I can think of two uses.
It has the effect of creating a new string with the same value as the original, and that could be useful in very rare circumstances.
In the following example, an explicit copy is done to protect $x from modification by the sub because the sub modifies its argument.
$ perl -E'
sub f { $_[0] =~ tr/a/A/; say $_[0]; }
my $x = "abc";
f($x);
say $x;
'
Abc
Abc
$ perl -E'
sub f { $_[0] =~ tr/a/A/; say $_[0]; }
my $x = "abc";
f("$x");
say $x;
'
Abc
abc
By virtue of creating a copy of the string, it stringifies objects. This could be useful when dealing with code that alters its behaviour based on whether its argument is a reference or not.
In the following example, explicit stringification is done because require handles references in #INC differently than strings.
$ perl -MPath::Class=file -E'
BEGIN { $lib = file($0)->dir; }
use lib $lib;
use DBI;
say "ok";
'
Can't locate object method "INC" via package "Path::Class::Dir" at -e line 4.
BEGIN failed--compilation aborted at -e line 4.
$ perl -MPath::Class=file -E'
BEGIN { $lib = file($0)->dir; }
use lib "$lib";
use DBI;
say "ok";
'
ok
In your case quotes are completely useless. We can even says that it is wrong because this is not idiomatic, as others wrote.
However quoting a variable may sometime be necessary: this explicitely triggers stringification of the value of the variable. Stringification may give a different result for some values if thoses values are dual vars or if they are blessed values with overloaded stringification.
Here is an example with dual vars:
use 5.010;
use strict;
use Scalar::Util 'dualvar';
my $x = dualvar 1, "2";
say 0+$x;
say 0+"$x";
Output:
1
2
My theory has always been that it's people coming over from other languages with bad habits. It's not that they're thinking "I will use double quotes all the time", but that they're just not thinking!
I'll be honest and say that I used to fall into this trap because I came to Perl from Java, so the muscle memory was there, and just kept firing.
PerlCritic finally got me out of the habit!
It definitely makes your code more efficient, but if you're not thinking about whether or not you want your strings interpolated, you are very likely to make silly mistakes, so I'd go further and say that it's dangerous.

why can't I use this in my subroutine?

I have the following:
$sql="SELECT * FROM table WHERE name = ? ";
&checkDB( $sql , "bob" , "while" );
sub checkDB(){
my $sth=$dbh->prepare($_[0]) or warn "$DBI::errstr";
$sth->execute( $_[1] ) or warn "$DBI::errstr";
print $_[2] . "\n"; # this works
$_[2] ( my #rows= $sth -> fetchrow() ) { # this doesn't work
blah, blah, blah
}
}
I pass my sql statement, 'bob' and either a "while" or "unless" variable into the subroutine.
My subroutine will let me pass the "while" variable (it will print "while\n") but it won't let me use it to fetchrows. What am I doing wrong?
my exact error is "syntax error at script.pl near "}" "....works fine if I substitute $_[2] with the actual word "while"
Because while and "while" are different things.
You can't substitute arbitrary language constructs for strings.
It is not possible to use variable in place of keyword in perl. If you really must do such constructs, look at eval - it lets you run arbitrary string as perl code.
I think what you would need here is an eval, however this has evil written all over it. You definitely want to be sure that nothing in that code is tainted (coming from user). I think you probably should think very hard about why you would want to do this and if there isn't a better way (two different functions for example). I know we programmers like to be lazy, but evals are dangerous.