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

I just downloaded and started using Excel::Writer::XLSX and have written some buggy code. I have isolated the problem down to a few simple lines. The following script will produce an error when provided an argument so that it uses the $closure_workbook variable. (I can't remember the term for this kind of variable that is scoped outside the current block, but I thought it was a "closure".) The error is

Use of uninitialized value in numeric eq (==) at C:/strawberry/perl/site/lib/Excel/Writer/XLSX/Workbook.pm line 839 during global destruction.

Without the argument, the script uses the $workbook variable, runs fine and saves the workbook as expected.

I am running this on Strawberry Perl 5.16.2 on Windows 8. Excel::Writer::XLSX is version 0.67. Please help!

Thanks, John

The script:

use strict; use Excel::Writer::XLSX; &func(@ARGV); { my $closure_workbook; sub func { my $book; if (@_) { $book = $closure_workbook = Excel::Writer::XLSX->new( 'perl_bu +g.xlsx' ); } else { my $workbook = Excel::Writer::XLSX->new( 'perl_good.xlsx' ); $book = $workbook; } my $worksheet = $book->add_worksheet(); $worksheet->write(0, 0, 'Hi Excel!'); } }

Replies are listed 'Best First'.
Re: variable scopes in Excel::Writer::XLSX
by toolic (Bishop) on May 25, 2013 at 01:26 UTC
    Maybe this simplified code can help you isolate your problem:
    use strict; use warnings; use Excel::Writer::XLSX; my $file = (@ARGV) ? 'perl_bug.xlsx' : 'perl_good.xlsx'; my $book = Excel::Writer::XLSX->new($file); my $worksheet = $book->add_worksheet(); $worksheet->write( 0, 0, 'Hi Excel!' );
      Thanks, but the code I posted was not written to be the most concise and direct way to perform a task. It was written to demonstrate the error that I found. It may be oddly structured as a result. The choice of file names is really secondary to the problem.
Re: variable scopes in Excel::Writer::XLSX
by johnrcomeau (Novice) on May 25, 2013 at 04:35 UTC
    He may have a point though. I condensed the error down to a simpler example. Simply ASSIGNING the workbook to a closure variable produces the error:
    use strict; use Excel::Writer::XLSX; &func(@ARGV); { my $closure_workbook; sub func { my $file = 'perl.xlsx'; my $book = Excel::Writer::XLSX->new($file); if (@_) { $closure_workbook = $book; } my $worksheet = $book->add_worksheet(); $worksheet->write(0, 0, 'Hi Excel!'); } }
      People seem to be misunderstanding the point of my question. If you look at this code, you can see that there is a single line that may optionally be executed: the line that assigns $book to the static variable $closure_workbook. It's not worth explaining why, but I want to store $book in a static variable. And I see no reason that the one extra assignment should cause an error.
      use strict; use Excel::Writer::XLSX; &func(@ARGV); { my $closure_workbook; sub func { my $file = 'perl.xlsx'; my $book = Excel::Writer::XLSX->new($file); if (@_) { $closure_workbook = $book; } my $worksheet = $book->add_worksheet(); $worksheet->write(0, 0, 'Hi Excel!'); } }
        If you look at this code...

        By "this code", I assume you mean the code posted at the end of the Re^2: variable scopes in Excel::Writer::XLSX node, and that this code is all that is needed to produce the error you describe. (I'm not in a position right now to run the code; all I can do is literally 'look' at it.)

        ... I see no reason that the one extra assignment should cause an error.

        Nor do I. The  $closure_workbook variable is never touched except to assign it a value in certain circumstances. It is never otherwise accessed within  func() and no other function than  func() can possibly access this lexical variable (unless there's something you're not showing us). The next step is to eliminate the
            if (@_) {
                $closure_workbook = $book;
            }
        block of code (leaving the  my $closure_workbook; statement just for grins) and see if the error still occurs. My bet is it will. This means the root cause of your problem is entirely unconnected to anything you're doing with this 'closure' variable.

        For a while, I had my suspicions about the  &func(@ARGV); ampersand invocation of  func(), but I can't see this has any relevance.

        Update: Oops... Should have refreshed the thread before posting. I see from Re^4: variable scopes in Excel::Writer::XLSX that the problem is resolved. At any rate, the problem had, indeed, nothing to do with anything that was happening with the 'closure' variable.

        People seem to be misunderstanding the point of my question..
        Because what you intended is not clear. Closures in perl are not written like that. And you must have reason for what you are doing. You are calling a variable that is outside a function within the function, to assign to it an object. To what end? Oh I just want to call it. Is that not funny? Please take a look at the various suggestions already given. The solutions you wanted might be somewhere in the middle, if not already given.

Re: variable scopes in Excel::Writer::XLSX
by 2teez (Vicar) on May 25, 2013 at 13:00 UTC

    Hi johnrcomeau,
    Really, I can't figure what you really wanted to use, however you can use this: [the code below actually works]

    use warnings; use strict; use Excel::Writer::XLSX; func(@ARGV); sub func { my $file = 'perl.xlsx'; my $book = Excel::Writer::XLSX->new($file); my $worksheet = $book->add_worksheet(); my $closure_workbook; if (@_) { $closure_workbook = sub { $worksheet->write_col( 0, 0, [@_] ); }; return $closure_workbook->(@_); } else { $worksheet->write( 0, 0, 'Hi Excel!' ); } }

    If you tell me, I'll forget.
    If you show me, I'll remember.
    if you involve me, I'll understand.
    --- Author unknown to me
Re: variable scopes in Excel::Writer::XLSX
by Anonymous Monk on May 28, 2013 at 09:14 UTC
    Try adding a workbook close() to synchronize the garbage collector and the implicit destroy method. See the docs for more info.