Pass a subroutine to module and redefine it? - perl

I'm trying to create a module with a method that receives a subroutine and redefines it. I had no problem redefining a subroutine inside the main script but the same syntax doesn't seem to work inside the method:
main.pl
use strict;
use warnings;
use ReDef;
sub orig{
print "Original!\n";
}
orig;
*orig=sub{print "not Original!\n";};
orig;
ReDef::redef(\&orig);
orig;
ReDef.pm
package ReDef;
use strict;
use warnings;
sub redef {
my $ref=shift;
*ref = sub {print "Redefined!";}
}
1;
Test output:
perl main.pl
Original!
Subroutine main::orig redefined at main.pl line 9.
not Original!
not Original!
ReDef::redef() doesn't redefine. The way I see it, the *ref is a coderef and assigning to it another subroutine should change main::orig();
What is the correct syntax?

Your redef function should be like this:
package ReDef;
use strict;
use warnings;
sub redef {
my $ref = shift;
no warnings qw(redefine);
*$ref = sub { print "Redefined!" };
}
And you should NOT call it like this:
ReDef::redef(\&orig);
Instead, you must call it like this:
ReDef::redef(\*orig);
Why? When you call orig, you're looking up the name "orig" via the symbol table, so the redef function needs to be altering the symbol table, so that it can point that name to a different bit of code. Globrefs are basically pointers to little bits of symbol table, so that's what you need to pass to ReDef::redef.
As an analogy, imagine that when you want to know the date of the Battle of Lewes, your procedure is to go to the library, look in the catalogue for the shelf address of a book on 13th century English battles, go to that shelf, and look up the date... voila 14 May 1264! Now, imagine I want to feed you altered information. Simply defining a new coderef would be like putting a new book on the shelf: it won't trick you because the catalogue is still pointing you at the old book. We need to alter the catalogue too.
UPDATE
You can make this a little prettier using prototypes. Prototypes are not usually recommended, but this seems to be a non-evil use for them...
use strict;
use warnings;
sub ReDef::redef (*) {
my $ref = shift;
no warnings qw(redefine);
*$ref = sub { print "Redefined!\n" };
}
sub orig { print "Original!\n" }
orig;
ReDef::redef *orig; # don't need the backslash any more
orig;

