Click here to Skip to main content
Click here to Skip to main content

Full-featured Automatic Argument Parser

By , 5 Nov 2003
 

Contents

Introduction

What? Yet another argument parsing class?! What for? Well, actually for a couple of reasons:

  • Supports many argument types:
    • Switches (eg. /foo, /foo=1, /foo=true [localized])
    • Named/unnamed flags (eg. -raFl, -aABCDEF). Flags are "type-safe" in the sense that you specify which values are accepted. For example, if the user writes -raFlW and 'W' is not an accepted flag, then an ArgumentFormatException is raised.
    • Named/unnamed values (eg. /foo=bar, "foo")
    • Any prefix(es) you specify (eg. -foo, /foo, \foo, ...)
    • Any assignment symbol(s) (eg. /foo=bar, /foo:bar, ...)
    • Any additional or overriding pattern you provide
  • Automatically sets field/property values according to arguments provided using custom attributes.
    • All value types are supported, including enumerations.
    • You can specify a single member, a class, a type (for static members) or an assembly.
    • One argument can set many members at once, even if located in different types.
  • Supports globalization through custom attributes (ie you provide a ResourceManager, a CultureInfo and a resource ID and the argument name/alias will be automatically updated according to resource file). This works for switches, named values and flags.
  • Keeps track of handled and unhandled arguments.
  • All that in less than 400 lines of code. :)

I want to thank Ray Hayes for his idea of automatically setting field/property by judiciously using custom attributes. This is a great example of that proverbial 1% of inspiration. :) Take a look at his article here.

Quick Example

This is an example quickly showing how you might use the class, more or less taken from the included demo, which by the way is a utility I posted some time ago on this site. You can read the article here.

using Common;
using Common.IO;
using System;
using System.Collections;
using System.Collections.Specialized;
using System.IO;

namespace xmove
{
    class XMove
    {
        private static System.Resources.ResourceManager m_resMan;

        // this argument can either be named "batch", "file", "batchFile"
        // or even "smurf" if you fancy it
        [AutoSetMember("batch", "file", "batchFile", "smurf")]
        private static String m_batchFile;

        [AutoSetMember("yc", SwitchMeansFalse=true)]
        private static bool m_confirmMove = true;

        [AutoSetMember("yc", SwitchMeansFalse=true)]
        private static bool m_confirmOverwrite = true;
        
        [AutoSetMember("r")]
        private static bool m_recursive = false;
        
        private static FindFile.SearchAttributes m_searchAttributes;
        
        [STAThread]
        static void Main(string[] args)
        {
            // a FlagCollection contains all accepted flags
            FlagCollection flags = new FlagCollection();
            
            // here we add the flag named "a" with accepted values
            // a,c,d,h,...
            // the argument must then looks like "-a<VALUES>" on the command
            // line
            flags.Add("a", "acdehnorstFA");
            
            // create a new parser that accept all argument formats,
            // is case sensitive and accept provided flags
            ArgumentParser parser = new ArgumentParser(ArgumentFormats.All,
                false, flags);
            
            // parse the arguments with the result stored in a
            // StringDictionary
            StringDictionary theArgs = parser.Parse(args);

            // automatically set all static members of the class XMove
            parser.AutoSetMembers(typeof(XMove));
            
            // now, the values of m_batchFile, m_confirmMove,
            // m_confirmOverwrite and m_recursive are all set !
            
            // the corresponding arguments have been moved to
            // parser.HandledArguments
            // the remaining arguments are in parser.UnhandledArguments
            
            // one argument remains which can't be set automatically
            if (theArgs.ContainsKey("a"))
            {
                String attribs = theArgs[m_resMan.GetString("app0019")];
                
                if (attribs.IndexOf("A") > -1)
                    m_searchAttributes |= FindFile.SearchAttributes.All;

                if (attribs.IndexOf("F") > -1)
                    m_searchAttributes |= FindFile.SearchAttributes.AnyFile;
                
                .........
            }
            
            
            // here we load the resources for the application
            LoadResources();
            
            // we then set the resource manager of the
            // AutoSetMemberAttribute accordingly
            // why this property is static is explained in the
            // "How it Works" section of this article
            AutoSetMemberAttribute.Resources = m_resMan;

            // create a new instance of Dummy class
            Dummy dummy = new Dummy();
            
            // then set members of this class, no matter if they are
            // static, instance, public, private, protected, internal
            
            // type conversion is done automatically !
            parser.AutoSetMembers(dummy);
        }
        
