Handling constants in Perl?

N

newsbot

First my disclaimer... 5 Years of Perl and I've never really needed to
do what I am asking here (mainly because of how Perl works so well
without it, I guess), but I need to do it now, and I'm a little
surprised that it doesn't work the way I thought it would.

I want to be able to hold, in one place, a file/module of constants
that I can use in any other module in a project. Kind of like header
files (.h) in C:

#define FOO "bar"

Then I include that .h file in all .c source files. So in Perl, I did
this:

=== TestK.pm ===

use constant TEST1 => 'Test1';
use constant TEST2 => 'FooBar';

1;

=== TestMod.pm ===

#!/usr/bin/perl
$|++;

use strict;
use warnings qw( all );

use TestK;

sub Test {

print __PACKAGE__ . ": TEST1 = " . TEST1 . "\n";
print __PACKAGE__ . ": TEST2 = " . TEST2 . "\n";

}

1;

=== test.pl ===

#!/usr/bin/perl
$|++;

use strict;
use warnings qw( all );

use TestK;
use TestMod;

TestMod::Test();

print __PACKAGE__ . ": TEST1 = " . TEST1 . "\n";
print __PACKAGE__ . ": TEST2 = " . TEST2 . "\n";

__END__

I *don't* get what I expect here. Instead, I get:

Bareword "TEST1" not allowed while "strict subs" in use at test.pl line
12.
Bareword "TEST2" not allowed while "strict subs" in use at test.pl line
13.

It's like I can't "use TestK" in both test.pl and TestMod.pm. If I
comment out the "use TestMod;" and the TestMod::Test() call in test.pl,
the print statments in test.pl are fine. If I comment out the print
statements in test.pl and the "use TestK;" in test.pl, then the call to
TestMod::Test() in test.pl is fine. But I can't do both.

Documentation on "use constants" says that constants are package
scoped. But without providing a package name for the constants in
TestK.pm, I would expect the constants to be scoped to each package
they were used in, so I would get:

main::TEST1
main::TEST2
TestMod::TEST1
TestMod::TEST2

