Need help with constants and package names.

T

Terry

Hello all, I'm having trouble with packages and constants. What I'd
like to have is a single .pm for all constants used by the application.
The application will have other .pm files as well, and I'd like for them
to have access to the constants as well. I thought the following would
do what I want, but fails miserably:

#### file: ./main
#!/usr/bin/perl

use warnings;
use strict;

use One::Two::Constants;
use One::Two::Obj;

print "Constant PI is: " , PI , "\n";
my $obj = Obj->new();
print "Object member PI is: " , $obj->{'pi'} , "\n";




#### file ./One/Two/Constants.pm
package One::Two::Constants;

use constant { PI => 3.1415 };

1;



#### file ./One/Two/Obj.pm
package One::Two::Obj;

use warnings;
use strict;

use One::Two::Constants;

sub new {
my $class = shift;
my $self = {
some => 'thing' ,
more => 'stuff' ,
pi => PI
};
return bless $self , $class;
}

1;


$ ./main
Bareword "PI" not allowed while "strict subs" in use at One/Two/Obj.pm
line 10.
Compilation failed in require at ./main line 7.
BEGIN failed--compilation aborted at ./main line 7.



The only way I've been able to get this to run is by removing the
package declaration from Constants.pm, changing the package declaration
of Obj.pm to just 'package Obj', and by referring to 'PI' inside of
Obj.pm as 'main::pI'. Clearly this is a poor approach.

All I want is all constants kept in one place, able to be referred to
from anywhere else. Any suggestions?

As a side note, aren't package names supposed to be fully qualified?
i.e, aren't .pm file supposed to begin with something like:

package One::Two::Something;

Importing with 'use' only seems to work when packages are declared with
only their basename:

package Something;

Thank you for your help.

-Terry
 
J

John Bokma

Terry said:
All I want is all constants kept in one place, able to be referred to
from anywhere else. Any suggestions?

Just don't do it that way, try to use namespaces (packages), e.g

Math::pI

etc.
 
S

Sherm Pendley

Terry said:
Hello all, I'm having trouble with packages and constants. What I'd
like to have is a single .pm for all constants used by the application.
The application will have other .pm files as well, and I'd like for them
to have access to the constants as well. I thought the following would
do what I want, but fails miserably:

.... snip ...
#### file ./One/Two/Constants.pm
package One::Two::Constants;

use constant { PI => 3.1415 };

1;

Long answer, read:

perldoc Exporter
perldoc perlmod
perldoc perlmodlib

Short answer, to export symbols into the caller's package, inherit from
Exporter and declare them in @EXPORT:

package One::Two::Constants;

use strict;
use warnings;

use Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw(PI);

use constant PI => 3.1415;

1;

But *do* read the long answer. The code above is enough to get you
started, but there's definitely a lot more to learn than this simple
example shows.
As a side note, aren't package names supposed to be fully qualified?
i.e, aren't .pm file supposed to begin with something like:

package One::Two::Something;

That is correct.
Importing with 'use' only seems to work when packages are declared with
only their basename:

package Something;

No, use works just fine with nested module names. If it's not working
for you, post a short (but complete) script that demonstrates the problem.

sherm--
 
A

A. Sinan Unur

Hello all, I'm having trouble with packages and constants.

In addition to Sherm and John's excellent responses, let me just point
out that you can save yourself a lot of trouble by using h2xs. While its
official description states:

DESCRIPTION
*h2xs* builds a Perl extension from C header files. The extension
will include functions which can be used to retrieve the value of
any #define statement which was in the C header files.

it can also be used to generate nice skeleton files for your modules.

h2xs -AXn One::Two::Constants

See

perldoc h2xs

for more information.

Sinan
 
T

Terry

Long answer, read:

perldoc Exporter
perldoc perlmod
perldoc perlmodlib

Thanks for the lead! After reading the Exporter perldoc I see my
blunder.
Short answer, to export symbols into the caller's package, inherit from
Exporter and declare them in @EXPORT:

package One::Two::Constants;

use strict;
use warnings;

use Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw(PI);

use constant PI => 3.1415;

1;


Something I didn't find in the perldoc was how to export a hash using
the 'use constant' pragma. The examples seemed to be geared toward
single imports as in your example. The reason I'm using 'use export'
with a hash ref is to import multiple constants (I'll have at least
fifty by the time I'm done). ie:

use constant {
one => 1 ,
two => 2 ,
# ...
};

How then to pass this list to @EXPORT ? I tried assigning the hash ref
to a scalar first and assigning its keys into @ISA , but you probably
already know that didn't work ;) Any suggestions?


That is correct.


No, use works just fine with nested module names. If it's not working
for you, post a short (but complete) script that demonstrates the problem.

The Obj.pm file in my last post demonstrates the problem:


##### file: ./One/Two/Obj.pm
package One::Two::Obj;

use warnings;
use strict;

use One::Two::Constants;

sub new {
my $class = shift;
my $self = {
some => 'thing' ,
more => 'stuff' ,
pi => PI
};
return bless $self , $class;
}

1;

###### file ./main
#!/usr/bin/perl

use warnings;
use strict;

use One::Two::Constants;
use One::Two::Obj;

my $obj = Obj->new();



$ ./main
Can't locate object method "new" via package "Obj" (perhaps you forgot
to load "Obj"?) at ./main line 10.

If I change the package declaration in Obj.pm from 'One::Two::Obj' to
just 'Obj' it works fine. I also tried exporting 'new' from Obj.pm as
in your example above to no avail.

Thanks again for your help Sherm!
 
C

Christopher Nehren

In addition to Sherm and John's excellent responses, let me just point
out that you can save yourself a lot of trouble by using h2xs. While its
official description states: [...]
See

perldoc h2xs