        private void LoadResources()
        {
            ....
        }
    }
    
    class Dummy
    {
        public enum MyEnum
        {
            a,
            b,
            c,
            d
        }
        
        [AutoSetMember("foo")]
        private static int field1;
        
        // here we indicate that the argument name is to be retrieved using
        // the ResourceManager at runtime
        [AutoSetMember(ResID="0001")]
        private Double field2;
        
        // same here
        [AutoSetMember(ResID="0002")]
        protected String Property1
        {
            get {...}
            set {...}
        }
        
        [AutoSetMember("buzz")]
        public MyEnum Property2
        {
            get {...}
            set {...}
        }
    }
}

Using the code

Two main classes do all the work: ArgumentParser and AutoSetMemberAttribute. As usual, contructors let you directly set one or more properties, so I will skip them.

ArgumentParser class

Properties

public char[] AllowedPrefixes [get, set]
The accepted prefix(es).

public ArgumentFormats ArgumentFormats [get, set]
The accepted argument format(s).

public char[] AssignSymbols [get, set]
The accepted assignation symbol(s).

public string CustomPattern [get, set]
An additional or overriding pattern. In the pattern, use capture name constants made public by this class (ArgumentNameCaptureName, ArgumentValueCaptureName, FlagNameCaptureName, FlagsCaptureName and PrefixCaptureName).

public StringDictionary HandledArguments [get]
The argument(s) that have been automatically set by AutoSetMembers method.

public StringDictionary UnhandledArguments [get]
The argument(s) that have not been automatically set by AutoSetMembers method.

public bool UseOnlyCustomPattern [get, set]
Indicates if the custom pattern provided is overriding the internal pattern automatically generated.

Methods

public StringDictionary Parse(string[] args)
Parses the array of arguments and returns the dictionary of parsed arguments
UnhandledArgument property is also updated accordingly.

public void AutoSetMembers(Assembly assembly)
public void AutoSetMembers(Type type)
public void AutoSetMembers(object instance)
public void AutoSetMembers(object classToProcess, MemberInfo member)
Automatically sets member(s) of the provided assembly, type, class instance. Also works for a single field/property.

public void Clear()
Clears all saved arguments (both handled and unhandled).

private void BuildPattern()
Builds the pattern to be used when parsing each argument.

private void SetMemberValue(object instance, MemberInfo memberInfo, object value)
Set the static or instance member to the specified value.

AutoSetMemberAttribute

Properties

public static CultureInfo Culture [get, set]
The culture to be used for retrieving culture aware aliases.

public static ResourceManager Resources [get, set]
The resource manager to be used for retrieving culture aware aliases.

public ArrayList Aliases [get]
Command line argument's name or aliases if many names are possible.

public string Description [get, set]
The description of the command line argument.

public object ID [get, set]
An ID (can be anything you want).

public string ResID [get, set]
The resource ID to be used when retrieving culture aware aliases.

public bool SwitchMeansFalse [get, set]
Indicates if the meaning of a switch is false instead of true as usual.

How it Works

Parsing of the command line

Here is a quick explanation of the regex that is constructed by ArgumentParser.BuildPattern() private method. Variables are indicated by capitalized names and are to be replaced at runtime.

