in reply to Computed "my" declarations?

You'll have to use code generation. The result is that you get a subroutine that's hard-coded to use a list of stuff and you just change how it compiles if you need to alter it. I included a #line declaration so the compilation errors, if any, would come out on the right line.

package Foo::Bar; BEGIN { my @attributes = qw( ... ); eval '#line ' . (1+__LINE__) . ' "' . __FILE __ . '" sub ... { ... my ( ' . join( ', ', map { "\$$_" } @attributes ) . ' ) = @{$self}{qw(' . join( ' ', @attributes ) . ')}; ... }'; die $@ if $@; }

This node has been updated to fix some minor bugs including the omission of the elipses after the my declaration. This mistake on my part confused someone about my intentions. I did not intend to indicate that the subroutine had no more statements in it.

Replies are listed 'Best First'.
Re^2: Computed "my" declarations?
by Tanktalus (Canon) on Oct 26, 2005 at 23:23 UTC

    Won't that create the my variables inside the scope of your sub, which will then go out of scope pretty much immediately? I don't think the OP is asking the right question because s/he's talking about package-scope variables, which are, by their nature, global, not lexical. In that case, it becomes much easier:

    for (@attributes) { no strict 'refs'; ${$package . '::' . $_} = do { ... something to do with $_ ... }; }
    Of course, if $package is supposed to be this package, then that is a bit simpler - change $package to __PACKAGE__, and you're done.

    Update: now that diotalevi has added some extra ellipses, I can see how he's proposing to do this without the variables going out of scope. And now I think I better understand the OP's question, which has nothing to do with globals because he's trying to create a variable that is a string. And that string just happens to look like a global variable name.

    In this case, I'd suggest a number of possibilities. First thing to come to my head is stupid, so I'm going to skip that one. ;-) The next idea is to just create the constants that you're going to need using constant:

    use constant { map { uc $_ => __PACKAGE__ . "::$_" } qw(name timestamp etc.) };
    Then you don't need to keep redeclaring anything.

    (I said a number of possibilities, right?) Still, use a hash. The usage would be something like $self->{$my{name}} which is still a lot lighter than $self->{__PACKAGE . '::name'}

    And back to the stupid one. Create a package, say called "My" or "P" or something. Set up an AUTOLOAD function which looks at the package of the caller, and just returns that package name concatenated with '::' and then the function being called. Usage would then be $self->{P->name}

    But don't do that last one.

      No. It is creating a subroutine which lists everything explicitly when compiled but didn't have to be written that way. Assuming the named variables were foo, bar, and baz, here is what the subroutine would look like. I've left all the uninteresting parts filled with ellipses and you're expected to understand that something useful would go there.

      Obviously, the code author is going to want to do something with those variables or there's no point to extracting them in the first place.

      sub ... { ... my ( $foo, $bar, $baz ) = @{$self}{qw(foo bar baz)}; ... }

      I think it is an error on everyone's part to talk about packaged scoped variables. That's only because there's no way to refer to a lexical symbolically and its a bug in everyone else's brain that they went that way.

      PS, I updated this node and the parent. Both to add this note and to fix a bug where the list of attributes wasn't correctly quoted in qw(...) marks.