In further addition, I'd like to suggest Module::Starter (and its
command-line frontend, module-starter) as a modern alternative /
complement to h2xs. It presents a friendlier interface to module
skeleton creation, and isn't historically (read: confusingly, for the
new user anyway) named.

You can find M::S on your local CPAN mirror.

Best Regards,
Christopher Nehren
 
A

A. Sinan Unur

In further addition, I'd like to suggest Module::Starter (and its
command-line frontend, module-starter) as a modern alternative /
complement to h2xs.

I did not know about this module. Thanks very much.

Sinan.
 
S

Sherm Pendley

Terry said:
Something I didn't find in the perldoc was how to export a hash using
the 'use constant' pragma. The examples seemed to be geared toward
single imports as in your example. The reason I'm using 'use export'
with a hash ref is to import multiple constants (I'll have at least
fifty by the time I'm done). ie:

use constant {
one => 1 ,
two => 2 ,
# ...
};

How then to pass this list to @EXPORT ? I tried assigning the hash ref
to a scalar first and assigning its keys into @ISA , but you probably
already know that didn't work ;) Any suggestions?

I haven't found a way to do that either - and believe me I've tried. In
my CamelBones project I have one file with over a hundred constants -
each one appears twice, once to define its value and once in @EXPORT.
The Obj.pm file in my last post demonstrates the problem:

##### file: ./One/Two/Obj.pm
package One::Two::Obj;

use warnings;
use strict;

use One::Two::Constants;

sub new {

.... snip ...
###### file ./main
#!/usr/bin/perl

use warnings;
use strict;

use One::Two::Constants;
use One::Two::Obj;

my $obj = Obj->new();


$ ./main
Can't locate object method "new" via package "Obj" (perhaps you forgot
to load "Obj"?) at ./main line 10.

To call a class method like new(), you need to specify the full class
name, not just the last component of it:

my $obj = One::Two::Obj->new();

Objects know what package they belong to though, so instance methods can
be called with the name of the method alone:

$obj->do_something();
If I change the package declaration in Obj.pm from 'One::Two::Obj' to
just 'Obj' it works fine.

In that case, you're not using Exporter in One::Two::Obj. If you were,
it wouldn't work as expected because the file name doesn't match the
package name.

use One::Two::Obj;

is roughly equivalent to

BEGIN {
require "One/Two/Obj.pm";
One::Two::Obj::import();
}

But if the code in One/Two/Obj.pm is actually in package Obj, there
would be no One::Two::Obj::import() to call. So the symbols in the
module wouldn't be correctly imported into the calling package.
I also tried exporting 'new' from Obj.pm as

You only need to export non-oo functions. Class methods are normally
called using the fully-qualified class name as above. Instance methods
are called via an object reference, and the class of the object is used
to determine the package to find the method in.

sherm--
 
T

Terry

I haven't found a way to do that either - and believe me I've tried. In
my CamelBones project I have one file with over a hundred constants -
each one appears twice, once to define its value and once in @EXPORT.

Thanks anyway. I looked at your CamelBones project too - very
interesting!
To call a class method like new(), you need to specify the full class
name, not just the last component of it:

my $obj = One::Two::Obj->new();

Ah, got it. I was expecting it to be Java-ish, where after the package
is imported, it can be instantiated via its class name only.

Thanks again for the help Sherm.

-Terry
 
T

Terry

I did not know about this module. Thanks very much.

Sinan.

Thanks for the help on this thread! I'm working with a refactor of some
existing code, but I'll remember this the next time I need to create a
module.

-Terry
 
B

Brian McCauley

Surely you mean @EXPORT?

It works just fine if you pay proper attension to run-time v.
compile-time issues.
I haven't found a way to do that either - and believe me I've tried.

BEGIN {
my %constants = (
one => 1 ,
two => 2 ,
# ...
);
require constants;
import constants %constants;
push @EXPORT => keys %constants;
}
 
E

Eric Schwartz

Terry said:
Hello all, I'm having trouble with packages and constants. What I'd
like to have is a single .pm for all constants used by the application.

Others have commented on how, and why (and why not), so I'll just add
my two cents on the wisdom of doing so:

On clpm, we tell everybody to 'use warnings;' and 'use strict;'. Why?
Because, among other things, they force you to declare your variables,
so you can catch when you typo writing one and also so you can trace
the life of your variable more easily. When you import a variable
(or a constant), you're effectively circumventing at least one of
those benefits.

Admittedly, it's worse with variables, where you're potentially
subject to action-at-a-distance when some random code out there
somewhere modifies a variable you didn't know it was modifying, but
even with constants, unless you explicitly import them all, it's very
easy to forget where it's defined and why, if you're in the habit of
exporting such things by default. (And for my next trick, a run-on
sentence the length of War and Peace!)

Just a thought.

-=Eric
 
B

Ben Morrow

It works just fine if you pay proper attension to run-time v.
compile-time issues.


BEGIN {
my %constants = (
one => 1 ,
two => 2 ,
# ...
);
require constants;
import constants %constants;
push @EXPORT => keys %constants;
}

That BEGIN is unnecessary if there is no other code in the file: the
require is happening at BEGIN time anyway, and before import is called.

If there *is* other code in the file, it won't have access to the
constants.

Ben
 
B

Brian McCauley

Ben said:
That BEGIN is unnecessary if there is no other code in the file: the
require is happening at BEGIN time anyway, and before import is called.

If there *is* other code in the file, it won't have access to the
constants.

How do you figure that?

There were several mistakes in my code but that wasn't one of them. The
two lines...

require constants;
import constants %constants;

....should have read...

require constant;
import constant $_ => $constants{$_} for keys %constants;

....and then the code beyond the BEGIN block _can_ access the the constants.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top