// The whole parsing string (with all possible argument formats) :
// ---------------------------------------------------------------
// (CUSTOM_PATTERN)
// |(^(?<PREFIX>[PREFIXES])(?<FLAGNAME>)FLAG_NAMES)(?<FLAGS>[FlagsCaptureName]+)$)
// |(^(?<PREFIX>[PREFIXES])(?<NAME>[^EQUAL_SYMBOLS]+)([EQUAL_SYMBOLS](?<VALUE>.+))?$)
// |(LITERAL_STRING_SYMBOL?(?<VALUE>.*))
//
// Again, but commented :
// ----------------------
// (CUSTOM_PATTERN)|        # custom pattern, if any (it has priority over
//                          # standard pattern)
//
// foreach flag in FlagCollection :
//
// (^
// (?<PREFIX>[PREFIXES])            # mandatory prefix
// (?<FLAGNAME>)FLAG_NAMES)       # flag name
// (?<FLAGS>[FlagsCaptureName]+)  # flag value
// $)|
//
// (^
// (?<PREFIX>[PREFIXES])            # mandatory prefix
// (?<NAME>[^EQUAL_SYMBOLS]+)     # argument name (which includes flag
//                                # name/values)
// ([EQUAL_SYMBOLS](?<VALUE>.+))? # argument value, if any
// $)
//
// |(
// LITERAL_STRING_SYMBOL?   # optional @ caracter indicating literal string
// (?<VALUE>.*)             # standalone value (will be given an index when
//                          # parsed in Parse() method)
// )

Automatically setting member values

At design time, AutoSetMemberAttribute custom attribute is used to specify which argument will be used to set the affected member. Other informations include description, resource ID and an ID that you might use at your convenience.

At runtime, you can set the AutoSetMemberAtttribute static properties Resource and Culture. It's necessary to make those properties static because attributes are serialized at compile time, so they can only have constant values as instance properties.

When you execute ArgumentParser.SetAutoMembers(...), the following occurs :

  1. AutoSetMember attributes will be located using reflection.
  2. If a resource ID is specified and a resource manager and a culture are provided, the localized argument name will be added as an alias.
  3. The argument associated to the member (if any) will be converted to the member's type
  4. The member's value will be updated using ArgumentParser.SetMemberValue(...).
  5. The argument will be removed from ArgumentParser.UnhandledArguments and added to ArgumentParser.HandledArguments.
/// <SUMMARY>
/// Automatically sets members for the provided
/// <SEE cref="System.Reflection.Assembly" />.
/// </SUMMARY>
/// <PARAM name="assembly">The <SEE cref="System.Reflection.Assembly" /> to process.</PARAM>
public void AutoSetMembers(Assembly assembly)
{
    Type[] types = assembly.GetTypes();

    foreach (Type type in types)
        AutoSetMembers(type);
}

/// <SUMMARY>
/// Automatically sets members for the provided <SEE cref="System.Type" />.
/// </SUMMARY>
/// <PARAM name="type">The <SEE cref="System.Type" /> to process.</PARAM>
/// <REMARKS>Only static members will be processed.</REMARKS>
public void AutoSetMembers(Type type)
{
    MemberInfo[] members = type.FindMembers(
        AutoSetMemberAttribute.SupportedMemberTypes,
        AutoSetMemberAttribute.SupportedBindingFlags, Type.FilterName, "*");

    foreach (MemberInfo member in members)
        AutoSetMembers(type, member);
}

/// <SUMMARY>
/// Automatically sets members for the provided class instance.
/// </SUMMARY>
/// <PARAM name="instance">The class instance to process. Must not be null.</PARAM>
/// <REMARKS>Both static and instance members will be processed.</REMARKS>
public void AutoSetMembers(object instance)
{
    MemberInfo[] members = instance.GetType().FindMembers(
        AutoSetMemberAttribute.SupportedMemberTypes,
        AutoSetMemberAttribute.SupportedBindingFlags, Type.FilterName, "*");

    foreach (MemberInfo member in members)
        AutoSetMembers(instance, member);
}

