The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#ifndef getoptpp_h
#define getoptpp_h

extern "C" {
#include <getopt.h>
}
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
#include <vector>

namespace getoptpp {
  
  class opt_base;
  typedef std::vector<opt_base*> opt_list_t;
  enum {
    opt_template_length = 30
  };
  
  template <typename T> struct _opt_list {
    static opt_list_t* opts;
    static void register_opt(opt_base* opt) {
      if (opts == NULL) {
	opts = new opt_list_t();
      }
      opts->push_back(opt);
    }
    static void unregister_opt(opt_base* opt) {
      assert(opts != NULL);
      opt_list_t::iterator i = std::find(opts->begin(), opts->end(), opt);
      assert(i != opts->end());
      opts->erase(i);
      if (opts->size() == 0) {
	delete opts;
	opts = NULL;
      }
    }
  };
  template <typename T> opt_list_t* _opt_list<T>::opts;
  typedef _opt_list<bool> opt_list;
  
  class opt_base {
  protected:
    option o_;
    char shortname_;
    bool die_on_validate_;
    std::string desc_;
  public:
    opt_base(char shortname, const char* longname, int has_arg, bool required,
	     const std::string& desc)
      : shortname_(shortname), die_on_validate_(required), desc_(desc) {
      o_.name = longname;
      o_.has_arg = has_arg;
      o_.flag = NULL;
      o_.val = 0;
      opt_list::register_opt(this);
    }
    virtual ~opt_base() {
      opt_list::unregister_opt(this);
    }
    char shortname() const { return shortname_; }
    const option& get_option() const { return o_; }
    virtual void handle() {
      die_on_validate_ = false;
    }
    virtual bool validate() {
      if (die_on_validate_) {
	std::cerr << "option --" << o_.name << " not set" << std::endl;
	return false;
      }
      return true;
    }
    void help(std::ostream& os) {
      std::string s("  --");
      s += o_.name;
      switch (o_.has_arg) {
      case required_argument: s += '='; break;
      default: break;
      }
      if (s.size() < opt_template_length) {
	s.insert(s.end(), opt_template_length - s.size(), ' ');
	os << s << "  " << desc_;
      } else {
	os << s << std::endl << std::string(opt_template_length + 2, ' ')
	   << desc_;
      }
    }
  };
  
  class opt_flag : public opt_base {
  protected:
    bool flag_;
  public:
    opt_flag(char shortname, const char* longname, const std::string& desc)
      : opt_base(shortname, longname, no_argument, false, desc), flag_(false)
      {}
    virtual void handle() {
      flag_ = true;
    }
    const bool operator*() const { return flag_; }
  };
  
  template <typename T> class opt_value_base : public opt_base {
  protected:
    T value_;
  public:
    opt_value_base(char shortname, const char* longname, bool required,
		   const std::string& desc, const T& defval)
      : opt_base(shortname, longname, required_argument, required, desc),
	value_(defval) {}
    const T& operator*() const { return value_; }
    T& operator*() { return value_; }
    const T* operator->() const { return &value_; }
    T* operator->() { return &value_; }
  };
  
  class opt_str : public opt_value_base<std::string> {
  public:
    opt_str(char shortname, const char* longname, bool required,
	    const std::string& desc, const std::string& defval = std::string())
      : opt_value_base<std::string>(shortname, longname, required, desc,
				    defval) {}
    virtual void handle() {
      opt_value_base<std::string>::handle();
      value_ = optarg;
    }
  };
  
  class opt_int : public opt_value_base<int> {
  public:
    opt_int(char shortname, const char* longname, bool required,
	    const std::string& desc, const int& defval = 0)
      : opt_value_base<int>(shortname, longname, required, desc, defval) {}
    virtual void handle() {
      opt_value_base<int>::handle();
      if (sscanf(optarg, "%d", &value_) != 1) {
	std::cerr << "--" << get_option().name << " only accepts numbers"
		  << std::endl;
	exit(1);
      }
    }
  };
  
  class opt_version : public opt_flag {
  protected:
    std::string version_;
  public:
    opt_version(char shortname, const char* longname,
		const std::string& version)
      : opt_flag(shortname, longname, "print version"), version_(version) {}
    virtual void handle() {
      std::cout << version_ << std::endl;
      exit(0);
    }
  };
  
  class opt_help : public opt_base {
  protected:
    std::string progname_;
    std::string commands_;
  public:
    opt_help(char shortname, const char* longname, const std::string& progname,
	     const std::string& commands)
      : opt_base(shortname, longname, no_argument, false, "print this help"),
	progname_(progname), commands_(commands) {}
    virtual void handle() {
      std::cout << "Usage: " << progname_ << " options " << commands_
		<< std::endl;
      std::cout << "Options: " << std::endl;
      for (opt_list_t::const_iterator i = opt_list::opts->begin();
	   i != opt_list::opts->end();
	   ++i) {
	std::cout << "  ";
	(*i)->help(std::cout);
	std::cout << std::endl;
      }
      std::cout << std::endl;
      exit(0);
    }
  };
  
  inline bool getopt(int argc, char** argv) {
    std::string optstr;
    option* opts = new option [opt_list::opts->size() + 1];
    for (size_t i = 0; i < opt_list::opts->size(); ++i) {
      opts[i] = (*opt_list::opts)[i]->get_option();
      if (char ch = (*opt_list::opts)[i]->shortname()) {
	optstr.push_back(ch);
      }
      if (opts[i].has_arg != no_argument) {
	optstr.push_back(':');
      }
    }
    memset(opts + opt_list::opts->size(), 0, sizeof(option));
    int ch, longidx;
    while ((ch = getopt_long(argc, argv, optstr.c_str(), opts, &longidx))
	   != -1) {
      if (ch == ':' || ch == '?') {
	return false;
      }
      (*opt_list::opts)[longidx]->handle();
    }
    bool success = true;
    for (opt_list_t::iterator i = opt_list::opts->begin();
	 i != opt_list::opts->end();
	 ++i) {
      if (! (*i)->validate()) {
	success = false;
      }
    }
    return success;
  }
}

#endif