Package fails because it refers to variable in main program - how to fix?

H

Henry Law

I have a main program and two packages. The first package loads up a
number of system-wide variables and exposes them to the program that
uses them. The second package has utility subroutines, some of which
need access to variables exposed by the first package. For example,
the log file name is a system-wide variable which is exposed as
"global" and in the "Utilities" package there's a routine that writes
log records.

I can't work out how to get the Utilities package to see the "global"
variables, despite reading perlmod fairly carefully; some pointers to
relevant bits of doc would be helpful.

This works, and shows that global variables are exposed:
----------------------------------------------------
#! C:\Perl\bin\Perl.exe

use strict;
use warnings;

use NFB::ClientGlobal(':all');
#use NFB::Utilities('logrec');

print "Log file name is $g_logfile\n";
----------------------------------------------------

F:\>tryit.pl
Log file name is F:\NFB\nfblog.txt

F:\>

I'm not going to paste in the whole of NFB::Utilities, but the key
lines are these:

sub logrec {
open (LOGFILE,">>main::$g_logfile") or die "etc..";

If I "use" it from the program above the error message is

Global symbol "$g_logfile" requires explicit package name at
NFB/Utilities.pm line 48.

In other words "logrec" is not seeing the definition of $g_logfile; as
you can see I have been experimenting with explicit package names -
including naming the "NFB::ClientGlobal" package that exposed the
$g_logfile variable in the first place, but all without success.
Coaching, please!
 
P

Paul Lalli

Henry said:
I have a main program and two packages. The first package loads up a
number of system-wide variables and exposes them to the program that
uses them. The second package has utility subroutines, some of which
need access to variables exposed by the first package. For example,
the log file name is a system-wide variable which is exposed as
"global" and in the "Utilities" package there's a routine that writes
log records.

This seems like a bad idea. If you have the ability to import from the
first package (as the main:: package does), why can't your second
package also import from it? Why does your second package need to
depend upon main:: having already imported from the first package?
I can't work out how to get the Utilities package to see the "global"
variables, despite reading perlmod fairly carefully; some pointers to
relevant bits of doc would be helpful.

This works, and shows that global variables are exposed:
----------------------------------------------------
#! C:\Perl\bin\Perl.exe

use strict;
use warnings;

use NFB::ClientGlobal(':all');
#use NFB::Utilities('logrec');

print "Log file name is $g_logfile\n";

If I understand what you're saying, NFB::ClientGlobal declares a
package variable $g_logfile, and allows it to be exported. Your main
package then calls NFB::ClientGlobal->import(':all') (by way of the
'use' statement). This has the effect of allowing
$NFB::ClientGlobal::g_logfile to be used in package main:: without
fully declaring it.

Does that all sound correct?

If so, then it would seem to be the obvious solution for the second
package, NFB::Utilities, to also include a call to
use NFB::ClientGlobal qw/:all/;
so that $NFB::ClientGlobal::g_logfile can also be used by package
NFB::ClientGlobal.
I'm not going to paste in the whole of NFB::Utilities, but the key
lines are these:

sub logrec {
open (LOGFILE,">>main::$g_logfile") or die "etc..";

If I "use" it from the program above the error message is

Global symbol "$g_logfile" requires explicit package name at
NFB/Utilities.pm line 48.

You haven't fully declared the global variable correctly. The sigil
($) goes before the package declaration, not the identifier name:

sub logrec {
open my $logfile, '>>', $main::g_logfile or die "etc...";
#...
}
In other words "logrec" is not seeing the definition of $g_logfile; as
you can see I have been experimenting with explicit package names -
including naming the "NFB::ClientGlobal" package that exposed the
$g_logfile variable in the first place, but all without success.
Coaching, please!

Again, this method will "work", but is very buggy. A utility package
should never have to depend upon its caller having imported from
another package. If Package2 needs a variable exported by Package1,
Package2 should import Package1.

Hope this helps,
Paul Lalli
 
A

Anno Siegel

Henry Law said:
I have a main program and two packages. The first package loads up a
number of system-wide variables and exposes them to the program that
uses them. The second package has utility subroutines, some of which
need access to variables exposed by the first package. For example,
the log file name is a system-wide variable which is exposed as
"global" and in the "Utilities" package there's a routine that writes
log records.

See how global variables mean trouble? Could you make the logfile a
parameter of the logrec() utility? It would have saved you this kind
of headache.

If it must stay global, Utilities should import it from ClientGlobal
on its own instead of relying on main:: to do it and making a cross-
package access. main:: can import it as well, if it needs to know.
That's the purpose of a configuration module like ClientGlobal that
whichever part of the program can consistently import what it needs
from it.

However, while roundabout, your method would work as well. You simply
haven't got the cross-package variable syntax right. See code correction
below.
I can't work out how to get the Utilities package to see the "global"
variables, despite reading perlmod fairly carefully; some pointers to
relevant bits of doc would be helpful.

This works, and shows that global variables are exposed:
----------------------------------------------------
#! C:\Perl\bin\Perl.exe

use strict;
use warnings;

use NFB::ClientGlobal(':all');
#use NFB::Utilities('logrec');

print "Log file name is $g_logfile\n";
----------------------------------------------------

F:\>tryit.pl
Log file name is F:\NFB\nfblog.txt

F:\>

I'm not going to paste in the whole of NFB::Utilities, but the key
lines are these:

sub logrec {
open (LOGFILE,">>main::$g_logfile") or die "etc..";

It's $main::g_logfile, not main::$g_logfile. That's all that is wrong.

[snip rest of code]

Anno
 
H

Henry Law

If it must stay global, Utilities should import it from ClientGlobal
on its own instead of relying on main:: to do it and making a cross-
package access. main:: can import it as well, if it needs to know.
That's the purpose of a configuration module like ClientGlobal that
whichever part of the program can consistently import what it needs
from it.

Thanks, Paul and Anno. There is a reason why I'm trying to do what
I'm doing but the faint whiff of not-right-ness has now become a foul
reek so I fear I can't avoid some re-structuring.

But before I crawl back to my editor, advise me once more: the
routines I'm writing make quite intensive use of a small set of
system-wide variables - the host name of the server, for example, the
name of the database to connect to, the name of the log file, etc etc.
I'm as against global variables as the next guy (learnt that in APL
programming way back last century) but what is the most convenient way
of making them accessible when they're needed?
 
T

Tad McClellan

Henry Law said:
On 24 Jun 2005 17:44:47 GMT, (e-mail address removed)-berlin.de (Anno
Siegel) wrote:


[ snip using a bunch of global "configuration" variables ]

Thanks, Paul and Anno. There is a reason why I'm trying to do what
I'm doing but the faint whiff of not-right-ness has now become a foul
reek so I fear I can't avoid some re-structuring.

But before I crawl back to my editor, advise me once more: the
routines I'm writing make quite intensive use of a small set of
system-wide variables - the host name of the server, for example, the
name of the database to connect to, the name of the log file, etc etc.
I'm as against global variables as the next guy (learnt that in APL
programming way back last century) but what is the most convenient way
of making them accessible when they're needed?


Package (global) variables live in the Symbol Table, which is just
a hash with some special access methods.

Simply use your own hash instead of the symbol table hash:

$cf{server} = '127.0.0.1'; # instead of $server
$cf{db_name} = 'myDatabase; # instead of $db_name
...

ie. have 1 global hash with 27 keys rather than 27 separate globals.
 

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,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top