It is possible in perl to call method using package name in variable like this:
my $cls = 'PackageName';
$cls->method()
which calls PackageName->method()
What is the correct syntax to use package variable, e. g. $PackageName::SOME_VAR in the same manner, having package name in $cls variable?
You need to use the following syntax.
use strict;
use warnings;
use feature 'say';
package Foo;
our $bar = 123;
package main;
{
my $package = 'Foo';
no strict 'refs';
say ${"${package}::bar"};
}
The outer ${ ... } is for the reference. The syntax says take the value in the symbol table with the given name as a scalar. The inner ${package} is just the lexical variable $package, with the curly braces {} as a name delimiter.
It's important to turn off strict references for it to work. Remember to do that in the smallest scope possible.
Related
I have some code I decided to pull into a module in perl. I admit that I am doing a bit of monkey-see monkey-do stuff following documentation I've found online.
There is only one pubicly visible subroutine, which sets 2 variables * would like to use in other subroutines in the module, without passing them explicitly as parameters -- $fname, and #lines. Looking online I came up with the "our" keyword, but when I try to declare them at global level (see code snippet below), I get the following error:
mig_tools.pm did not return a true value at
I have worked around the issue by declaring "our $fname" and "our #lines" in every subroutine they are used, but I would prefer to declare them once at global scope. Is that possible?
Here's what I take to be the relevant part of the code.
package mig_tools;
require Exporter;
use strict;
use warnings;
our #ISA = qw(Exporter);
our #EXPORT = qw( ensure_included);
our $fname;
our #lines;
// definitions of already_has_include find_include_insert_point and ensure_included.
All-lowercase variables are reserved for local identifiers and pragma names. Your module should be in MigTools.pm and you should use it with use MigTools
The did not return a true value error is just because your module file didn't return a true value when it was executed. It is usual to add a line containing just 1; to the end of all modules
Your MigTools.pm should look like this. It is best to use the import directly from Exporter instead of subclassing it, and our doesn't help you to create a global variable, but I am a little worried about why you are structuring your module this way
package MigTools;
use strict;
use warnings;
use Exporter qw/ import /;
our #EXPORT = qw/ ensure_included /;
my ($fname, #lines)
sub ensure_included {
}
sub already_has_include {
}
sub find_include_insert_point {
}
sub ensure_included {
}
1;
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!
I'm new to Perl and I'm learning OOP in Perl right now.
Is there a way without any additional libraries (it is forbidden to use any additional lib) to access variable from one package in another one?
package Class;
my $CONSTANT = 'foo'; # this doesn't work, neither our $CONSTANT ..
# ...
# class methodes
# ...
package main;
print Class::$CONSTANT ."\n";
Your constant declaration is wrong
Constants do not have a $ before their name because they are not variables -- a variable (as implied by the name) contains a value which can vary.
Try this (it uses the constant module but that's included in the default installation:
use constant CONSTANT => "Foo";
Accessing class constants
You can then access them as:
Class::CONSTANT # I suggest NOT using this as 'Class::Constant' is a module name, rename your class to something useful
Or, if you have $obj as an instance of Class:
$obj->CONSTANT;
Sample code showing both access methods
use warnings;
use strict;
package MyClass;
use constant SOME_CONSTANT => 'Foo';
sub new
{
my $type = shift; # The package/type name
my $self = {}; # Empty hash
return bless $self, $type;
}
package main;
print MyClass::SOME_CONSTANT . "\n"; # Prints 'Foo\n'
my $obj = MyClass->new();
print $obj->SOME_CONSTANT; # Prints 'Foo'
And a demo.
Easy mistake to make. Put the sigil in front of the class name, e.g.
print ${Class::CONSTANT} . "\n";
print $Class::CONSTANT . "\n";
In addition constants can be defined using the constant package, e.g.
use constant MY_CONSTANT => 5;
print MY_CONSTANT();
You have to define variable using our.
package Class;
our $CONSTANT = 'foo';
# ...
# class methodes
# ...
package main;
print $Class::CONSTANT ."\n";
Keyword package works as syntactic block, so variable defined using my is not accessible outside this syntax block. You also have to place sigil $ in right place. And of course it is variable, not constant.
I know it is possible to use a variable as a variable name for package variables in Perl. I would like to use the contents of a variable as a module name. For instance:
package Foo;
our #names =("blah1", "blah2");
1;
And in another file I want to be able be able to set the contents of a scalar to "foo" and then access the names array in Foo through that scalar.
my $packageName = "Foo";
Essentially I want to do something along the lines of:
#{$packageName}::names; #This obviously doesn't work.
I know I can use
my $names = eval '$'. $packageName . "::names"
But only if Foo::names is a scalar. Is there another way to do this without the eval statement?
To get at package variables in package $package, you can use symbolic references:
no strict 'refs';
my $package = 'Foo';
# grab #Foo::names
my #names = #{ $package . '::names' }
A better way, which avoids symbolic references, is to expose a method within Foo that will return the array. This works because the method invocation operator (->) can take a string as the invocant.
package Foo;
our #names = ( ... );
sub get_names {
return #names;
}
package main;
use strict;
my $package = 'Foo';
my #names = $package->get_names;
Strict checking is preventing you from using a variable (or literal string) as part of a name, but this can be disabled locally:
my #values;
{
no strict 'refs';
#values = #{"Mypackage"}::var;
}
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";