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

Hi - I am attempting to EVAL a subroutine defined in my XML file and later call it in the Perl code (the call is in the XML file).

I am having problems wherein I can't seem to get it work if my sub has any quoted arguments, so I am assuming I am just not escaping or interpolating the "" right?

My XML contains a sub as follows: sub mySub() { # stuff }

My call mySub() to the sub works fine. However, if I try mySub("Hello World"), it does not return anything upon evaluation. If I hard code the direct calls in my Perl script it works either ways.

Also in the code the line below will cause an error to be thrown saying "Useless use of string in void context at test.pl line XX."

#eval { eval { "$config->{client}->{1}->{file}->{1}->{executeInternal} +" } };

However, if I replace the "{}" with "()" it doesn't throw that error. Could someone please explain why using the block form does this?

eval ( eval { "$config->{client}->{1}->{file}->{1}->{executeInternal} +" } );

Below is the code (Perl and XML) that I am attempting to use:

Perl code:

#!/usr/bin/perl -w use warnings; use strict; use XML::Simple; # Getting the ref using XMLIn my $config = XMLin("test.xml", forceArray=>[qw(config client file)], s +uppressEmpty=>undef); # Print the actual subroutine and call from XML print "The actual code in the XML is:\n"; print $config->{client}->{1}->{file}->{1}->{subroutines}->{callInterna +lSub},"\n"; # Eval the sub - works fine - contains a mySub() call without argument +s print "EVALing the actual code in the XML. Works fine if my call is si +mply mySub() without arguments. \n"; eval (qq"{" . $config->{client}->{1}->{file}->{1}->{subroutines}->{cal +lInternalSub} . "}"); print "\n"; #Direct call to EVALed sub - works fine print "Direct call to the EVALed sub without arguments works fine. \n" +; mySub("hello world"); print "\n"; # Print a bunch of stuff print "Print a bunch of stuff:\n"; print "$config->{client}->{1}->{file}->{1}->{executeInternal}\n"; print eval { "$config->{client}->{1}->{file}->{1}->{executeInternal}" +},"\n"; print eval qq{"$config->{client}->{1}->{file}->{1}->{executeInternal}" +}; print "\n\nNow trying the actual eval:\n"; eval (qq"{" . $config->{client}->{1}->{file}->{1}->{executeInternal} . + "}"); # As expected, the line below works too (not because of the EVAL) - #eval (qq"{" . mySub("hello world") . "}"); # Do I have to escape my "" in my XML file? If I simple call mySub() i +n the XML, it works fine. # I tried a few things (\,\\) to no avail. # The line below will cause an error to be thrown saying "Useless use +of string in void context at test.pl line XX." #eval { eval { "$config->{client}->{1}->{file}->{1}->{executeInternal} +" } }; # However, if I replace the "{}" with "()" it doesn't throw that error +. Could someone please explain why using the # block form does this? #eval ( eval { "$config->{client}->{1}->{file}->{1}->{executeInternal} +" } );

XML code:

<config> <client name="1"> <file name="1"> <executeInternal>mySub(\"Hello World\")</executeInternal> <executeInternalWORKS>mySub()</executeInternalWORKS> <subroutines> <callInternalSub> sub mySub() { my $message = "blah"; my $message = shift; #broken version print "Message- $message\n"; print "Caller-> ",caller,"\n"; } mySub(); </callInternalSub> </subroutines> </file> </client> </config>

I apologize if the comments in the code are excessive. Thank you for your help.

Replies are listed 'Best First'.
Re: eval sub
by moritz (Cardinal) on Oct 09, 2008 at 19:56 UTC
    Also in the code the line below will cause an error to be thrown saying "Useless use of string in void context at test.pl line XX."

    That's a warning, not an error.

    What you have to understand is that eval { block here } and eval EXPRESSION are two fundamentally different things. The first one is like do { ... }, except that it catches excpetions (other languages call that try. do { "something" } warns because "something" doesn't serve any purpose, because it's not used anyway.

    So you just want eval $Your_string_here.

    Update: Forgot the links to the docs: eval.

Re: eval sub
by JavaFan (Canon) on Oct 09, 2008 at 20:03 UTC
    Could someone please explain why using the block form does this?

    Because you combine block and string. What you have is equivalent to:

    eval {"Hello!"}
    All you're executing is a string in void context. You don't want a block here - the code you have is in a string, so you want to do string eval. Hence, you ought to be doing
    eval $config->{client}->{1}->{file}->{1}->{executeInternal};
      Thanks for the responses guys.

      moritz, thanks for correcting me - you are right, it is a warning, not an error - that was a typo that percolated as I was doing my copy/paste. I might not be understanding you correctly and am still unsure why I can't make a sub call in block form. I am pretty sure this would work: eval { mySub("hello world"); }
      eval <mystring> doesn't work when I have "" in the sub in the XML. I am unsure if I am not escaping/interpolating the "" correctly.

      My first thought was to try what javafan had and it just does not return anything upon evaluation. I then tried many different things to no avail. If I hard code the direct calls in my Perl script it works either ways.

      almut, thank you for your input. I don't believe prototyping to be an issue here. Don't you think the direct call should also not have worked in that case?

      Thanks again.
        I don't believe prototyping to be an issue here.

        Maybe.  What I meant is probably best expressed with a simple snippet of code, which resembles in essence what I understood you're trying to do (but without the XML and stuff):

        my $code = << 'EOC'; sub mySub() { print "args: @_\n"; } EOC eval $code; print qq(\ncalling 'mySub()'\n); eval 'mySub()'; print "error: $@" if $@; print qq(\ncalling 'mySub("hello world")'\n); eval 'mySub("hello world")'; print "error: $@" if $@;

        Running this gives:

        calling 'mySub()' args: calling 'mySub("hello world")' error: Too many arguments for main::mySub at (eval 3) line 2, near ""h +ello world")

        As you can see, the second call (with arguments) fails with the prototype in place.  And it fails silently, unless you check $@.

Re: eval sub
by almut (Canon) on Oct 09, 2008 at 20:06 UTC

    One thing that seems strange is that you've specified a no-arguments prototype (the empty parentheses in the definition sub mySub() {...}), though you're then trying to call the routine with a parameter ( mySub("hello world") ) ...

    Normally, this would throw a "Too many arguments for..." error, but presumably the error message doesn't show up due to the eval. Have you checked $@?

    Any reason to not just say sub mySub {...} ?

      almut, sorry I skipped the other question. There is no reason to not just say sub mySub {...}. I was just trying a million different things when I couldn't get the eval <string> to work with "". I just left it in there in the test script I posted.