/// <SUMMARY>
/// Automatically sets member of the provided class instance or
/// <SEE cref="System.Type" />.
/// </SUMMARY>
/// <PARAM name="classToProcess">The class instance or <SEE cref="System.Type" /> to process.</PARAM>
/// <PARAM name="member">The member which will be set. Must be a field or a property.</PARAM>
/// <REMARKS>Both static and instance members are accepted.</REMARKS>
public void AutoSetMembers(object classToProcess, MemberInfo member)
{
    AutoSetMemberAttribute attrib = Attribute.GetCustomAttribute(member,
        typeof(AutoSetMemberAttribute)) as AutoSetMemberAttribute;

    if (attrib != null)
    {
        if (attrib.ResID != null && AutoSetMemberAttribute.Resources != null)
            attrib.Aliases.Add(AutoSetMemberAttribute.Resources.GetString(
                attrib.ResID, AutoSetMemberAttribute.Culture));

        String argValue = null;
        bool found = false;

        foreach (String alias in attrib.Aliases)
        {
            if (m_unhandled.ContainsKey(alias))
            {
                argValue = (String)m_unhandled[alias];

                m_handled.Add(alias, argValue);
                m_unhandled.Remove(alias);

                found = true;
                break;
            }
            else if (m_handled.ContainsKey(alias))
            {
                argValue = (String)m_handled[alias];

                found = true;
                break;
            }
        }

        if (found)
        {
            Type memberType = null;

            switch (member.MemberType)
            {
                case MemberTypes.Property:
                    memberType = ((PropertyInfo)member).PropertyType;
                    break;

                case MemberTypes.Field:
                    memberType = ((FieldInfo)member).FieldType;
                    break;
            }

            if (memberType == typeof(bool))
            {
                if (argValue == "")
                    SetMemberValue(classToProcess, member,
                        !attrib.SwitchMeansFalse);
                else if (argValue == Boolean.FalseString ||
                    argValue == Boolean.TrueString)
                    SetMemberValue(classToProcess, member,
                        Boolean.Parse(argValue));
                else
                    // last chance ... if can't parse it as integer, an
                    // exception will be raised
                    SetMemberValue(classToProcess, member,
                        Int32.Parse(argValue) != 0);
            }
            else if (memberType == typeof(String))
                SetMemberValue(classToProcess, member, argValue);
            else if (memberType.IsEnum)
            {
                object value = Enum.Parse(memberType, argValue,
                    m_ignoreCase);
                SetMemberValue(classToProcess, member, value);
            }
            else if (memberType.IsValueType)
                SetMemberValue(classToProcess, member,
                    Convert.ChangeType(argValue, memberType));
        }
    }
}

/// <SUMMARY>
/// Sets the static or instance member (property or field) to the specified
/// value.
/// </SUMMARY>
/// <PARAM name="instance">The class instance or <SEE cref="System.Type" /> to be used.</PARAM>
/// <PARAM name="memberInfo">The member to be set.</PARAM>
/// <PARAM name="value">The new value of the member.</PARAM>
private void SetMemberValue(object instance, MemberInfo memberInfo,
    object value)
{
    if (memberInfo is PropertyInfo)
    {
        PropertyInfo pi = (PropertyInfo) memberInfo;

        if (pi.CanWrite)
        {
            MethodInfo methodInfo = pi.GetSetMethod(true);

            BindingFlags bindingFlags = BindingFlags.SetProperty;

            if (methodInfo.IsStatic)
                bindingFlags |= BindingFlags.Static;

            pi.SetValue(instance, value, bindingFlags, null, null, null);
        }
    }
    else if (memberInfo is FieldInfo)
    {
        FieldInfo fi = (FieldInfo) memberInfo;

        BindingFlags bindingFlags = BindingFlags.SetField;

        if (fi.IsStatic)
            bindingFlags |= BindingFlags.Static;

        fi.SetValue(instance, value, bindingFlags, null, null);
    }
}

License

This article, along with any associated source code and files, is licensed under The MIT License

About the Author

Sebastien Lorion
Architect
Canada Canada
Member
Sébastien Lorion is software architect as day job.
 
He is also a musician, actually singing outside the shower Smile | :)
 
He needs constant mental and emotional stimulation, so all of this might change someday ...

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Generalhiiiiiiiiiigroupglory44415 Apr '11 - 8:58 
Hello ,
How are you today ? .
I hope you are fine and all is well with you, My name is miss glory i saw your profile at codeproject.com which really interest me and i decide to communicate with you, and it will please me if you will be my friend, Here is my direct email address (glory_west10@yahoo.in),so that i will tell you more about me and my picture hope to hear from you
Glory.
( glory_west10@yahoo.in)
QuestionHow get a regular parameter rather than an option?memberDaveKolb2 Apr '09 - 20:00 
If I run a command like "mycommand somearg" with no option switches, then I get zero handled or unhandled arguments, yet if I add an option switch, then the "somearg" becomes one of the unhandled arguments. How am I supposed to fetch non option arguments?
 
