Cook Computing

NOptFunc

June 6, 2009 Written by Charles Cook

A few days ago Simon Willison posted about his optfunc command line parsing program written in Python:

Command line parsing libraries in Python such as optparse frustrate me because I can never remember how to use them without consulting the manual. optfunc is a new experimental interface to optparse which works by introspecting a function definition (including its arguments and their default values) and using that to construct a command line argument parser.

This is the example he provides:


import optfunc
    
def upper(filename, verbose = False):
    "Usage: %prog <file> [--verbose] - output file content in uppercase"
    s = open(filename).read()
    if verbose:
        print "Processing %s bytes..." % len(s)
    print s.upper()
 
if __name__ == '__main__':
    optfunc.run(upper)

And this is the resulting command-line interface:


$ ./demo.py --help
Usage: demo.py <file> [--verbose] - output file content in uppercase
    
Options:
  -h, --help show this help message and exit
  -v, --verbose 

I've recently been experimenting with C# 4.0 and I realized that the new optional parameter and default parameter value features make possible a similar style of command line parsing. So I wrote some code to do this and the result is the NOptFunc project. Using NOptFunc the code above can be written like this in C#:


using System;
using System.IO;
using CookComputing;

class Program
{
  static void Main(string[] args)
  {
    try
    {
      NOptFunc.Run(typeof(Program).GetMethod("Run"), args);
    }
    catch (Exception ex)
    {
      Console.Error.WriteLine(ex.Message);
    }
  }

  public static void Run(string filename, bool verbose = false)
  {
    string s = File.ReadAllText(filename);
    if (verbose)
      Console.WriteLine("Processing {0} bytes...", s.Length);
    Console.WriteLine(s.ToUpper());
  }
}

In comparison to the Python code the invocation of NOptFunc.Run() is quite ugly and also suffers from potentially failing at runtime if the wrong method name is supplied. It would be nice to be able to write something like this:


      NOptFunc.Run(methodinfo(Program.Run), args);

i.e. assuming that C# had a methodinfo operator along the lines of typeof, returning an instance of MethodInfo instead of Type (overloaded methods would complicate matters, requiring something like methodinfo(Program.Run(int, string)) ). Ian Griffiths discussed this in his post Getting a MethodInfo From a Method Token:

So I get a relatively warm fuzzy feeling about using typeof - I like code that will only be able to run if it can't fail. All other things being equal, I prefer this to code that has potential runtime failure modes.

I've always been mildly perplexed that there's no equivalent way of retrieving a MethodInfo object. E.g. a hypothetical methodinfo(SomeClass.SomeMethod) operator. It's not up the top of my list of language features I want added, it just seems mildly inconsistent to have the operator for getting Type objects but not the corresponding FieldInfo and MethodInfo objects. (Interestingly, there doesn't seem to be a direct way to retrieve an EventInfo in IL, so I can't really object to that one not being in the language.)

Until recently, I had never looked into the details of this. I wasn't previously sure if this missing feature was just something C# chooses not to do, or whether it's because, it can't be done. But I recently had reason to generate some IL that does exacly this, so I can now say with confidence that it's possible, and it's just that C# doesn't supply a corresponding operator.

I've still got a lot of tidying up to do with NOptFunc, for example throwing exceptions with more useful messages and supporting --help, but the basic functionality is working.