How to execute functions from packages using function reference in perl? - perl

I wanted to use function reference for dynamically executing functions from other packages.
I have been trying different solutions for a while for the idea and nothing seemed to work!
So, i thought of asking this question and while attempting to do so, solution worked! but I'm not sure if it's the correct way to do so: it requires manual work and is a bit "hacky". Can it be improved?
A package to support required functionality
package Module;
# $FctHash is intended to be a Look-up table, on-reception
# of command.. execute following functions
$FctHash ={
'FctInitEvaluate' => \&FctInitEvaluate,
'FctInitExecute' => \&FctInitExecute
};
sub FctInitEvaluate()
{
//some code for the evalute function
}
sub FctInitExecute()
{
//some code for the execute function
}
1;
2. Utility Script needs to use the package using function reference
use strict;
use warnings 'all';
no strict 'refs';
require Module;
sub ExecuteCommand()
{
my ($InputCommand,#Arguments) =#_;
my $SupportedCommandRefenece = $Module::FctHash;
#verify if the command is supported before
#execution, check if the key is supported
if(exists($SupportedCommandRefenece->{$InputCommand}) )
{
// execute the function with arguments
$SupportedCommandRefenece->{$InputCommand}(#Arguments);
}
}
# now, evaluate the inputs first and then execute the function
&ExecuteCommand('FctInitEvaluate', 'Some input');
&ExecuteCommand('FctInitExecute', 'Some input');
}
But now, this technique seems to work! Still, is there a way to improve it?

You can use can. Please see perldoc UNIVERSAL for details.
use strict;
use warnings;
require Module;
sub ExecuteCommand {
my ($InputCommand, #Arguments) = #_;
if (my $ref = Module->can($InputCommand)) {
$ref->(#Arguments);
}
# ...
}

You've built a fairly standard implementation for using a hash as a dispatch table. If that's your intention, I don't seen any reason to do more than clean it up a little. can is a good alternative if you're attempting to build something OO-ish, but that's not necessary if all you're after is a command lookup table.
Here's a version that a) is runnable Perl as it stands (your attempt to mark comments with // in the question's version is a syntax error; in Perl, // is the 5.10-and-higher "defined-or" operator, not a comment marker) and b) has more of a perlish accent:
Module.pm
package Module;
use strict;
use warnings;
use 5.010;
our $function_lookup = {
FctInitEvaluate => \&init_evaluate,
FctInitExecute => \&init_execute,
};
sub init_evaluate {
say 'In init_evaluate';
}
sub init_execute {
say 'In init_execute';
}
1;
script.pl
#!/usr/bin/env perl
use strict;
use warnings;
require Module;
execute_command('FctInitEvaluate', 'Some input');
execute_command('FctInitExecute', 'Some input');
sub execute_command {
my ($input_command, #arguments) = #_;
$Module::function_lookup->{$input_command}(#arguments)
if exists($Module::function_lookup->{$input_command});
}

Related

Perl Import Package in different Namespace

is it possible to import (use) a perl module within a different namespace?
Let's say I have a Module A (XS Module with no methods Exported #EXPORT is empty) and I have no way of changing the module.
This Module has a Method A::open
currently I can use that Module in my main program (package main) by calling A::open I would like to have that module inside my package main so that I can directly call open
I tried to manually push every key of %A:: into %main:: however that did not work as expected.
The only way that I know to achieve what I want is by using package A; inside my main program, effectively changing the package of my program from main to A.
Im not satisfied with this. I would really like to keep my program inside package main.
Is there any way to achieve this and still keep my program in package main?
Offtopic: Yes I know usually you would not want to import everything into your namespace but this module is used by us extensively and we don't want to type A:: (well the actual module name is way longer which isn't making the situation better)in front of hundreds or thousands of calls
This is one of those "impossible" situations, where the clear solution -- to rework that module -- is off limits.
But, you can alias that package's subs names, from its symbol table, to the same names in main. Worse than being rude, this comes with a glitch: it catches all names that that package itself imported in any way. However, since this package is a fixed quantity it stands to reason that you can establish that list (and even hard-code it). It is just this one time, right?
main
use warnings;
use strict;
use feature 'say';
use OffLimits;
GET_SUBS: {
# The list of names to be excluded
my $re_exclude = qr/^(?:BEGIN|import)$/; # ...
my #subs = grep { !/$re_exclude/ } sort keys %OffLimits::;
no strict 'refs';
for my $sub_name (#subs) {
*{ $sub_name } = \&{ 'OffLimits::' . $sub_name };
}
};
my $name = name('name() called from ' . __PACKAGE__);
my $id = id('id() called from ' . __PACKAGE__);
say "name() returned: $name";
say "id() returned: $id";
with OffLimits.pm
package OffLimits;
use warnings;
use strict;
sub name { return "In " . __PACKAGE__ . ": #_" }
sub id { return "In " . __PACKAGE__ . ": #_" }
1;
It prints
name() returned: In OffLimits: name() called from main
id() returned: In OffLimits: id() called from main
You may need that code in a BEGIN block, depending on other details.
Another option is of course to hard-code the subs to be "exported" (in #subs). Given that the module seems to be immutable in practice this option is reasonable and more reliable.
This can also be wrapped in a module, so that you have the normal, selective, importing.
WrapOffLimits.pm
package WrapOffLimits;
use warnings;
use strict;
use OffLimits;
use Exporter qw(import);
our #sub_names;
our #EXPORT_OK = #sub_names;
our %EXPORT_TAGS = (all => \#sub_names);
BEGIN {
# Or supply a hard-coded list of all module's subs in #sub_names
my $re_exclude = qr/^(?:BEGIN|import)$/; # ...
#sub_names = grep { !/$re_exclude/ } sort keys %OffLimits::;
no strict 'refs';
for my $sub_name (#sub_names) {
*{ $sub_name } = \&{ 'OffLimits::' . $sub_name };
}
};
1;
and now in the caller you can import either only some subs
use WrapOffLimits qw(name);
or all
use WrapOffLimits qw(:all);
with otherwise the same main as above for a test.
The module name is hard-coded, which should be OK as this is meant only for that module.
The following is added mostly for completeness.
One can pass the module name to the wrapper by writing one's own import sub, which is what gets used then. The import list can be passed as well, at the expense of an awkward interface of the use statement.
It goes along the lines of
package WrapModule;
use warnings;
use strict;
use OffLimits;
use Exporter qw(); # will need our own import
our ($mod_name, #sub_names);
our #EXPORT_OK = #sub_names;
our %EXPORT_TAGS = (all => \#sub_names);
sub import {
my $mod_name = splice #_, 1, 1; # remove mod name from #_ for goto
my $re_exclude = qr/^(?:BEGIN|import)$/; # etc
no strict 'refs';
#sub_names = grep { !/$re_exclude/ } sort keys %{ $mod_name . '::'};
for my $sub_name (#sub_names) {
*{ $sub_name } = \&{ $mod_name . '::' . $sub_name };
}
push #EXPORT_OK, #sub_names;
goto &Exporter::import;
}
1;
what can be used as
use WrapModule qw(OffLimits name id); # or (OffLimits :all)
or, with the list broken-up so to remind the user of the unusual interface
use WrapModule 'OffLimits', qw(name id);
When used with the main above this prints the same output.
The use statement ends up using the import sub defined in the module, which exports symbols by writing to the caller's symbol table. (If no import sub is written then the Exporter's import method is nicely used, which is how this is normally done.)
This way we are able to unpack the arguments and have the module name supplied at use invocation. With the import list supplied as well now we have to push manually to #EXPORT_OK since this can't be in the BEGIN phase. In the end the sub is replaced by Exporter::import via the (good form of) goto, to complete the job.
You can forcibly "import" a function into main using glob assignment to alias the subroutine (and you want to do it in BEGIN so it happens at compile time, before calls to that subroutine are parsed later in the file):
use strict;
use warnings;
use Other::Module;
BEGIN { *open = \&Other::Module::open }
However, another problem you might have here is that open is a builtin function, which may cause some problems. You can add use subs 'open'; to indicate that you want to override the built-in function in this case, since you aren't using an actual import function to do so.
Here is what I now came up with. Yes this is hacky and yes I also feel like I opened pandoras box with this. However at least a small dummy program ran perfectly fine.
I renamed the module in my code again. In my original post I used the example A::open actually this module does not contain any method/variable reserved by the perl core. This is why I blindly import everything here.
BEGIN {
# using the caller to determine the parent. Usually this is main but maybe we want it somewhere else in some cases
my ($parent_package) = caller;
package A;
foreach (keys(%A::)) {
if (defined $$_) {
eval '*'.$parent_package.'::'.$_.' = \$A::'.$_;
}
elsif (%$_) {
eval '*'.$parent_package.'::'.$_.' = \%A::'.$_;
}
elsif (#$_) {
eval '*'.$parent_package.'::'.$_.' = \#A::'.$_;
}
else {
eval '*'.$parent_package.'::'.$_.' = \&A::'.$_;
}
}
}

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

How to dynamically avoid 'use module' to reduce memory footprint

Given the following module:
package My::Object;
use strict;
use warnings;
use My::Module::A;
use My::Module::B;
use My::Module::C;
use My::Module::D;
...
1;
I would like to be able to call My::Object in the next 2 scenarios:
Normal use
use My::Object;
My::Module->new();
Reduced memory use. Call the same object but with a condition or a flag telling the object to skip the use modules to reduce memory usage. Somehow like:
use My::Object -noUse;
My::Module->new();
If tried the Perl if condition without success.
The problem I'm having it's with big objects with a lot of uses, then only loading this object consumes a lot of RAM. I know I can refactor them but it will be wonderful if somehow I can avoid these uses when I'm sure none of them is used on the given scenario.
One solution will be to replace all uses with requires on all places when the modules are needed, but I don't see convenient when some of them are used in a lot of methods.
Any ideas?
Thanks
The native pragma autouse will load modules needed when plain subroutines are called:
use autouse 'My::Module::A' => qw(a_sub);
# ... later ...
a_sub "this will dynamically load My::Module::A";
For proper OO methods, Class::Autouse will load modules (classes) when methods are called:
use Class::Autouse;
Class::Autouse->autouse( 'My::Module::A' );
# ... later ...
print My::Module::A->a_method('this will dynamically load My::Module::A');
What I think you're looking for is perhaps require - require is evaluated later so you can use it successfully in a conditional:
if ( $somecondition ) {
require Some::Module;
}
Of course, you won't be able to do Some::Module->new() if you've not loaded it - there's just no way around that.
Where use is triggered at compile time (and thus will trigger warnings under perl -c if the module is unavailable) require happens are runtime. You should probably test if require was successful as a result.
e.g.:
if ( $somecondition ) {
eval { require Some::Module };
warn "Module Not loaded: ".$# if $#;
}
Otherwise you may be looking for:
Is it possible to pass parameters to a Perl module loading?
#!/usr/bin/perl
package MyObject;
sub import {
my ( $package, $msg ) = #_;
if ( defined $msg and $msg eq "NO_USE" ) {
#don't load module
}
else {
require XML::Twig;
}
}
1;
And then call:
use if $somecondition, MyObject => ( 'NO_USE' );
Or just simpler:
use MyObject qw( NO_USE );
Edit:
After a bit of fiddling with 'use' - there's a couple of gotchas, in that use if doesn't seem to like lexical variables. So you need to do something like:
#!/usr/bin/perl
package MyObject;
use strict;
use warnings;
our $import_stuff = 1;
sub import {
my ( $package, $msg ) = #_;
if ( $msg and $msg eq "NO_USE" ) {
$import_stuff = 0;
}
use if $import_stuff, 'Text::CSV';
}
1;
And call:
#!/usr/bin/perl
use strict;
use warnings;
use MyObject qw( NO_USE );
use Data::Dumper;
print Dumper \%INC;
my $test = Text::CSV -> new();
(Which errors if you set NO_USE and doesn't otherwise).
I think that's an artifact of use being a compile time directive still, so requires a (package scoped) condition.

Detect compile phase in Perl

I'm working with a module that makes use of some prototypes to allow code blocks. For example:
sub SomeSub (&) { ... }
Since prototypes only work when parsed at compile time, I'd like to throw a warning or even a fatal if the module is not parsed at compile time. For example:
require MyModule; # Prototypes in MyModule won't be parsed correctly
Is there a way to detect if something is being executed at compile or run time/phase in Perl?
If you're running on Perl 5.14 or higher, you can use the special ${^GLOBAL_PHASE} variable which contains the current compiler state. Here's an example.
use strict;
use warnings;
sub foo {
if ( ${^GLOBAL_PHASE} eq 'START' ) {
print "all's good\n";
} else {
print "not in compile-time!\n";
}
}
BEGIN {
foo();
};
foo();
Output:
all's good
not in compile-time!
Before 5.14 (or on or after, too), you can do:
package Foo;
BEGIN {
use warnings 'FATAL' => 'all';
eval 'INIT{} 1' or die "Module must be loaded during global compilation\n";
}
but that (and ${^GLOBAL_PHASE}) doesn't quite check what you want to know, which is whether the code containing the use/require statement was being compiled or run.

Pass a subroutine to module and redefine it?

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.