This works for me:
use v5.16;
use strict;
use warnings;
package Redef;
sub redef {
my $ref = shift;
${$ref} = sub { say "Redefined!"; }
}
package main;
my $orig = sub { say "Original!"; };
Redef::redef(\$orig);
$orig->(); # Redefined!
Although it’s just a result of trial and error, I’d be happy to see better answers.
What maybe got you confused is the typeglob operator, *. In Perl you dereference using a sigil (${$scalar_ref}, #{$array_ref}) and the * operator is used for symbol table tricks – which could also be used in your case, see the answer by #tobyink.

Related

Not able to export methods using Exporter module in perl

I'm trying to export the methods written in my custom module using Exporter perl module. Below is my custom module ops.pm
use strict;
use warnings;
use Exporter;
package ops;
our #ISA= qw/Exporter/;
our #EXPORT=qw/add/;
our #EXPORT_OK=qw/mutliply/;
sub new
{
my $class=shift;
my $self={};
bless($self,$class);
return $self;
}
sub add
{
my $self=shift;
my $num1=shift;
my $num2=shift;
return $num1+$num2;
}
sub mutliply
{
my $self=shift;
my $num1=shift;
my $num2=shift;
return $num1*$num2;
}
1;
Below is the script ops_export.pl using ops.pm
#!/usr/bin/perl
use strict;
use warnings;
use ops;
my $num=add(1,2);
print "$num\n";
when i execute the above script i'm getting below error.
Undefined subroutine &main::add called at ops_export.pl line 8.
I'm not getting why my script is checking in &main package even though i have exported the add in ops.pm using #EXPORT
Where am i going wrong?
ops is a pragma already used by Perl. From the docs:
ops - Perl pragma to restrict unsafe operations when compiling
I don't know what that actually means but that's the issue here.
Rename your module to something else, preferably something with uppercase characters as #simbabque suggests in a comment, because lowercase "modules" are somehow reserved for pragmas (think of warnings or strict).
Also: Calling your add function won't work because you mix up OO code and regular functions. Your add expects three parameters and you supply only two (1 and 2).
When writing OO modules you shouldn't export anything (not even new), i.e.:
package Oops;
use strict; use warnings;
use OtherModules;
# don't mention 'Export' at all
sub new {
...
}
sub add {
...
}
1;
And then in your scripts:
use strict; use warnings;
use Oops;
my $calculator = Oops->new();
my $result = $calculator->add(1, 2);
print $result, "\n"; # gives 3

Perl : Like in java we can access public member (variables) in other class is there any same concept in perl

I have 2 perl file and i want to use value of one variable in another perl file as input so how i can do it is there any concept like java we can declare it as public and use it.
any help appreciated thank you!
In this answer, I'll skip the discussion about whether it is the right decision to use OOP or not and just assume you want to do it the OOP-way.
In short, all variables of an object in Perl can be considered public. In fact, the problem is often the opposite - to make some of them private. Anyway, if you have a file Obj.pm which defines an object with a field foo which looks like this:
package Obj;
sub new {
my $class = shift;
my $self = {foo => "bar"};
bless $self, $class;
return $self;
}
you can access the foo variable as if it were public:
use Obj;
my $obj = Obj->new();
print $obj->{foo};
For perhaps a more pleasant OOP in Perl, look at the Moose package which gives you more flexibility.
As #user2864740 pointed you don't need "OO" in perl to share variables.It is one way, Let's say you have two files
Foo.pm(package):
#!/usr/bin/perl
use strict;
use warnings;
use Exporter;
package Foo;
our #ISA = qw(Exporter);
our #EXPORT = qw( blat); #exported by default
our #EXPORT_OK = qw(bar );#not exported by default
our $x="42";#variable to be shared should be "our" not "my"
sub bar {
print "Hello $_[0]\n"
}
sub blat {
print "World $_[0]\n"
}
1;
Access that variable from other file as
bar.pl :
#!/usr/bin/perl
use strict;
use warnings;
use Foo;
print "$Foo::x";#imported variable
blat("hello");#imported subroutine
If you want to import listed functions then:
#!/usr/bin/perl
use strict;
use warnings;
use Foo qw(bar blat);# import listed subs
print "$Foo::x";#imported variable
blat("hello ");#imported subroutine
bar("hi");#this also get imported

Perl prototype subroutine in class

I am trying to call a prototype function from a class without instantiating an object. An example of my class MyClass :
package MyClass;
use strict;
use warnings;
sub import{
my $class = shift;
my ($caller) = caller();
eval "sub ${caller}::myprot(\&);";
eval "*${caller}::myprot = \&MyClass::myprot;";
}
sub myprot (&) {
my ($f) = #_;
$f->();
}
1;
I want to call the prototype from a script main.pl:
use strict;
use warnings;
use MyClass;
myprot {
print "myprot\n";
};
and I am getting the errors:
Use of uninitialized value in subroutine entry at MyClass.pm line 14.
Use of uninitialized value in subroutine entry at MyClass.pm line 14.
Undefined subroutine &main::myprot called at main.pm line 8.
I don't really understand the undefined subroutine error: With use, import is called which defines the prototype for main.pl. I also really don't understand the uninitialised value error.
I'd be happy for some explanation.
You're looking for Exporter.
package MyClass;
use strict;
use warnings;
use Exporter qw( import );
our #EXPORT = qw( myprot );
sub myprot(&) {
my ($f) = #_;
$f->();
}
1;
I usually use #EXPORT_OK (requiring the use of use MyClass qw( myprot );) rather than exporting by default.
There's a bunch of sketchy things going on in that code.
Unchecked use of eval means if it fails, you'll never know. eval should be used as eval "code" or die $#. You'll find it's throwing an error because strict does not like it when you mess with the symbol table (that's what *name = \&code is doing).
Using eval to export subroutines is overkill. eval STRING is a potential security hole and should be used as a last resort (eval BLOCK is fine). You can manipulate the symbol table without eval, but strict will not like the use of symbolic references.
my $caller = "foo";
*{"${caller}::myprot"} = \&MyClass::myprot;
# Can't use string ("foo::myprot") as a symbol ref while "strict refs" in use...
You have to turn off strict first. This is generally known as "aliasing".
no strict 'refs';
*{$caller.'::myprot'} = \&myprot;
Setting the prototype beforehand is unnecessary, the alias will take care of it for you.
It turns out this is all unnecessary, there's a number of modules which do this for you. The most common one is Exporter and comes with Perl. This makes your custom import unnecessary.
use Exporter 'import';
our #EXPORT = qw(myprot);
Other general tips...
Hard coding the name of a class in a class (ie. \&MyClass::myprot should just be \&myprot) should be avoided. It makes it harder to change the class or move the code around.
Hybrid modules which are both classes and export functions, are discouraged. They're harder to use, test and document and produce odd side effects. You should put myprot into its own module.
Are you sure you really want to do this?
The problem is that the double quotes will eat the backslash you have in the glob assignment.
eval "*${caller}::myprot = \&MyClass::myprot;"
should be
eval "*${caller}::myprot = \\&MyClass::myprot;"
But please don't ask me to debug your code!

