Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask
 
PerlMonks  

More Macro work...out to export a definition to next-outer level?

by perl-diddler (Hermit)
on Sep 30, 2010 at 03:48 UTC ( #862741=perlquestion: print w/ replies, xml ) Need Help??
perl-diddler has asked for the wisdom of the Perl Monks concerning the following question:

I was trying to expand my macro ability into per-class vars, and decided I'd like it to declare the variables using the 'our' construct. Unfortunately, while it work (under non-strict with warnings, running it under 'strict' generates errors. I'd rather it not generate either.

Presumably, its because my 'our' definition is defining the variables in the 'enclosed scope' of the eval, so pop, come out of the eval, and the 'our' is no longer in effect and vars are undefined. I've tried moving the package and our out of the eval, but to save context, I was storing the package and trying to reset it when done -- but perl is giving a syntax error when I use a variable with a package. Seems odd, but I guess that's not allowed (though I don't see that documented under 'perlfunc package'). Here's code that demonstrates the problem:

#!/usr/bin/perl -w use strict; use feature ':5.10'; # note use of BEGIN and 'caller' to achieve macro definition... BEGIN { sub class_vars { my $pck=caller; foreach(@_) { eval " my \$p=\"__PACKAGE__\"; package ${pck}; our \$$_; sub ${pck}::$_ { shift; \$$_ = \$_[0] if \@_; \$$_; } package \$p; " } } } package MyPackage; *class_vars=\&main::class_vars; { class_vars( qw(one two three) ); sub init_one_from_three { $one=$three-2; } sub new { my $package=shift; my $parms=$_[0]; my $this={}; foreach(keys %$parms) { $$_=$parms->{$_}; } bless $this, $package; } } package main; my $p=new MyPackage({three => 3,}); $p->two(1); $p->init_one_from_three; printf "two=%d, three=%d, one=%d\n",$p->two, $p->three, $p->one;
Without "-w", it will give the correct answer, but with warnings -- which I'd also like to get rid of. I switched to trying to switch package because it didn't like a package declaration in an 'our' statement. Obviously this version of the code isn't removing the redundant package declaration from the sub.

Any ideas from those more learned that I? Much appreciated!

Comment on More Macro work...out to export a definition to next-outer level?
Download Code
Re: More Macro work...out to export a definition to next-outer level?
by ikegami (Pope) on Sep 30, 2010 at 06:09 UTC

    class_vars was being called too late to be of any use.

    Putting a BEGIN block around a sub definition is useless, as a sub definition doesn't generate code to execute.

    Contrary to what you said, you weren't getting a syntax error, you were getting a strict error. Quite appropriate since you asked for it.

    Solution:

    BEGIN { package class_vars; use strict; use warnings; sub import { shift; my $pkg = caller; for my $var (@_) { eval(" package $pkg; use vars '\$$var'; sub $var { \$$var = \$_[1] if \@_ > 1; return \$$var; } "); } } $INC{'class_vars.pm'} = __FILE__; } { package MyPackage; use strict; use warnings; use class_vars qw( one two three ); sub init_one_from_three { $one=$three-2; } sub new { my $package=shift; my $parms=$_[0]; my $this={}; foreach(keys %$parms) { no strict 'refs'; $$_=$parms->{$_}; } bless $this, $package; } } { use strict; use warnings; my $p=new MyPackage({three => 3,}); $p->two(2); $p->init_one_from_three; printf "one=%d two=%d, three=%d\n", $p->one, $p->two, $p->three; }
    one=1 two=2, three=3
    The following simulates putting the module in its own file:
    BEGIN { ... $INC{'class_vars.pm'} = __FILE__; }

    Package declarations are lexically scoped. You had lots of needless package switching.

    I left in the limit that class vars must be scalars.

    You call them class vars, but then you initialise them when constructing an instance, and you access them via a class instance. They're not class variables at all. They're object attributes, and you're going the wrong way about creating a singleton.

    Update: Adjust the value in %INC as per reply.

      $INC{'class_vars.pm'} = 1;
      Please use a filename instead of 1, like
      $INC{'class_vars.pm'} = __FILE__;
      Sorry, you just lost me -- you used a magic spell above my level. ;-)

      In my previous example: #!/usr/bin/perl -w #use strict; use feature ':5.10'; BEGIN { sub instance_vars { my $pck=caller; foreach(@_) { eval "sub ${pck}::$_ { my \$p = shift; \$p->{$_} = \$_[0] if \@_; \$p->{$_}; }" } } } package MyPackage; *instance_vars=\&main::package_vars; { instance_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(2); printf "two=%d, three=%d\n",$p->two, $p->three; </code> I didn't have any reference to use or import.

      To invoke the code, I called a sub 'instance_vars', that was in a begin block. The reason the package statements are / were in the way they are is that the macros definitions are at the top of the file in 'main'.

      The way you are doing it may be the only way to do it, but it isn't close to as simple as the case for the 'instance' vars.

      You say that the above isn't satisfactory for 'singletons'. Why not? Aren't they global variables to each package that are initialized only once on the initial passthrough when the package is first read, and then retain their value as global variables in each package (or accessible via package::var) from without the package?

      The part where you put the classname.pm in the INC var likely prevents future 'use' statements from actually trying to find the file, I'd guess, and the 'use' statement does the same as: "package::import( qw(one two three) )"? It's workable, but I don't understand why something simple like I used for instance variables doesn't work.

      If putting a BEGIN block around a sub definition is useless, then how does this 'instance' var code work? It puts a sub definition in a BEGIN block which is then called in the code below. Also, the class_vars code *works* (with strict turned off, with warnings -- i.e. the variables being set in 'MyPackage' retain their values -- they aren't local but are treated as package variables, so I don't see how you can the subs don't work

      I think you are showing me an alternate way of doing what I'm doing, but I don't think it's answering my question -- which may have no good answer, i.e. the way you are doing it may be the only way -- I don't know.

      The 'sub' in main gets declared as a global as all named subs are, so in a BEGIN block or not, I think it is having the desired effect. The problem is adding an 'our' statement that is taken as a declarator for the rest of the block that the caller is in (not just in within the eval -- which is works for, but is ended when the eval ends and goes out of scope.

        That's lame! I edited the above, even previewed it, and resubmitted it with an inserted 'code' tag (it was missing one before the code, as you can see, it already had one after the code ;-/ ). But only the above version was stored and it seems I can't update it. Oh poo! Anyway, hopefully this version will stay with the code tag intact!
        #!/usr/bin/perl -w #use strict; use feature ':5.10'; BEGIN { sub instance_vars { my $pck=caller; foreach(@_) { eval "sub ${pck}::$_ { my \$p = shift; \$p->{$_} = \$_[0] if \@_; \$p->{$_}; }" } } } package MyPackage; *instance_vars=\&main::instance_vars; { instance_vars( qw(one two three) ); sub new { my $package=shift; my $parms=$_[0]; my $this={}; foreach(%$parms) { $this->{$_}=$parms->{$_}; } bless $this, $package; } } package MyPackagetwo; { our @ISA = qw (MyPackage); } package main; my $p=new MyPackagetwo({three => 3,}); $p->two(2); printf "two=%d, three=%d\n",$p->two, $p->three;

        You say that the above isn't satisfactory for 'singletons'.

        Because you're using two screens of fragile and complicated code to replace two lines of code.

        To invoke the code, I called a sub 'instance_vars', that was in a begin block.

        No, it's not in a BEGIN block. The sub's definition is in a BEGIN block, but I already indicates how that's useless.

        Aren't they global variables to each package that are initialized only once

        No, they're initialised by the constructor, any number of times.

        and then retain their value as global variables in each package (or accessible via package::var) from without the package?

        That may be, but you access them via method calls.

        I think you are showing me an alternate way of doing what I'm doing, but I don't think it's answering my question

        I did. Like I said, you called it too late. You only call class_vars after you try to compile the code that uses them. All I did was made a change to call it earlier.

Re: More Macro work...out to export a definition to next-outer level?
by Anonymous Monk on Oct 01, 2010 at 09:31 UTC
    Is it possible to write lisp kind of macros in Perl 5? How?, can somebody show it with a small snippet of code.

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (6)
As of 2014-08-22 07:17 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The best computer themed movie is:











    Results (148 votes), past polls