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

In my program, the user may enter various types of information (integer, numeric, dates, time, strings, IP addresses, etc.). The program reads from a configuration file what type of information is needed, and then verifies that the user entered the correct type of value.

Originally, I had a series of if/elsif statements to check the type of entry against a particular subroutine. Something like this:

if ($type == "INTEGER") { $convAnswer = checkInteger($answer); } elsif ($type eq "DATE") { $convAnswer = checkDate($answer); } elsif ($type eq "IPADDRESS") { $convAnswer = checkIpaddress($answer); } elsif ($type eq "FOO") { $convAnswer = checkFoo($answer); } else { die qq(There is no such type as "$type"\n); }
This worked, but it created really long bits of code. Plus, if I created a new "type", I would have to go into my code, and change the if/elsif statements. Therefore, I am attempting to switch to this:

my $sub = "check" . ucfirst(lc($type)); if (not defined (&{$sub}) { die qq(There is no such type as "$type"\n); } $convAnswer = &{$sub}($answer);
The good news is that this makes my program easier to maintain and understand because I've eliminated dozens of lines of testing data types and seeing which routine is needed. To add a new variable type, I simply have to define a new routine.

The bad news is that this fails if I have "strict 'refs'" invoked. I could do this:

my $sub = "check" . ucfirst(lc($type)); if (not defined (&{$sub}) { die qq(There is no such type as "$type"\n); } no strict "refs"; $convAnswer = &{$sub}($answer); use strict "refs"
And that works, but I feel there should be a better way without turning off strict "refs".

Is there a better way of doing this? I was thinking of putting the check inside an eval block and then seeing if the eval succeeds or fails. However, I think this makes the code even more difficult to understand, so I would prefer to be able to use subroutine references if possible.

Replies are listed 'Best First'.
Re: Trying to get around the "use strict 'refs'" Issue
by derby (Abbot) on May 07, 2007 at 17:25 UTC

    Instead of using symbolic references, use a dispatch table.

    my $table = { INTEGER => \&checkInteger, DATE => \&checkDate, IPADDRESS => \&checkIpaddress, FOO => \&checkFoo }; if( defined $table->{uc $type} ) { $table->{uc $type}( $answer ); }
    (untested)

    -derby

    update: added untested code snippet

Re: Trying to get around the "use strict 'refs'" Issue
by FunkyMonk (Bishop) on May 07, 2007 at 17:38 UTC
    This is a job for a dispatch table...

    #tested my %dispatch = ( INTEGER => \&checkInteger, DATE => \&checkDate, IPADDRESS => \&checkIpaddress, FOO => \&checkFoo ); my $type = "INTEGER"; my $answer = "44"; die "can't do $type" unless exists $dispatch{$type}; my $convAnswer = $dispatch{$type}->($answer); sub checkInteger { print "int" } sub checkDate { print "date" } sub checkIpaddress { print "ip" } sub checkFoo { print "foo" }

    If you insist on using your no strict "refs" method it's only a matter of time before someone enters deleteAllFiles as the data type.

    Edit: Damn. You're all too fast. Or is it that I'm too Slow?

      If you insist on using your no strict "refs" method it's only a matter of time before someone enters deleteAllFiles as the data type.
      Those are two different matters, and yet can be related. Strictures is about enforcing dicipline on variable declarations and/or explicit namespace usage, hard references, and bareword misplace/misuse. But use strict; gives us no guarantee about malicious user input prevention. We need another step for that. And this step is independent of strict.

      Nevertheless, for me, strict and warnings are just too valuable to be put aside. I wouldn't go and code without them, even if I were at the same level as merlyn, except probably in one-liners.

      Damn. You're all too fast. Or is it that I'm too Slow?
      Don't worry, it's not about speed* in aswering :-) But rather, IMO, it's about correctness, accuracy, and relevancy**, well, as far as "the right question" is concerned. I personally consider your effort has met the three.

      * Of course, answering a question with the same solution as other previous answers in the same thread after a significant time potentially invites something bad.

      ** I was thinking about something regarding my own "bads" but couldn't recall, and I said this to remind myself. Now I remember two of them, hence this second note


      Update: Added the second footnote

      Open source softwares? Share and enjoy. Make profit from them if you can. Yet, share and enjoy!

      Seems like a dispatch table is the way to go.

      To use the dispatch table, I'll have to modify both the table and add the subroutine instead of just adding a new routine to my program

      However, as you pointed out, what if someone defined a nefarious routine? My program would simply do it's little check, notice the "checkCauseMayhem" and go on its merry way.

        However, as you pointed out, what if someone defined a nefarious routine?

        If someone malicious has access to your computer so as to be able to modify the code of any program you deem important, you have greater worries than said person modifying the code of any program you deem important.

        However, as you pointed out, what if someone defined a nefarious routine? My program would simply do it's little check, notice the "checkCauseMayhem" and go on its merry way.

        That's just the same with your previous approach. The only difference being that in one case you use a generic hash and make it specialized for this task. In the other one, a very specific one -the stash-, designed to do something completely different.

Re: Trying to get around the "use strict 'refs'" Issue
by thezip (Vicar) on May 07, 2007 at 17:26 UTC

    Have you considered using a dispatch table?

    This link regarding dispatch tables should get you started...


    Where do you want *them* to go today?
Re: Trying to get around the "use strict 'refs'" Issue
by GrandFather (Saint) on May 07, 2007 at 22:06 UTC

    If you "objectify" your code then you can:

    my $sub = 'handle_' . $type; die qq(There is no such type as "$type"\n) unless $self->can ($sub); $self->$sub (); ... sub handle_integer { ... }

    DWIM is Perl's answer to Gödel