Image

Imageutil wrote in Imagecpp

command line options parsing

Do you have a favorite bit of code/lightweight library for parsing command line options in C++? Below is my own rather inelegant attempt. (My goal was to make something that was typesafe and that could automatically generate usage information. My code is limited to handling options that take exactly one argument. Also, since you must pass a variable into the option to get its output value, the code makes it inconvenient to share standard Option objects between the code for different command line utilities, a big failing. And it makes it a pain to cleanly create a list of options and then destroy it. I'm interested in hearing any suggestions you have on how to improve this code as well as suggestions on libraries to look at.)

options.h:
#ifndef __OPTIONS_H__
#define __OPTIONS_H__

#include <cstring>
#include <stdexcept>
#include <iostream>
#include <sstream>
#include <string>
using namespace std;

/**
 * A command line option.
 */
class Option {
 public:

  /**
   * Create a new Option.
   *
   * theFlag -- flag identifying this option. This should probably
   * begin with "-" or "--".
   */
  Option(const char* theFlag) : _flag(theFlag) {}

  virtual ~Option() {}

  /**
   * Returns the string identifying this option, eg, "--width".
   */
  string flag() const
    {
      return _flag;
    }

  /**
   * Returns a description of this option.
   */
  virtual string description() const = 0;

  /**
   * Parses the given string and sets the value
   * associated with this option.
   * If there is a problem parsing value,
   * prints a message to cerr and throws an
   * invalid_argument exception.
   */
  virtual void parse(const char* value) = 0;

 private:
  string _flag;
};

/**
 * Specification for a specific option.
 */
template<typename T>
class OptionSpec : public Option {
 public:
  OptionSpec(const char* flag, T& value, const T& defaultValue, const char* description = "") : Option(flag), _value(value), defaultValue(defaultValue), _description(description) {
    value = defaultValue;
  }

  void parse(const char* strValue)
    {
      istringstream in(strValue);
      T result;
      in >> result;
      _value = result;
      if (in.fail()) {
	cerr << "Problem parsing " << flag() << ". Got (" << strValue << ")\n";
	string message(flag());
	message += ": (";
	message += strValue;
	message += ")";
	throw invalid_argument(message);
      }
    }

  string description() const
    {
      ostringstream message;
      message << _description
	      << " Defaults to " << defaultValue << ".\n";
      return message.str();
    }

 private:
  OptionSpec(const OptionSpec& anOptionSpec);
  OptionSpec& operator=(const OptionSpec& anOptionSpec);

  T& _value;
  T defaultValue;
  string _description;
};

/**
 * Create a new Option that will set the value for the given
 * flag. The caller is responsible for deleting the memory
 * of the returned object.
 */
template<typename T>
Option* newOption(const char* flag, T& value,
		  const T& defaultValue, const char* description="")
{
  return new OptionSpec<T>(flag, value, defaultValue, description);
}

/**
 * Parses the given command line arguments using the given options.
 * If an unrecognized argument is encountered, prints an informative
 * message to cerr and throws an invalid_argument exception.
 *
 * options -- pointer to the first element in the array of Options.
 * optionsCount -- number of options.
 * args -- pointer to the first argument.
 * argsCount -- number of elements in the arguments array.
 */
void parseArguments(Option** options, size_t optionsCount,
		    const char* const* args, size_t argsCount)
{
  if (argsCount == 0) {
    return;
  }

  size_t argsEnd = argsCount - 1;
  for (size_t i = 0; i < argsEnd; i += 2) {
    string flag(args[i]);
    bool unrecognized = true;
    for (size_t j = 0; j < optionsCount && unrecognized; ++j) {
      if (flag == options[j]->flag()) {
	options[j]->parse(args[i+1]);
	unrecognized = false;
      }
    }
    if (unrecognized) {
      cerr << "Unrecognized flag: " << flag << "\n";
      string message("Unrecognized flag: ");
      message += flag;
      throw invalid_argument(message);
    }
  }
}

/**
 * Prints a description of the options to out.
 */
void printOptions(ostream& out, const Option* const* options, size_t count)
{
  for (size_t i = 0; i < count; ++i) {
    out << options[i]->flag() << " <value>:\n";
    out << "  " << options[i]->description();
  }
}

#endif



main.cpp:
#include <iostream>
#include "options.h"
using namespace std;

int main(int numArgs, char** args)
{
  double a;
  int n;
  string s;
  string banana("banana");
  Option* options[3] = {newOption("--a", a, 3.1415, "a!"),
			newOption("--n", n, 13, "n!"),
			newOption("--s", s, banana, "s!")};
  parseArguments(options, 3, args+1, numArgs-1);
  for (int i = 0; i < 3; ++i) {
    delete options[i];
  }

  cout << "a: " << a << "\n";
  cout << "n: " << n << "\n";
  cout << "s: " << s << "\n";
  return 0;
}




$ g++ -o m main.cpp
$ ./m
a: 3.1415
n: 13
s: banana
$ ./m --a 44 --s hello --n 4
a: 44
n: 4
s: hello