Obviously, this is NOT the case. And again, I don't recall ever
needing to do this, so I'm a little amazed. I don't recall (nor find
on Google in c.l.p.m) any real discussion of using constants in
multiple packages. (I think mainly because use of "constants" in Perl
as they are in C isn't in high use.)

Can anyone shed light on the above?

Thanks!
-ceo
 
M

Mark Clements

First my disclaimer... 5 Years of Perl and I've never really needed to
do what I am asking here (mainly because of how Perl works so well
without it, I guess), but I need to do it now, and I'm a little
surprised that it doesn't work the way I thought it would.

I want to be able to hold, in one place, a file/module of constants
that I can use in any other module in a project. Kind of like header
files (.h) in C:

#define FOO "bar"

Then I include that .h file in all .c source files. So in Perl, I did
this:

=== TestK.pm ===

use constant TEST1 => 'Test1';
use constant TEST2 => 'FooBar';

1;
<snip>
There has just been a question on this but:

perldoc Exporter

Obviously, this is NOT the case. And again, I don't recall ever
needing to do this, so I'm a little amazed. I don't recall (nor find
on Google in c.l.p.m) any real discussion of using constants in
multiple packages. (I think mainly because use of "constants" in Perl
as they are in C isn't in high use.)
bob 989 $ grep -lr "use constant" /usr/local/lib/perl5 | wc
121 121 7298
bob 990 $ grep -lr "Exporter" /usr/local/lib/perl5 | wc
340 340 17656

(though constants can be defined by other means)

Both "use constants" and the Exporter are used quite extensively in CPAN
and the core modules..

regards,

Mark
 
N

newsbot

Putting the constants in a package and exporting them seems to work
fine. I just didn't expect this and would still like to know if anyone
can shed light on why it doesn't work the way I originally expected.

Perl Cookbook gives a somewhat brief but informative rundown on
constants and says that constants are scoped to the package in which
they are created. Which to me means it should work the way I
originally expected this to work.

Anyway...

=== TestK.pm ===

package TestK;

use base qw( Exporter );
@EXPORT = qw( TEST1 TEST2 );

use constant TEST1 => 'Test1';
use constant TEST2 => 'FooBar';

1;
__END__

....seems to work. Now test.pl runs as I expected it to run:

$ test.pl
TestMod: TEST1 = Test1
TestMod: TEST2 = Test2
TestMod: TEST3 = Test3
main: TEST1 = Test1
main: TEST2 = Test2
main: TEST3 = Test3

Thanks!
-ceo
 
N

newsbot

I just replied with the same as this was being posted. So this seems
to work. But my original question still stands however. Why doesn't
it work the way the documentation on "use constant" says that it
would/should work? If I use a module with no package name, then the
routines and variables in the module should be scoped to the current
package...

-ceo
 
M

Mark Clements

I just replied with the same as this was being posted. So this seems
to work. But my original question still stands however. Why doesn't
it work the way the documentation on "use constant" says that it
would/should work? If I use a module with no package name, then the
routines and variables in the module should be scoped to the current
package...

OK - I see what you mean now.

bob 548 $ cat TestConstant.pm
use constant TT => 1;
use constant UU => 2;

1;
bob 549 $ cat test1.pl
use strict;
use warnings;

use TestConstant;

print UU."\n";
bob 550 $ perl test1.pl
2


TestConstant has no package defined, so main is assumed, so TT and UU
become part of the main package.


When I run your code (from original post) I get:

Undefined subroutine &TestMod::Test called at test1.pl line 10.

TestMod.pm has been included, but doesn't define the package TestMod, so
therefore the subroutine it defines appears in the main package.

If I correct this (ie call main::Test) the result is:

main: TEST1 = Test1
main: TEST2 = FooBar
main: TEST1 = Test1
main: TEST2 = FooBar

bob 631 $ perl -v

This is perl, v5.8.2 built for sun4-solaris

Which perl are you using?

Mark
 
I

Ivan Nevostruev

I think the reason your program fails is in the nature of use
statement. According to perlfunc use is actually BEGIN { require
Module; import Module LIST; }. This is my explanation of what is
happening without Exporter module:
- when you use your constant module first time (use TestK), it's
really included
- no importing going with step
- when you use your constant module second time (in main program), it
is not included, because perl does not include files second time (see
%INC hash for list of included files)
- no importing going with step too
- so compiler does not see your constants

If you are using Exporter:
- when you use your constant module first time (use TestK), it's
really included
- importing of names
- when you use your constant module second time (in main program), it
is not included
- but importing is going
- and the constants are available

Conclusion: you'd better use Exporter, to get full module

P.S. I actually fail on this very tricky issue, but in more complex
situation :) Sorry for my bad English.
 
N

newsbot

Ok, this is what I get for retyping and not cutting and pasting... A
cardinal rule broken... (How many FAQs does it take?) Sorry.

TestMod.pm should have read:

=== TestK.pm ===

package TestMod;

use strict;
use warnings qw( all );

use TestK;

sub Test {

print __PACKAGE__ . ": TEST1 = " . TEST1 . "\n";
print __PACKAGE__ . ": TEST2 = " . TEST2 . "\n";

}

1;

__END__

(Cut and pasted this time.)

Which is how I ran it. THEN it gave me the error I originally
described.

I am using ActiveState Perl for Windows v5.8.4.

-ceo
 
I

Ivan Nevostruev

Well, i run your script and get the following errors:
Bareword "TEST1" not allowed while "strict subs" in use at TestMod.pm line 7.
Bareword "TEST2" not allowed while "strict subs" in use at TestMod.pm line 8.
Compilation failed in require at test.pl line 8.
BEGIN failed--compilation aborted at test.pl line 8.
They occur because line "use TestK;" is before line "use TestMod;" in
test.pl. So perl includes 'TestK.pm' into test.pl. And when it parses
'TestMod.pm', it does not include 'TestK.pm', because with file in
already included.

And if you just change line order to "use TestMod;use TestK;" in
test.pl, the following errors occur:
Bareword "TEST1" not allowed while "strict subs" in use at test.pl line 12.
Bareword "TEST2" not allowed while "strict subs" in use at test.pl line 13.
Execution of test.pl aborted due to compilation errors.

To prevent those errors, you need to write complete module with
Exporter. Including of 'TestK.pm' file occurs only once (just as it was
before), but 'import' method will be called twice, and your constants
will be imported into all namespaces you need.
 
A

Anno Siegel

Ivan Nevostruev said:
Well, i run your script and get the following errors:
They occur because line "use TestK;" is before line "use TestMod;" in
test.pl. So perl includes 'TestK.pm' into test.pl. And when it parses
'TestMod.pm', it does not include 'TestK.pm', because with file in
already included.

And if you just change line order to "use TestMod;use TestK;" in
test.pl, the following errors occur:

No. If the module TestMod uses TestK itself (and the modules are
otherwise written correctly), "use TestK" after "use TestMod" has
no effect.
To prevent those errors, you need to write complete module with
Exporter. Including of 'TestK.pm' file occurs only once (just as it was
before), but 'import' method will be called twice, and your constants
will be imported into all namespaces you need.

Not really. All it takes is to remove the spurious "use TestK" from
the main program. That is taken care of by TestMod, as it should be.
Then the arrangement works as the OP intended.

Another question is whether it's wise to have a module whose effect
depends on which package gets hold of it first. (The first package gets
the constants, others get nothing.) We have seen right in this thread
that this behavior is confusing. Much better to put the constants in
their own Exporting package and let clients import what they need.

Anno
 

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

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top