For instance if were to run "mycommand /sw1 /sw1 arg1 arg2 /sw3=value", how should I fetch arg1 and arg2 that are not option switches and not values of an option switch?
 
Thanks, Dave
 
Dave Kolb
http://dotnetcodeslingers.com

AnswerRe: How get a regular parameter rather than an option?memberSebastien Lorion3 Apr '09 - 5:27 
Here's how:
 
var parser = new ArgumentParser(...);
StringDictionary theArgs = parser.Parse(args);
 
string arg1 = theArgs["0"];
string arg2 = theArgs["1"];

 
Sébastien
 


Intelligence shared is intelligence squared.
 
Homepage : http://www.sebastienlorion.com

GeneralRe: How get a regular parameter rather than an option?memberDaveKolb3 Apr '09 - 8:31 
Thanks Sebastian. I had thought that calling "parser.Parse" was optional and only needed if I wanted a StringDictionary and that if I called "parser.AutoSetMembers" that was sufficient.
 
Also, I found that the "parser = new ArgumentParser(ArgumentFormats.All, true)" form of the constructor failed with a null reference to flags and I had to use the form that included a flags argument even though it was an empty collection. Is that a minor bug?
 
Nice class btw!
 
Thanks much, Dave
 
Dave Kolb
http://dotnetcodeslingers.com

AnswerRe: How get a regular parameter rather than an option?memberSebastien Lorion3 Apr '09 - 9:06 
Well, I have not done it before, but my guess would that you can assign the argument name "0", "1", etc. to a member and so AutoSetMembers will work as expected.
 
Seems like the code version on CP is older than the one I got in my depot. That article is quite old (by internet standards Wink | ;) ), so I will check if I should update it or not. But yes, seems like the version currently on CP has a bug Wink | ;)
 
Sébastien
 


Intelligence shared is intelligence squared.
 
Homepage : http://www.sebastienlorion.com

GeneralRe: How get a regular parameter rather than an option?memberDaveKolb3 Apr '09 - 9:15 
Another feedback is that running "myprogram /sw1 arg1" where /sw1 is not valid, leaves both sw1 and arg1 in the UnHandledArguments collection as well as in the dictionary returned by parser.Parse(args).
 
Ideally all parsed arguments and switches would be unique amongst HandledArguments, UnhandledArguments and the theArgs StringDictionary. That would make it easier to test for valid switches, invalid switches and the proper number of plain arguments I think.
 
Is there proper way to test for invalid switches and the proper number of plain arguemnts that are not switches that I missed?
 
If expecting one plain argument arg1, I would like would like to test theArgs.Count==1 and to check if there were any invlaid switches, I would like to check UnHnadledArguments.Count > 0. Since sw1 and arg1 appear both in the theArgs dictionary and in UnHandledArguments I cannot see a way to do either check currently. Hope that made sense.
 
Thanks, Dave
 
Dave Kolb
http://dotnetcodeslingers.com

AnswerRe: How get a regular parameter rather than an option?memberSebastien Lorion3 Apr '09 - 9:35 
Let me check the version I got first, it is the same principle, but it is heavily refactored. One learns many things over the years Wink | ;)
 
Sébastien
 


Intelligence shared is intelligence squared.
 
Homepage : http://www.sebastienlorion.com

AnswerRe: How get a regular parameter rather than an option?memberSebastien Lorion4 Apr '09 - 17:59 
Ok, I took a longer look at the code I got here. Actually, I have the version on CP and another newer one. I read my notes about the latter, and it appears it was a work in progress which I decided was leading nowhere, so I will correct the CP version instead.
 
I get your point about validating arguments, but I am not sure how I could figure out if "/sw1 arg1" is a switch with a parameter or it is a standalone switch and a plain argument. I think putting them in the wrong place is worse than putting them at two places. Let me know if you have any idea how to differentiate both cases.
 
I will wait for your comments before I post a new version of the code.
 
Sébastien
 


Intelligence shared is intelligence squared.
 
