Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses
 
PerlMonks  

Subroutine Prototyping/Subroutine Argument Parsing

by Missing Words (Scribe)
on Jun 08, 2003 at 05:32 UTC ( [id://264071]=perlquestion: print w/replies, xml ) Need Help??

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

Hello Monks,

I am currently modifing a subroutine(later to be added to a module) which I use often. The subroutine's name is masked_read. I have prototyped it to take two optional arguments, one being a scalar variable, and the other being any scalar like so: sub masked_read(;\$$). However, I have two questions:

  • How can I modify the code to accept the second argument if the function is being used as a rvalue: $password = &masked_read
  • Is there a better way to ensure that the second scalar value is only one character?
Read more for my current code and a more detailed description of what I am trying to accomplish:
The following is a function with some code so it can be used if neccessary:
use Term::ReadKey; sub masked_read(;\$$) { my $mask; if($_[1]){ if(length $_[1] > 1){ die "arg 2 of masked_read() should only be 1 character long"; } $mask = $_[1]; } else{ $mask="*"; } my $key; $|=1; while(1) { while(not defined($key = ReadKey(-1))) {}; if(ord($key) != 13) { if(ord($key) == 8) { print "\b \b"; chop(${$_[0]}); } else { ${$_[0]} .= $key; print "$mask"; } } else {last} } $|=0; return ${$_[0]}; } print "Enter:"; my $password = masked_read; print "\npassword:$password";

The first Question:

Currently, when called two arguments are passed. The function can be called in any of the following ways:
masked_read($password, $mask); masked_read($password, '#'); masked_read($password); my $password = masked_read();

However, I would like the user to also have the following option: $password = masked_read('#')

I have already considered simply switching the order of the two parameters; however, that leads to syntax such as: masked_read('#',$password) . Which is counterintuitive and creates the problem of getting the scalar variable when the first value is not present.

I have also thought of creating a if-then structure to determine what the user has passed, but cannot think of or find a way to determine whether something is a variable or not.

This issue is the one that has stumped me, and any direction at all would be helpful.
Second Question:

As you can see I am currently using the length function to catch arguments in $_[1] that is more than one character. Right now this is needed because the method I use to mask the password on the screen does not work with a mask of more than one character if the person enters a backspace. This works fine, however I was wondering if there was a more efficient way of doing this. I looked through most of the information I have on prototyping and I did not see any way to accomplish it through prototyping means. If anyone knows of an alternative, it would be very helpful.


I realize this subroutine is pretty trite however I am doing this mostly as a means to learn about prototyping, subroutines, and later, making modules.

Thanks in advance.
Missing Words.

Replies are listed 'Best First'.
Re: Subroutine Prototyping/Subroutine Argument Parsing
by BrowserUk (Patriarch) on Jun 08, 2003 at 07:12 UTC

    A purely personal view, but I think the main problem is that you are over specifying the subroutine.

    • What is the benefit in allowing the sub to be passed a reference to a scalar for it to 'fill-in'?

      The 'obvious' way (IMO...YMMV...yada yada yada:), to use this routine, is as a function. Ie. In either of the following ways:

      • my $password = masked_input();
      • my $password = masked_input( $mask );

      I cannot envisage the utility if calling it as a subroutine.

      masked_input( \my $password, $mask );

    • Why do you feel the need to verify that the mask parameter is a single character?

      If the mask argument is present, simple use substr( $mask, 0, 1 ) as your mask character and ignore anything extra.

    I'm also not adverse to using perl's prototypes when I feel that they benefit my code, but in this case, I don't see the benefit. If you haven't already read FMTEYEWtKaPiP, then I highly recommend it. If you still want to use a prototype for this after doing so, then I'd probably suggest a simple (;$) at most, but even that is overkill I think.

    Your major problem/concern seems to be that you want to allow for multiple, omitable arguments. As I already said, I don't think this case warrents it, but you have two (probably more, but these come to mind) options, if you don't use a prototype,.

    If you wish to specify the ordering of the arguments, and allow the user to supply the second argument whilst omitting the first, he can do so by using undef

    eg. my $pw = masked_input undef, '#';.

    The second option would be to use named parameters:

    masked_input( ref => \my $pw, mask => '#' ); my $pw = masked_input( mask => '@' ); my $pw = masked_input();

    Again, I think this is overkill for this application, but for some purposes, this can be a very useful way to do things.


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller


Re: Subroutine Prototyping/Subroutine Argument Parsing
by sauoq (Abbot) on Jun 08, 2003 at 08:31 UTC
    I am doing this mostly as a means to learn about prototyping, subroutines, and later, making modules.

    The absolutely most important thing to learn about Perl's prototypes is that in most cases — almost all cases, really — they should not be used at all. They are not prototypes by any definition that you may be expecting if you came to Perl from another language.

    Their primary reason for existing at all is to allow us to write subs that have the same semantics as some builtins. The ability to prototype a sub that takes a BLOCK can be used to extend the language syntax, but that's generally not a good idea anyway. An empty prototype can be a hint that the sub might be inlinable, which is nice and all but usually it's almost meaningless in terms of real performance.

    Not only do Perl's prototypes fail to do what you expect, but they fail to do it in ways that can be real easy to trip over. If you haven't already read the link that BrowserUk gave you, read it now. In fact, even if you have already read it, read it again. And avoid prototypes until you really understand the issues involved. You'll be saving yourself headaches in the long run.

    Update: Erk! Changed to reflect what I meant... I do not have some absurd prejudice against Perl's subroutines. :-) Thanks BrowserUk!

    -sauoq
    "My two cents aren't worth a dime.";
    
Re: Subroutine Prototyping/Subroutine Argument Parsing
by broquaint (Abbot) on Jun 08, 2003 at 10:28 UTC
    Further to what BrowserUk and sauoq have said, prototypes are ignored by methods e.g
    sub foo($) { print "got @_\n"; } my @args = qw/ more than one arg /; ## class method call main->foo(@args); ## standard sub call foo(@args); __output__ got main more than one arg got 4
    This behaviour can be attributed to the fact that exact method to be called cannot be determined until runtime.
    HTH

    _________
    broquaint

Re: Subroutine Prototyping/Subroutine Argument Parsing
by Missing Words (Scribe) on Jun 08, 2003 at 14:25 UTC
    Thank You all very much.

    I read the link that BrowserUkand others suggested. After reading it I have taken all of your advice and decided not to use prototypes.

    I originally put them in so the user of the function would not have to add '\'s before the variable if calling the subroutine like so: masked_read($password) and to ensure the fisrt value was a valid lvalue; However, after thinking about it I have decided that BrowserUk is correct and that the preceding usage would almost never be used (by myself or any other unfortunate soul who is using my function). Now, the only argument which can be passed is the mask character, which is parsed by the method that BrowserUk suggested.

    Thanks to all of you for your help. I actually ended up learning more about prototypes by not using them.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://264071]
Approved by hossman
Front-paged by data64
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others admiring the Monastery: (7)
As of 2024-04-19 14:44 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found