Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

GetOpt::Long usage style

by demerphq (Chancellor)
on Mar 08, 2003 at 11:15 UTC ( [id://241367]=perlmeditation: print w/replies, xml ) Need Help??

Getopt::Long is a basic part of the Perl toolset. However one minor nit that Ive had up to now is that specifying the arguments and their destinations, along with defaults seemed a bit clumsy under strict. Take an example from the documentation

use GetOpt::Long; my $verbose = ''; # option variable with default value (false) my $all = ''; # option variable with default value (false) GetOptions ('verbose' => \$verbose, 'all' => \$all);

To me this is inelegant. I have two "declarations" about a variable in separate locations, one the real declaration along with the default value, and the other the association of the option name (which may be different from the variable name) to the variable. If I want to rename a variable for clarity later on I have to change two items. Now perhaps this is blindingly obvious to the rest of you but I just realized that you can instead write this

use GetOpt::Long; #Adjust bractes and commas to taste GetOptions ( 'verbose' => \(my $verbose = ''), 'all' => \(my $all = ''), );

Which to me seems preferable. Everything is in one place. It says "this variable normally comes from outside", it puts the declaration of the variable next to the declaration of the option handling it is associated with, and it provides a way to document the whole lot in one place if you need to.

Any thoughts?


---
demerphq


Replies are listed 'Best First'.
(jeffa) Re: GetOpt::Long usage style
by jeffa (Bishop) on Mar 08, 2003 at 14:06 UTC
    This kind of redundancy has never really bothered me. I usually use Getopt::Long in conjunction with Pod::Usage and my 'skeleton' usually looks like:
    use Getopt::Long; use Pod::Usage; our ($user,$pass,$numb,$help); GetOptions( 'user|u=s' => \$user, 'pass|p=s' => \$pass, 'numb|n=i' => \$numb, 'help|h|?' => \$help, ); $numb ||= 42; pod2usage(-verbose=>2) if $help; pod2usage(-verbose=>1) unless $user and $pass; __END__
    Considering that the variables used to hold user options are being shared across two (or more) packages, this doesn't seem overly redundant to me. But that's just me. ;)

    UPDATE: hmmm, here is something evil:

    our ($user,$pass,$numb,$help); my @tmpl = ( 'user|u=s', 'pass|p=s', 'numb|n=i', 'help|h|?', ); @tmpl = map {"'$_' => \\\$@{[(split('\|',$_))[0]]},"} @tmpl; eval "GetOptions(@tmpl);";

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
      No eval, complies with strict:
      our ($user,$pass,$numb,$help); my @cmdopt = ( [qw(user u=s)], [qw(pass p=s)], [qw(numb n=i)], [qw(help h ?)], ); GetOptions(map { +(join '|', @$_) => *{$main::{$_->[0]}}{SCALAR} } @cm +dopt);
      It will even bomb out if your variable and option names mismatch:
      Use of uninitialized value in ref-to-glob cast at t.pl line ##.
      Can't use string ("") as a symbol ref while "strict refs" in use at t.pl line ##.
      Of course, this assumes that the variables live in package main. If you need them elsewhere, the $main:: bit will need adjustment. It is possible to generalize even that bit, but that requires some painful twisting - or disabling the ref stricture.

      Makeshifts last the longest.

Re: GetOpt::Long usage style
by robartes (Priest) on Mar 08, 2003 at 11:58 UTC
    Purely from a code clarity and locality point, this is better. It is however a bit difficult to figure out why it works (anything but blindingly obvious, at least to me). Let me try to figure it out for my own good - correct me if I'm wrong:
    my $verbose='';
    That's easy - $verbose becomes an empty scalar. This is also easy:
    (my $verbose='');
    This is an empty list (or better, a list containing that particular piece of emptiness referenced by $verbose.

    And here comes the hairy part:

    \(my $verbose='');
    This is a reference to that particular piece of emptiness pointed to by $verbose, hence in a sense (though strictly speaking not) a reference to $verbose. We're basically pointing to the same scalar thingy as $verbose is pointing to, so when GetOpt::Long uses this reference to stash the option value, we can use $verbose to get at that value. Correct?

    That is a very neat trick, sir! Despite the minor headache in figuring it out (for me anyway), I think I shall be using it from now on. Perhaps you need to figure out a catchy name for it :) ?

    CU
    Robartes-

      \(my $verbose='');

      my returns what has been declared. (Although the behaviour of it as a function is a little interesting, I dont think it can be prototyped, and I can't find complete documentation for it off hand.) The parenthesis required because the precedence of the assignment operator causes

      GetOptions(x=>my $x=1,y=>my $y);

      to be parsed something like

      GetOptions('x',(my $x=1,y=>my $y));

      which is a syntax error. They aren't required when the variable isn't initialized as part of the my. Also I personally think that they add visual calrity when the reference to the variable is taken. Since the my returns the variable declared (as an lvalue) we can take a reference to it with \. The reference operator \ can technically go inside the parens or outside in this case as

      \($x,$y,$z)

      is shorthand for

      (\$x,\$y,\$z)

      Hope that clarifys things. ;-) Er, and yes. You analysed it correctly. :-)

      A couple of related tricks are:

      my $txt="Foo bar"; (my $clean=$txt)=~s/oo/u/g; open my $fh,"File" or die "File $!"; if (my ($x,$y,$z)=/A(..)(..)(..)Z/) { }

      ---
      demerphq


        I know this is ancient, but since it's high on the Google hit list, I wanted to mention one thing...

        This very cool trick does not work for @arrays. :-(

        This:

        use Getopt::Long; GetOptions( 'single=s' => \(my $single = ''), 'multi=s' => \(my @multi = ()), );
        does *NOT* DWIM. According to "perlref":
        As a special case, "\(@foo)" returns a list of references to the contents of @foo, not a reference to @foo itself.
        I tried all kinds of permutations, and the best I could come up with was:
        use Getopt::Long; GetOptions( 'single=s' => \(my $single = ''), 'multi=s' => do { use vars '@multi'; \@multi }, );
        which, isn't quite as clean, IMHO.

        (It *DOES*, however, keep the variable declarations and command-line options on the same line, at least, so you don't have to make changes in multiple places when adding new options. *shrug*)

      This is also easy:
      (my $verbose='');
      This is an empty list (or better, a list containing that particular piece of emptiness referenced by $verbose.

      You might call it easy, but it's wrong. Twice. First of all, parenthesis do NOT make lists. Context makes lists. Parenthesis are used for precedence.
      Furthermore, if the context would demand a list (which in this case it does), it's not an empty list. How could it be? There's a scalar inside it. It's a one element list.

      Abigail

        Goes to show again that I feel perfectly comfortable with my foot in my mouth. Thanks for the heads up, demerpq, zby and Abigail-II!

        CU
        Robartes-

      I have not a Perl reference at hand now but I think that \($something) is the same as (\$something). That is the \ operator for a list returns a list of references not a reference to a list.

      UPDATEOK - writing about no having a reference on a web site was a bit lame.

Re: GetOpt::Long usage style
by mattriff (Chaplain) on Mar 08, 2003 at 18:41 UTC
    In the spirit of TIMTOWTDI...

    I tend to just use a hash and be done with it:

    my %opt; GetOptions(\%opt,'verbose','all');

    Sure, $verbose is a few less characters than $opt{verbose}, but for me it's a nice way to separate the data that came from outside my script.

    - Matt Riffle

      Yeah I do use this style as well. However it still presents duplication when you want to provide defaults or multiple names or types for options (which rules out a quick key %opts), in fact its worse because you dont have compile time var name checking of the var names if they get out of synch. In other words the connection between destination and source becomes too loose and it becomes really easy to make a subtle error

      my %opt={verbose=>0,all=>0); GetOptions(\%opt,'talkative|verbose','everything|all');

      So what happens if we werent careful and do it like

      GetOptions(\%opt,'talkative|verbose','all|everything');

      Oops, now the --all options goes to the wrong place.

      Anyway, I still would use the %hash style as well for some situations, and as the interface to Getopt::Long is so flexible you can mix the two:

      GetOptions(\%opts,'Debug=i'=>\(my $Debug=0),'verbose','all');

      And then theres also using subs for the options... Mmm fun!


      ---
      demerphq


        Personally, I tend to keep thingsl ike synonyms in a data structure of their own. So, I might do something like:
        my %options = ( verbose => 0, everything => 1, ); my %synonyms = ( verbose => [ qw( talkative )], everything => [ qw( all )], ); GetOptions( \%options, ( map { join\('|', $_, @{ $synonyms{$_} || []}) } keys %options ), );
        This way you can, at a glance, see exactly what your synonyms are, laid out nice as can be. A similar thing can be done with the "=i", "=s", etc.

        ------
        We are the carpenters and bricklayers of the Information Age.

        Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

        Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

Re: GetOpt::Long usage style
by Abigail-II (Bishop) on Mar 08, 2003 at 23:11 UTC
    Assuming that you actually use the variables you can set with a command line switch, if you rename one of those variables, you'd need to change the variable at least twice, even with style you are discussing.

    I've used that style for years (but not always), but not for the reason you mention. Renaming a variable more than once requires only two keystrokes per extra occurrence in an editor like vi (n.), so that expect doesn't bother me at all. Of course, that's assuming :g/$oldvar/s//$newvar/g wouldn't do the trick, in which case there wouldn't be any extra keystrokes.

    Abigail

      I suppose tis amatter of coding style, but often the process that occurs is like this: I know I need to handle options, x, y, z and that I want them have defaults. I write the Getoptions code, and then start working on the code that uses those options. If during that process I realize that the names i have chosen for the vars dont have the right names, I change them then and there, once finished the code that uses the vars, (and settling on the right names) I go back and adjust afterwards. Why not search and replace? Usually these vars are lexical for me, and usually I dont want to change every instance at once, yes I could hover on the search/replace button, changing the appropriate ones, but often I dont bother, its less effort to go to the one place in my code that I know is relevent and make he changes by hand, and quickly.

      Having said that your suggestions are welcome, but even more welcome would be an explanation of the motivations you had for using this style, and the motivations for not chosing it as well.

      Cheers,


      ---
      demerphq


Re: GetOpt::Long usage style
by ihb (Deacon) on Mar 08, 2003 at 19:57 UTC

    The one thing I don't like about it is that there are variable declarations that aren't at BOL. (There are exceptions to my own rule here. if (my $foo = bar()) { ... } and foreach my $foo (...) { ... } are such. They're special and scope related.)

    I want variable declarations to be at BOL because then I easily find them when I need to, like when moving code out to a subroutine. It's also easier to realize what scope my change will be effective in when patching.

    But it's quite possible that GetOptions make you realize that you have variable declarations in it, and then it's not an issue (until someone like me wants to patch it ;)).

    Besides that; nice touch.

    ihb
Re: GetOpt::Long usage style
by pg (Canon) on Mar 08, 2003 at 18:28 UTC
    This is a very nice style, and I will start to use it.

    The main point why it works is that, Perl allows you to my variables on fly (you only need to declare it when you need it), and it would be valid all the way to the end of the scope in which it is declared. (In this particular case, we don't see a scope change)

Re: GetOpt::Long usage style
by hangareighteen (Monk) on Mar 09, 2003 at 17:23 UTC
    I've always felt that gathering up options for use in any program was rather inelegant anyways. While it's nice to wrap up declaration, default value, and paramater value into one convenient statement it seems to pass over the structure of the problem.

    ## this is a poorly written demonstrative example use GetOpt::Long; my $args = { verbose => 0, # default false all => '', # default undef skip => [], # default empty }; GetOptions( 'verbose|v' => sub { \$args->{verbose} += 1 }, 'debug|d=i' => \$args->{verbose}, 'all' => \$args->{all}, 'skip|s=s' => sub { push(@{$args->{skip}}, $_[1]) } ); # get a default from the environment. $args->{all} = $ENV{FOO_DEFAULT_ALL} unless (defined($args->{all})); # get a default from a parsed configuration file. $args->{all} = $rcfile->{foo_default}->{all} unless (defined($args->{a +ll})); # get defaults from some arbitrary source. get_foo_defaults($args);

    I like to have all my arguments grouped in one place. In other words, I really hate using global variables, especially for something like this. Typically, I'll use a hash reference for convenience. This allows me to group the arguments, pass them around (and change) them easily, and define my 'default' values all in once place. Plus I have once place to look to see what paramaters I expect to keep track of, and one variable to Dump if necessary.

    Most of the time I'm gathering paramaters from other sources as well. Such as a configuration file, or environment variables, or sometimes even network sources. So there tends to be more than one place where these values are getting 'filled-in' anyways, and I'm not always starting with the command line options. So it seems wasteful to try and make everything line up under a GetOptions call, especially if you intend to change anything down the line.

    Occasionally I enjoy using the subroutine reference 'mode' of GetOpt to do simple things like incrementing a value depeding on how many times it appears on the command line, or doing simple paramater validation before it gets stored to a variable, or even accepting multiple values for a single paramater. These are really handy sometimes, and lining up all my declarations under the GetOptions call simply dosen't work -- I'll need to predeclare a variable anyways.

    I also tend to have a few options which modify the same variable anyways, which also keeps you from getting away with a 1:1 mapping. I like to let the user set debugging output specifically: '-d 3', as well as incrementally: '-v -v -v'. So predeclaring my paramater values here is also convenient.

    Also, in my own preference, I don't care wether or not the named paramater 'verbose' actually modifies a variable named 'verbose', it could well be named 'debug' or 'output_level' or any other name that makes sence for the context it's being used in. What matters is, the rest of my program looks for a variable named 'verbose'; the name of the paramater is really kind of irrelevant -- and is only used once in once place, whereas the value is used in multiple places.

    It all comes down to personal preference, and necessity, I guess. And my preferences and needs have resulted in my using a structure similar to the above almost exclusively. That's just me, though.

      how to set variables to default values...if user doesn`t specifies using Getopt::Long???
Re: GetOpt::Long usage style
by hsmyers (Canon) on Mar 09, 2003 at 08:05 UTC
    Under the general heading of 'ever so slightly embarrassing' I would admit to having to stick opt_ as a prefix to my GetOpt::Long variables---I attribute this to too many years as a machine code morlock (i.e. too many globals) That aside I like your reasoning and results, will adopt forthwith!!

    --hsm

    "Never try to teach a pig to sing...it wastes your time and it annoys the pig."

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others chanting in the Monastery: (5)
As of 2024-03-19 06:25 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found