Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses
 
PerlMonks  

Perl templating/macro creating using 'BEGIN'...

by perl-diddler (Chaplain)
on Sep 27, 2010 at 16:56 UTC ( [id://862246]=perlquestion: print w/replies, xml ) Need Help??

perl-diddler has asked for the wisdom of the Perl Monks concerning the following question:

I have the following code:
#!/usr/bin/perl -w use strict; use feature ':5.10'; BEGIN { sub main_package_vars { foreach(@_) { eval "sub $_ { my \$p = shift; \$p->{$_} = \$_[0] if \@_; \$p->{$_}; }" } } } package MyPackage; BEGIN { eval "*package_vars2 = \*main::main_package_vars"; *package_vars3 = \*main::main_package_vars; } { BEGIN { sub package_vars { foreach(@_) { eval "sub $_ { my \$p = shift; \$p->{$_} = \$_[0] if \@_; \$p->{$_}; }" } } } package_vars3( qw(one two three) ); sub new { my $package=shift; my $parms=$_[0]; my $this={}; foreach(%$parms) { $this->{$_}=$parms->{$_}; } bless $this, $package; } } package main; my $p=new MyPackage({three => 3,}); $p->two(1); printf "two=%d, three=%d\n",$p->two, $p->three;
When I define package variables one two and three, with 'package_vars', it works.

When I try to use a copy of package_vars from 'main', I can't figure out how to import the symbol into the package to make it work -- at execution time I get undefined 'two'.

So how do I get my sub created in main, imported into my package so that my "defined" statement, the package vars3( qw(one two three) ); works? I've tried a few variations besides the ones shown here. But can't see why the symbols aren't created during the 'BEGIN' run the same as the actual sub's are.

Anyone know why this isn't working or how to make it work? Thanks --- trying to understand how some packages work among other things...

Replies are listed 'Best First'.
Re: Perl templating/macro creating using 'BEGIN'...
by Corion (Patriarch) on Sep 27, 2010 at 17:04 UTC
    BEGIN { sub main_package_vars { foreach(@_) { eval "sub $_ { my \$p = shift; \$p->{$_} = \$_[0] if \@_; \$p->{$_}; }" } } }

    Why do you put this in a BEGIN block? It serves no purpose, at least when Perl looks at it.

    Also, why are you using string-eval, when the plain eval works just as well. Also see Class::Accessor.

    sub main_package_vars { foreach my $name (@_) { no strict 'refs'; *{ $name } = sub { my $p = shift; $p->{$name} = $_[0] if @_; $p->{$name}; }" } }

    In this next snippet from MyPackage, I don't understand why you use string-eval, and also why you try to execute the same statement twice:

    BEGIN { eval "*package_vars2 = \*main::main_package_vars"; *package_vars3 = \*main::main_package_vars; }

    The subroutine package_vars3 does not exist:

    package_vars3( qw(one two three) );

    What problem are you trying to solve? Do you want to have a hash using which you get/set variables?

    Maybe an object based on a plain hash using AUTOLOAD is already enough? If you want control over what keys get stored, again, see Class::Accessor.</c>

      The first 'main_package_vars' is in a BEGIN block because it is a duplicate of the one below it. I want to move the BEGIN block out of the package declaration and into the 'main' block.

      Then I want to use the package_vars definition in 'main'. I.e. I started with a BEGIN block only in a package like this (which works, but doesn't work if I remove the BEGIN block as you seem(?) to be suggesting).

      #!/usr/bin/perl -w use strict; use feature ':5.10'; package MyPackage; { BEGIN { sub package_vars { foreach(@_) { eval "sub $_ { my \$p = shift; \$p->{$_} = \$_[0] if \@_; \$p->{$_}; }" } } } package_vars( qw(one two three) ); sub new { my $package=shift; my $parms=$_[0]; my $this={}; foreach(%$parms) { $this->{$_}=$parms->{$_}; } bless $this, $package; } } package main; my $p=new MyPackage({three => 3,}); $p->two(1); printf "two=%d, three=%d\n",$p->two, $p->three;
      Now I try moving the BEGIN block out of the package and into main (thus 'main_package_vars'), and import it's functionality into the local package.

      That's the problem I'm trying to solve. How to have my subroutine definition for 'package_vars' in 'main', and use it from a package, like in this (which doesn't work because package_vars, isn't being 'called' in MyPackage, now that it's been moved to main).

      #!/usr/bin/perl -w use strict; use feature ':5.10'; BEGIN { sub package_vars { foreach(@_) { eval "sub $_ { my \$p = shift; \$p->{$_} = \$_[0] if \@_; \$p->{$_}; }" } } } package MyPackage; *package_vars=\&main::package_vars; { package_vars( qw(one two three) ); sub new { my $package=shift; my $parms=$_[0]; my $this={}; foreach(%$parms) { $this->{$_}=$parms->{$_}; } bless $this, $package; } } package main; my $p=new MyPackage({three => 3,}); $p->two(1); printf "two=%d, three=%d\n",$p->two, $p->three;
      The first version I posted had samples of the routine package_vars in each location where I could try changing the line that says 'package_vars3( qw(one two three));' to a line without the '3', and, call the original working version, vs. attempts at trying some way to call the version of 'package_vars' that was moved to main.

      Is that more clear?

        Some of the steps you take are somewhat clear, but I'm really wondering about the goal you're trying to reach by these steps. To me, it really seems as if you're trying to duplicate the functionality of Class::Accessor. The usage of Class::Accessor would be:

        package MyPackage; use parent qw(Class::Accessor); __PACKAGE__->mk_accessors(qw(two three)); package main; use strict; my $f = MyPackage->new({three => 3}); $f->two(1); printf "two=%d, three=%d\n",$f->two, $f->three;

        This seems to be your use case, so why reinvent the wheel?

      Also, why are you using string-eval, when the plain eval works just as well.
      Because it didn't work when I tried it. Perhaps your perl works differently?
      #!/usr/bin/perl -w use strict; use feature ':5.10'; package MyPackage; { BEGIN { sub package_vars { foreach(@_) { eval sub $_ { my \$p = shift; \$p->{$_} = \$_[0] if \@_; \$p->{$_}; }; } } } package_vars( qw(one two three) ); sub new { my $package=shift; my $parms=$_[0]; my $this={}; foreach(%$parms) { $this->{$_}=$parms->{$_}; } bless $this, $package; } } package main; my $p=new MyPackage({three => 3,}); $p->two(1); printf "two=%d, three=%d\n",$p->two, $p->three;
      I got: Illegal declaration of anonymous subroutine at pck0.pl line 10. What do you get?

        Sorry - you don't even need "plain eval". Just assigning the anonymous subroutine to the glob is all that's needed and creates a named subroutine from the anonymous subroutine.

Re: Perl templating/macro creating using 'BEGIN'...
by ig (Vicar) on Sep 27, 2010 at 19:14 UTC

    The reason your subroutines are not defined in your package 'MyPackage' is that your subroutines are being defined in package main, rather than in package MyPackage. Even though you use a reference to the subroutine main_package_vars that is defined in package MyPackage (i.e. package_vars3) the subroutine (main_package_vars) still runs in the context of package main and the subroutines it defines are defined in package main.

    By using fully qualified names, you can create the subroutines in any package name space. One option is to use __PACKAGE__ to pass the name of your package to main_package_vars and use this to create fully qualified names.

    You might also be interested to use Data::Dumper to inspect the namespace of your package.

    Here is a modified version of one of your code samples that might help you understand what is happening:

    #!/usr/bin/perl -w use strict; use feature ':5.10'; BEGIN { sub package_vars { my $package = shift; foreach(@_) { eval "sub ${package}::$_ { my \$p = shift; \$p->{$_} = \$_[0] if \@_; \$p->{$_}; }" } } } package MyPackage; use Data::Dumper; { main::package_vars( __PACKAGE__, qw(one two three) ); print Dumper(\%MyPackage::); sub new { my $package=shift; my $parms=$_[0]; my $this={}; foreach(%$parms) { $this->{$_}=$parms->{$_}; } bless $this, $package; } } package main; my $p=new MyPackage({three => 3,}); $p->two(1); printf "two=%d, three=%d\n",$p->two, $p->three;
      That's exactly what I want to do -- thank you for your understanding my question! However ... (sigh),... how can I do it without explicitly passing "__PACKAGE_" at the point of call. How can I tell what package 'main_package_vars' is called invoked from w/o explicitly passing it the package name?

      After reading your response, I tried some way of delaying the interpretation of '__PACKAGE__' to interpret it at expansion time, below, but I think I keep ending up with 'two' in main or in the packaged named '__PACKAGE__', which is no help! If you see what I mean...?

      I.e., if I was using some package like Class::Accessor, I don't see, yet, how it is getting the current package name and putting the routines into the current package's name space, but this is definitely the direction I'm looking for...maybe I just need to play around with this idea a bit and I'll bump into it...(?)...

        The ->import subroutine that is called from a use statement can find out the name of the calling package by using caller.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others romping around the Monastery: (3)
As of 2024-03-29 05:52 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found