Homepage : http://www.sebastienlorion.com

GeneralRe: How get a regular parameter rather than an option?memberDaveKolb4 Apr '09 - 19:58 
Thanks Sebastian. Since you only support : and = as assignment operators, then I should think it would not be a problem to determine if a switch or regular argument. Of course, one could no longer use a blank as an assignment operator as you suggested to one user, but that is just as well in my opinion, since it could only work for one blank and not more than one blank. So /sw1 arg1 would be a switch and a plain argument and /sw1=arg1 or /sw1:arg1 would be a switch with a value and no plain argument. Dave
 
Dave Kolb
http://dotnetcodeslingers.com

QuestionSuggestionmemberexbuddha16 Oct '06 - 10:14 
Hi... Nice work for sure! I like how simple it is to grab your code and use it with just a few lines of code. I was wondering if you can update the regex to accept this type of inputs "/i 2" instead of "/i:25" or "/i=25". I was not able to use your code for that type of user input.
 
Thanks.
AnswerRe: SuggestionmemberSébastien Lorion16 Oct '06 - 10:21 
Thank you for your comment Smile | :) I have currently no time to update the article, but you can do the following:
 
In the ArgumentParser class constructor, simply change
 
m_assignSymbols = new Char[] {'=', ':'};
 
for
 
m_assignSymbols = new Char[] {'=', ':', ' '};

 
Sébastien
 


Intelligence shared is intelligence squared.
 
Homepage : http://sebastienlorion.com

GeneralRe: Suggestionmemberexbuddha16 Oct '06 - 12:32 
Thanks. That does it! Cool | :cool:
QuestionUse the same switch twicememberArmus15 Sep '06 - 1:57 
Hi,
 
how can use the same switch twice?
Example:
 
MyProg.Exe /if "C:\*.bmp" /if "C:\*.jpg" /of "C:\Temp\"
 
Best regards,
Armus
AnswerRe: Use the same switch twicememberSébastien Lorion15 Sep '06 - 3:58 
The switch is used as a key in the m_unhandled and m_handled dictionaries, so at this moment, your scenario is not supported. How about having something like
 
MyProg.Exe /if "C:\*.bmp","C:\*.jpg" /of "C:\Temp\" ?
 

 
Sébastien
 


Intelligence shared is intelligence squared.
 
Homepage : http://sebastienlorion.com

QuestionDoes the code check for invalid options?memberJosef Meile15 Feb '05 - 9:30 
Hi Sébastien Lorion,
 
I just installed your code on one of my projects. It seems
to parse well the parameters, but as I see, there is no
way of checking if an invalid argument was given. I tested
it with your xmove example and I didn't get any error. I
try this:
 
C:\xmove\xmove /badarg1 /badarg2 "_" "_"
 
0 file(s) found.
0 file(s) moved.
 
I'm right or there is some option I'm missing.
 
Regargs,
Josef
AnswerRe: Does the code check for invalid options?memberSébastien Lorion17 Feb '05 - 2:50 
No automatic check is done, but you can check manually that no invalid arguments remain with ArgumentParser.UnhandledArguments property.
 
Sébastien
 


Intelligence shared is intelligence squared.
 
Homepage : http://sebastienlorion.com
GeneralAutoSetMember for VB.NET?membermike2orb23 Jan '04 - 18:04 
Attribute arrays are not CLS complaint and can't be used from VB.NET. Could you have a third version of the attribute that simply accepts the switch name?
 
Best,
Mike
GeneralRe: AutoSetMember for VB.NET?memberGuy LaRochelle29 Nov '04 - 2:27 
I totally agree with Mike... I have the same problem and can't find a way to work with the argument parser in VB.NET.
 
Too bad! I think we'll have to modify your code then...
 
Guy
QuestionWhy GPL?membergodefroi5 Nov '03 - 11:39 
This code seems to be GPL, so it can't be used in any project (even compled and used as a DLL) that isn't also GPL. Was this your intention?
AnswerRe: Why GPL?memberSébastien Lorion5 Nov '03 - 19:17 
It was a mistake but I thought I had updated the files on CP.
 
