eff_i_g has asked for the wisdom of the Perl Monks concerning the following question:

Fellow Monk(?:ey)?

Here is a diagram to start things:
----  ----
|01|  |02|
|  |  |  |
----  ----
  \    /
   ----
   |03|
   |  |
   ----
01 = custom module 1
02 = custom module 2
03 = file

-file is use-ing both of these modules.

-module 1 contains functions to open log files and return handles like so:

module 1:
our $LOG; # the functions

file:
open_log_and_error($file_name); # the above calls this function, imported from module 1, # which opens the file $file_name and ties it to the # handle $LOG write_log('stuff'); # the above calls this function, imported from module 1, # which does some processing and writes to $LOG

I would like objects in file that have been derived from module 2 to be able to use write_log, but they cannot because it is not use-ing module 1. Morever, if I did use module 1 in module 2, $LOG is already open and used by module 1. Is there any way module 2 can latch onto that and run this function?

I feel so use-d.

How do you folks handle something like this, when many files and modules and whatnottery all need to share file handles?

Thank you :)

Replies are listed 'Best First'.
Re: Modules sharing?
by Zaxo (Archbishop) on Nov 04, 2005 at 02:09 UTC

    One slick way to handle that is by localizing STDERR. If $LOG is a reference to a file handle to log to,

    use Module1 qw/$LOG/; use Module2; my $result = do { local *STDERR = $LOG; Module2::foo(42); };
    That's obviously pretty awkward to use small scopes, but you can wrap larger ones - even the whole file - if you like.

    With that, Module2 doesn't have to anticipate anything about logging. It only needs to make its complaints to STDERR and it is up to the user to redirect to the log handle.

    After Compline,
    Zaxo

Re: Modules sharing?
by asokoloski (Sexton) on Nov 04, 2005 at 02:08 UTC
    You should be able to just do something like
    unless (defined $LOG) { open $LOG, "file"; ... }
    in the open_log_and_error() function. That way, module 2 can use module 1 and be sure that the log file won't be opened twice when you call open_log_and_error in both places. The package variable $LOG will be the same for both the module and the script.

    The only problem I can see is if you have two instances of this script running at once, in which case it might be better to open and close the log file each time you want to write to it.
Re: Modules sharing?
by tilly (Archbishop) on Nov 04, 2005 at 03:20 UTC
    Add a method (and/or constructor argument) to module 2 to set how you want logging done. Tell the objects from module 2 that you want to use module 1's logging facility to do so.
Re: Modules sharing?
by eff_i_g (Curate) on Nov 07, 2005 at 23:41 UTC
    Here is a test that I set up that seems to be working, but I do not fully understand why. I removed the logging facility from module 1 and placed it into its own utility module. I'm confused in TestUtil.pm--why isn't redeclaring or reopening $LOG an issue? I assume the calls are entirely separate from each other? Even still, why are there no file conflicts?

    test.pl
    #!/usr/bin/perl use warnings; use strict; use TestMod1; use TestMod2; use TestUtil; print "in file.\n"; write_log('file');
    TestMod1.pm
    #!/usr/bin/perl package TestMod1; use warnings; use strict; use TestUtil; print "loaded Mod1\n"; write_log('Mod1'); 1;
    TestMod2.pm
    #!/usr/bin/perl package TestMod2; use warnings; use strict; use TestUtil; print "loaded Mod2\n"; write_log('Mod2'); 1;
    TestUtil.pm
    #!/usr/bin/perl package TestUtil; use strict; use warnings; require Exporter; our @ISA = qw(Exporter); our @EXPORT = qw(write_log); print "loading module TestUtil\n"; #our $LOG if not $LOG and print "LOG did not exist.\n"; our $LOG; open $LOG, '>', 'test.log' or die; print "\$LOG = $LOG\n"; sub write_log { my $string = shift; print $LOG "$string\n"; } 1;
    test.log
    Mod1 Mod2 file
    screen output
    loading module TestUtil $LOG = GLOB(0x4006fcd4) loaded Mod1 loaded Mod2 in file.
      I'm hesitant about replying as I'm certainly no Perl internals guru. But, from the evidence you've provided in this example, one is forced to come to the conclusion that there is some magic involved with 'use' such that when using files multiple times, the resulting code is actually only loaded and run once.

      If this were not the case then you should have gotten several 'loading module TestUtil' lines, and several $LOG = GLOB(someaddress) lines. You didn't, so it works like you want it to. Be glad and rejoice.

      -Scott

        by george! you are correct. now i remember seeing an example of this process somewhere, how the compiler skips already-loaded modules. the print statements do make that quite obvious--thanks for pointing that out--hooray! :)