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

Dear Monks, Hoping you can help. I can call the subroutine first_name() like this:

$self->xml()->first_name();

However I want to call it when 'first_name' is a constant. The following code doesn't work

use constant { STUFF => { 'name1' => 'first_name', 'name2' => 'last_name', }, }; foreach my $val (keys ${\( STUFF() ) } ){ my $value = eval("$self->xml()->${\( STUFF() ) }->{ $val }()"); }

I can't use a code ref like below because of how it needs to be called ( perhaps there is a way? ) Note: I cannot use Readyonly either. I've also tried various combinations of \&{$variable} but can't seem to get it to work.

use constant { STUFF => { 'name1' => $self->xml()->first_name(), 'name2' => $self->xml()->last_name(), }, };

Please help if you can. Thanks

Replies are listed 'Best First'.
Re: Calling a subroutine when part of call is a constant variable (symbolic method names) <2 updates>
by LanX (Saint) on May 26, 2017 at 22:49 UTC
    All this eval stuff looks overly complicated.

    You can simply put a methods name into a variable, here $meth and call it dynamically

    use strict; use warnings; use Data::Dump; my $o = new Class; my $count=0; for my $meth (qw/foo bar baz/) { $o->$meth($count++); } package Class; sub new { return bless {} } sub foo { my $self =shift; warn "foo: @_\n" } sub bar { my $self =shift; warn "bar: @_\n" } sub baz { my $self =shift; warn "baz: @_\n" }

    out

    foo: 0 bar: 1 baz: 2

    If you have problems using a constant in this syntax, then why not simply copy it first into a variable? This will certainly improve readability.

    NB: Readonly should work anyway!

    HTH! :)

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

    UPDATE:

    and if you rather prefer unreadable syntax

    my %meth = ( foo => "foo" ); $o->${\ $meth{foo} }($count++);

    UPDATE2

    this answers your original question:

    use constant { STUFF => { 'BAR' => 'bar', }, }; my $key = "BAR"; $o->${ \ STUFF->{$key} }($count++);

    Perl needs to see a $-sigil to accept a symbolic method name, that's why deref of a scalar ref ${ \ "bar" } is a workaround.

    But you forgot to put the $key part inside the deref

      Sorry don't think this addresses my question

        In what way does it not address your question?


        Give a man a fish:  <%-{-{-{-<

        It answers it perfectly.

        Dear Monks, Thanks for all your suggestions. I appreciate the time taken to help out. The Perl community really is the best! I figured it out, this is all I needed to get my code to execute correctly.

        foreach my $val (keys ${\( STUFF() ) } ){ my $subroutine = ${\( STUFF() ) }->{ $val }; my $value = $self->xml()->$subroutine(); }
Re: Calling a subroutine when part of call is a variable Contant
by kcott (Archbishop) on May 27, 2017 at 05:37 UTC

    In short, I think this may be all you need:

    $self->xml()->$_() for values %{STUFF()};

    You didn't describe the relationship between the various values and functions; what packages are in use; or what the functions do internally. I've made some (fairly gross) guesses for the test code below; hopefully, you can adapt this for your specific requirements.

    #!/usr/bin/env perl -l use strict; use warnings; package With::Xml; sub new { bless {} => 'With::Xml' } sub xml { print "With::Xml::xml(): @_"; $_[0] } sub first_name { print "With::Xml::first_name(): @_" } sub last_name { print "With::Xml::last_name(): @_" } package main; use constant STUFF => { name1 => 'first_name', name2 => 'last_name', }; my $self = With::Xml::->new(); $self->xml()->$_() for values %{STUFF()};

    Output:

    With::Xml::xml(): With::Xml=HASH(0x7f9b998040b0) With::Xml::first_name(): With::Xml=HASH(0x7f9b998040b0) With::Xml::xml(): With::Xml=HASH(0x7f9b998040b0) With::Xml::last_name(): With::Xml=HASH(0x7f9b998040b0)

    — Ken

Re: Calling a subroutine when part of call is a variable Contant
by AnomalousMonk (Archbishop) on May 27, 2017 at 01:32 UTC

    You might also use UNIVERSAL::can(), which might make the OO interface slightly more regular:

    c:\@Work\Perl\monks>perl -wMstrict -le "print qq{perl version: $] \n}; ;; use constant { STUFF => { 'bizz' => 'foe', 'bazz' => 'fie', 'bozz' => 'fee', 'buzz' => 'wiz', }, }; ;; my $obj = Kid->new; ;; my $n = 0; ;; METAMETHOD: for my $meta_method (qw/bizz buzz bazz zotz bozz/) { if (not exists STUFF->{$meta_method}) { print qq{no method corresponds to '$meta_method'}; next METAMETHOD; } ;; my $method = STUFF->{$meta_method}; ;; my $coderef = $obj->can($method); if (not $coderef) { print qq{cannot '$meta_method'->'$method' on }, ref $obj; next METAMETHOD; } $coderef->($obj, $method, $n++); } ;; { package Dad; ;; sub new { my $class = shift; return bless \my $x => $class; } sub fee { my $self = shift; $self->all(@_); } sub all { my $self = shift; print qq{method '$_[0]', n == $n}; } } ;; { package Kid; ;; use parent -norequire, qw(Dad); sub new { my $class = shift; return $class->SUPER::new; } sub fie { my $self = shift; $self->all(@_); } sub foe { my $self = shift; $self->all(@_); } } " perl version: 5.008009 method 'foe', n == 1 cannot 'buzz'->'wiz' on Kid method 'fie', n == 2 no method corresponds to 'zotz' method 'fee', n == 3
    Note the
        $coderef->($obj, $method, $n++);
    syntax for the code reference method call.


    Give a man a fish:  <%-{-{-{-<

      > Note the 
          $coderef->($obj, $method, $n++); 
      syntax for the code reference method call.

      Side note:

          $obj->$coderef($method, $n++);

      Should do, too. :) 

      Cheers Rolf
      (addicted to the Perl Programming Language and ☆☆☆☆ :)
      Je suis Charlie!

        Should do ...

        Aha! And indeed it does! (Somehow, I thought the roundabout syntax was necessary...)


        Give a man a fish:  <%-{-{-{-<