How can I get the name of the current subroutine in Perl?

In Perl we can get the name of the current package and current line number Using the predefined variables like __PACKAGE__ and __LINE__.
Like this I want to get the name of the current subroutine:
use strict;
use warnings;
print __PACKAGE__;
sub test()
{
print __LINE__;
}
&test();
In the above code I want to get the name of the subroutine inside the function test.
Use the caller() function:
my $sub_name = (caller(0))[3];
This will give you the name of the current subroutine, including its package (e.g. 'main::test'). Closures return names like 'main::__ANON__'and in eval it will be '(eval)'.
caller is the right way to do at #eugene pointed out if you want to do this inside the subroutine.
If you want another piece of your program to be able to identify the package and name information for a coderef, use Sub::Identify.
Incidentally, looking at
sub test()
{
print __LINE__;
}
&test();
there are a few important points to mention: First, don't use prototypes unless you are trying to mimic builtins. Second, don't use & when invoking a subroutine unless you specifically need the effects it provides.
Therefore, that snippet is better written as:
sub test
{
print __LINE__;
}
test();
I was just looking for an answer to this question as well, I found caller as well, but I was not interested in the fully qualified path, simply the literal current package name of the sub, so I used:
my $current_sub = (split(/::/,(caller(0))[3]))[-1];
Seems to work perfectly, just adding it in for if anyone else trips over this questions :)
There special __SUB__ exists from perl-5.16.
use v5.16;
use Sub::Identify qw/sub_fullname/;
sub foo {
print sub_fullname( __SUB__ ); # main::foo
}
foo();
Actually you can pass to sub_fullname any subroutine reference (even anonymous):
use Sub::Identify qw/sub_fullname/;
sub foo {
print sub_fullname( \&foo ); # main::foo
print sub_fullname( sub{} ); # main::__ANON__
}
foo();

How can I take a reference to a Perl subroutine?

I'm having some trouble figuring out how to make a reference to a subroutine in an external module file. Right now, I'm doing this:
External file
package settingsGeneral;
sub printScreen {
print $_[0];
}
Main
use settingsGeneral;
my $printScreen = settingsGeneral::printScreen;
&$printScreen("test");
but this result into an error:
Can't use string ("1") as a subroutine ref while "strict refs" in use
As noted in perlmodlib, you should start your module's name with an uppercase letter:
Perl informally reserves lowercase module names for 'pragma' modules like integer and strict. Other modules normally begin with a capital letter and use mixed case with no underscores (need to be short and portable).
One way to call a sub defined in another package is to fully qualify that sub's name when you call it:
SettingsGeneral::printScreen "important message\n";
If all you want is a reference to printScreen, grab it with the backslash operator
my $subref = \&SettingsGeneral::printScreen;
and call it with one of
&$subref("one\n");
&{$subref}("two\n");
$subref->("three\n");
You could create an alias in your current package:
*printScreen = \&SettingsGeneral::printScreen;
printScreen("another urgent flash\n");
Skip the parentheses (necessary because the sub in the current package wasn't known at compile time) by writing:
use subs 'printScreen';
*printScreen = \&SettingsGeneral::printScreen;
printScreen "the sky is falling!\n";
The Exporter module can do this custodial work for you:
SettingsGeneral.pm:
package SettingsGeneral;
use Exporter 'import';
our #EXPORT = qw/ printScreen /;
sub printScreen {
print $_[0];
}
1;
main:
#! /usr/bin/perl
use warnings;
use strict;
use SettingsGeneral;
printScreen "foo!\n";