I am working on a new version more powerful and better designed, but I have many things going on at the same time so that might wait until january.
 
I will update files here, but in the mean time, just consider them licensed under LGPL.
 
Sébastien
 


Intelligence shared is intelligence squared.
 
Homepage : http://www.slorion.webhop.org
GeneralLooks GreatmemberWilliam Bartholomew7 Sep '03 - 11:48 
Looks like a great argument parser, I've been using the one Microsoft released but it's a bit of a let down in a few areas but this one makes up for it!
 
Just a comment about licensing, you might find it will be more useful to people released as LGPL and not GPL so they can use it in closed-source applications unless it is your intention for it to only be used in open-source ones.
GeneralRe: Looks GreatmemberSébastien Lorion7 Sep '03 - 15:36 
Thank you Smile | :) Sometimes, one has to wonder why Microsoft haven't included basic stuff like this ...
 
As for switching to LGPL, I agree it would be less restrictive ... I will change it when publishing the new version (see answer to comment by
Heath Stewart).
 
Sébastien
 


Intelligence shared is intelligence squared.
 
Homepage : http://www.slorion.webhop.org
GeneralBest argument parser yetmemberBrian Gideon5 Sep '03 - 3:10 
Great job. I've seen a lot of good attempts at this, but I just couldn't convince myself to start using any of them for whatever reason. I was afraid I'd have to write my own, but I'm glad you beat me to it!
 
Brian
GeneralRe: Best argument parser yetmemberSébastien Lorion5 Sep '03 - 12:10 
Thank you ! I'm glad it does the job for you Smile | :) I love it when I can just take something almost as is so if I can make that happens to others, then that's cool Cool | :cool:
 
Sébastien
 


Intelligence shared is intelligence squared.
 
Homepage : http://www.slorion.webhop.org
GeneralGreat Utility - One SuggestioneditorHeath Stewart5 Sep '03 - 2:30 
This is one of the most complete command-line parsers I've seen for .NET, and you've done a good job with some seemless integration. One feature that would be nice - something I've seen with several PERL cmdline parser, is automatic documentation for cmdline parameters when ((-|/)?|(--?|/)h(elp)?) is specified. For instance, a Documentation property for the AutoSetMemberAttribute would be handy, as well as a way to set documentation for the collection of flags you keep (if applicable).
 
This is already a very nice utility, but this is just a suggestion that could possibly add value.
 
 
-----BEGIN GEEK CODE BLOCK-----
Version: 3.21
GCS/G/MU d- s: a- C++++ UL@ P++(+++) L+(--) E--- W+++ N++ o+ K? w++++ O- M(+) V? PS-- PE Y++ PGP++ t++@ 5 X+++ R+@ tv+ b(-)>b++ DI++++ D+ G e++>+++ h---* r+++ y+++
-----END GEEK CODE BLOCK-----
GeneralRe: Great Utility - One SuggestionmemberSébastien Lorion5 Sep '03 - 12:29 
Thank you Smile | :)
 
Yes, your suggestion is a good one. Adding documentation for simple arguments is straightforward, but for flags, I might have to fool around a bit more.
 
I was also thinking about adding better handling for flags by binding them to their respective enums if applicable. For example, suppose you have a property which contains an flag enums (like FileAttributes), passing a list of flags as argument would update this property by doing an OR on each flag.
 
Anyway, I will update the article when I'm done adding this bit, which should be in a couple of days Cool | :cool:
 
Be aware that it will probably break compatibility with how the flags are handled currently.
 
Sébastien
 


Intelligence shared is intelligence squared.
 
Homepage : http://www.slorion.webhop.org
GeneralVery NicememberBlake Coverett1 Sep '03 - 7:48 
My compliments - get's my 5.
 
--
-Blake (com/bcdev/blake)
GeneralRe: Very NicememberSébastien Lorion1 Sep '03 - 14:15 
Thank you Big Grin | :-D If it's proving useful to some people, I will be happy Smile | :)
 


Intelligence shared is intelligence squared.
 
Homepage : http://www.slorion.webhop.org

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130516.1 | Last Updated 6 Nov 2003
Article Copyright 2003 by Sebastien Lorion
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid