Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

BEGIN vs initialization

by Wiggins (Hermit)
on Nov 28, 2009 at 20:32 UTC ( [id://809940]=perlquestion: print w/replies, xml ) Need Help??

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

I want to use a BEGIN block to set a global context variable that other modules will use to adjust their control flow. But I remember the C++ "static initialization" pattern, which ran code before 'main' was called.

The question is
-When are initializations done?
-Are initialization code waiting to execute when the block is first entered?
-Are File globals initialized at parse or execute time?
-Is there any difference when initializing from a constant? a variable? a variable that is initialized from a constant? a function call?

When I began this 2 years ago, I could find no good documentation on the pragma "vars" other than "it pre-allocates global variables". That really says nothing about scope, mechanics, accessibility,... But I did get 'our' to work, and I like the explicit "Gbl::" prefix as self documentation.

This is the module of global values.

#GBL.pm package Gbl; #== Context ============ our $runHTTP = 1; our $runSMTP = 2; our $runContext = $runHTTP; # HTTP ...variable assignment might be af +ter BEGIN #our $runContext = 1; # HTTP ...variable assignment might be a +fter BEGIN # ... 1

This is the main controlling file (which will 'use Gbl;"

#main.pl use feature qw( :5.10 ); use strict; use warnings; use File::Temp qw/ tempfile tempdir /; use Email::MIME; use lib "/usr/local/mySystem"; use Gbl; BEGIN{ # Set some Globals for this environment $Gbl::runContext = $Gbl::runSMTP; #$Gbl::runContext = 2; $Gbl::pi_LogPath = "/tmp/primarylog.log" ; # log $Gbl::ds_LogPath = "/tmp/datalog.log" ; } use parseHTMP; use parseSMTP; #...

If the initialization executes immediately after parse, then

  1. $runHTTP is set to 1 durnig Gbl parse upon 'use' in main.pl
  2. $runSMTP is set to 2 durnig Gbl parse upon 'use' in main.pl
  3. $runContext = $runHTTP upon parse (==1)
  4. $Gbl::runContext = $Gbl::runSMTP (==2) upon BEGIN in main.pl
If it is at execute time
  1. code to set $runHTTP to 1 is prepared durnig Gbl parse upon 'use' in main.pl
  2. code to set $runSMTP to 2 is prepared durnig Gbl parse upon 'use' in main.pl
  3. code to set $runContext = $runHTTP is prepared upon parse
  4. $Gbl::runContext = $Gbl::runSMTP (== undef) upon BEGIN in main.pl
  5. Execution finally passed through Gbl, causing initialization code to execute.
    $Gbl::runContext = $Gbl::runHTTP

The initializers had not yet run in Gbl to define the variables at the point when main.pl's BEGIN block runs and accesses them?

It is always better to have seen your target for yourself, rather than depend upon someone else's description.

Replies are listed 'Best First'.
Re: BEGIN vs initialization
by almut (Canon) on Nov 28, 2009 at 23:07 UTC

    Variables are initialised when the respective code executes, and BEGIN blocks execute as early as possible, i.e. as soon as they are defined.  (And use implies BEGIN.)

    #!/usr/bin/perl use strict; use warnings; BEGIN { print "3: foo=$::foo\n"; our $foo = 42; print "4: foo=$foo\n"; BEGIN { print "1: foo=$foo\n"; $foo = 43; print "2: foo=$foo\n"; } print "5: foo=$foo\n"; $foo = 44; } BEGIN { print "6: foo=$::foo\n"; } __END__ Use of uninitialized value in concatenation (.) or string at ./809940. +pl line 11. 1: foo= 2: foo=43 3: foo=43 4: foo=42 5: foo=42 6: foo=44

    Note that although our $foo = 42; has already been "seen" during compilation, the variable is still undefined in the nested BEGIN block (which runs before the outer one).

Re: BEGIN vs initialization
by ikegami (Patriarch) on Nov 29, 2009 at 00:00 UTC

    Your questions don't make sense because there's no initialisation in Perl in the sense that you're picturing. Here's what you are missing:

    • "=" doesn't denote an initialiser in Perl. It's always just a normal assignment.
    • BEGIN blocks are executed as soon as they are compiled.
    • use Module; is the same as BEGIN { require Module; import Module; }.
    • Using require to load a file simply runs it as any other script (if it's not already loaded). It is both compiled and executed.

    Taking a simplified version of your code

    #main.pl use Gbl; BEGIN{ $Gbl::runContext = $Gbl::runSMTP; } ...
    # GBL.pm package Gbl; our $runSMTP = 2 1;

    Let's apply what I've said above to determine the order in which everything is executed
    main.pl
    1. Compile the code.
      1. use Gbl; is compiled.
      2. use Glb; is executed. (BEGIN blocks are executed as soon as they are compiled.)
        1. require Gbl; is executed.
          Gbl.pm
          1. Compile the code.
            1. package Gbl; is compiled.
            2. our $runSMTP = 2; is compiled.
            3. 1; is compiled.
          2. Execute the code.
            1. our $runSMTP = 2; is executed. (This is where 2 is assigned to $runSMTP.)
            2. 1; is executed.
        2. import Gbl; is executed.
      3. BEGIN { ... } is compiled.
        1. $Gbl::runContext = $Gbl::runSMTP; is compiled.
      4. BEGIN { ... } is executed. (BEGIN blocks are executed as soon as they are compiled.)
        1. $Gbl::runContext = $Gbl::runSMTP; is executed. (This is where 2 is assigned to $runContext.)
      5. The rest of the program ("...") is compiled.
    2. Execute the code.
      1. The rest of the program ("...") is executed.

Re: BEGIN vs initialization
by JavaFan (Canon) on Nov 28, 2009 at 23:54 UTC
    The general idea is that perl compiles the entire 'unit' (typically a file, or a string eval), then execute it. There are two exceptions:
    1. A BEGIN block. The block is run right after it is compiled. The block is compiled entirely before it's run.
    2. A use statement. This one is run as soon as it's compiled. One might see a use statement as a require and an import call inside a BEGIN block. The side-effect of a use statement is that the used module is compiled and executed.
    So, it your case, the order is (ignoring all the other modules):
    1. Compile main up to the 'use Gbl;' line.
    2. Run 'use Gbl;'. This causes:
      1. Compile Gbl.pm.
      2. Run the code in the Gbl package.
      3. Call 'Gbl->import' (if Gbl::import is defined).
    3. Compile the BEGIN block.
    4. Execute the code in the BEGIN block.
    5. Compile the rest of main.pl.
    6. Run main.pl (except the already run use statements and BEGIN blocks).
      I did go through the 'perlmod' to find the part about BEGIN executing as soon as parsed (before the rest of the file is parsed). Your explanation describes a set of events that answers all my questions.

      Step 2.2 (execute Gbl) takes place before returning to parse more of the main.pl! Therefore the variables will hold valid definitions when main's BEGIN block used them.

      I was picturing that in the absence of BEGIN and CHECK sort of diversions, that the entire file structure (including all the 'use' modules) would be assembled/parsed, then executed starting back at the top. That is a holdover from the old 'C'/cpp days.

      That also explains why BEGIN blocks execute during a "perl -c main.pl" syntax checking run.

      Thanks for a the concise answer to my fundamental question..

      It is always better to have seen your target for yourself, rather than depend upon someone else's description.

        I did go through the 'perlmod' to find the part about BEGIN executing as soon as parsed

        If you didn't realize that, then I'm curious as to why you placed parsing Glb first in both scenarios? It seems to indicate you knew use Glb; was executed as soon as it was parsed.

        If you knew that, the key point you were really missing was that require and thus use execute the module as any other script. It gets parsed and executed. That's got nothing to do with BEGIN.

Re: BEGIN vs initialization
by morgon (Priest) on Nov 28, 2009 at 22:26 UTC
    Unfortunately I am in a hurry at the moment so sorry for being short.

    Have a look in the perlmod-manpage to learn about BEGIN, UNITCHECK, CHECK, INIT and END and when they are run.

    But in my opinion you should not configure your program with global variables.
    There are so many problems with this approach that I don't even know where to start...

    My approach would be to have a configuration object that is a singleton (a posher version of a global variable :-) and that can either be initialized in main or in a "lazy" way when it is needed.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://809940]
Approved by broomduster
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others browsing the Monastery: (3)
As of 2024-03-19 06:21 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found