25 Nisan 2018 Çarşamba

program_options Custom Validator

Giriş
Custom validator yazmak için iki seçenek var.
1. operator >> içinde doğrulama yapmak
2. kendi tipimiz için validate() metodu yazmak.

operator>> metodu içinde
Örnek
Açıklaması şöyle.
...it's often easier to write a custom class for each option type. Normally program_options uses operator>> to decode the option value, so if you override this you get a chance to throw an exception that program_options recognises. If you use nested exceptions, you can print quite detailed error diagnostics.
Örnek
Şöyle yaparız. operator >> içinde kendi chek_value() metodunu çağırırız.
// a custom option type
struct foo {
  std::string value;
  
  // check the value and throw a nested exception chain if it's wrong    
  void check_value() const
    try {
      if (value != "foo" and value != "bar") {
        std::ostringstream ss;
        ss << "value must be foo or bar, you supplied " << std::quoted(value);
        throw std::invalid_argument(ss.str());
      }
    }
    catch (...) {
     std::throw_with_nested(po::validation_error(
       po::validation_error::invalid_option_value,"foo"));
    }
  }
  // overload operators
  friend std::istream &operator>>(std::istream &is, foo_or_bar &arg) {
    is >> arg.value;
    arg.check_value();
    return is;
  }

  friend std::ostream &operator<<(std::ostream &os, foo_or_bar const &arg) {
    return os << arg.value;
  }

};
validate metodu
İmzası şöyle
void validate(boost::any &v, const std::vector<std::string> &values, Foo * , int);
Örnek
Elimizde şöyle bir kod olsun
namespace po = boost::program_options;

struct MyClass {
    int x;
    MyClass(int a) : x(a){};

    friend std::ostream& operator<<(std::ostream& os, MyClass const& mc) {
        return os << "MyClass(" << mc.x << ")";
    }
};

void validate(boost::any &v, const std::vector<std::string> &values, MyClass * , int) {
    v = MyClass(std::stoi(values.front()));
}
Çağırmak için şöyle yaparız.
int main(int argc, char **argv) {
    po::options_description desc("");

    boost::optional<MyClass> my_option;
    desc.add_options()("MyClass", po::value(&my_option), "MyClass");

    po::variables_map args;
    store(parse_command_line(argc, argv, desc), args);
    notify(args);

    std::cout << "Arg: " << my_option << "\n";
}
Örnek - validate metodu
Şöyle yaparız. unsigned yerine nonnegative yapısını kullanırız.
struct nonnegative {
  unsigned value;
};

void validate(boost::any& v, const std::vector<std::string>& values,
 nonnegative*, int)
{
  using namespace boost::program_options;
  validators::check_first_occurrence(v);

  std::string const& s = validators::get_single_string(values);
  if (s[0] == '-') {
    throw validation_error(validation_error::invalid_option_value);
  }

  v = lexical_cast<unsigned>(s);
}
Örnek - validate metodu
Şöyle yaparız.
enum class Ipc : int8_t
{
    TCP = 0,
    UDP,
};

// This function is called for arguments of the type Ipc
void validate(boost::any& v, const vector<string>& values, Ipc*, int)
{
  po::validators::check_first_occurrence(v);
  const string& enteredValue = po::validators::get_single_string(values);
  const string& enteredValueUpper = boost::to_upper_copy(enteredValue);

  if (enteredValueUpper == "TCP")
  {
    v = boost::any(Ipc::TCP);
  }
  else if (enteredValueUpper == "UPD")
  {
    v = boost::any(Ipc::UDP);
  }
  else
  {
    throw po::validation_error(enteredValue);
  }
}
Hata varsa exception'daki mesaj şöyledir.
error: the argument for option '--ipc' is invalid: shm
Şu şekilde fırlatırsak hatalı girdiyi göremeyiz.
throw po::validation_error(po::validation_error::invalid_option_value, enteredValue);
Çıktı olarak şunu alırız.
error: the argument for option '--ipc' is invalid
Örnek - validate metodu
Elimizde kendi yapımız olsun
using clock = std::chrono::steady_clock;
struct duration {
  clock::duration value;

  friend std::ostream& operator<<(std::ostream& os, duration const& holder) {
    using namespace std::chrono;
    auto ms = duration_cast<milliseconds>(holder.value).count();

    if (ms >= 1000)
      return os << (ms/1000) << "s";
    else
      return os << ms << "ms";
    }
};
Şöyle yaparız.
template<class charT>
void validate(boost::any& v, const std::vector< std::basic_string<charT> >& xs,
 duration*, long)
{
  po::validators::check_first_occurrence(v);
  std::basic_string<charT> s(po::validators::get_single_string(xs));

  int magnitude;
  clock::duration factor;

  namespace qi = boost::spirit::qi;
  qi::symbols<char, clock::duration> unit;
  unit.add("s",1s)("ms",1ms)("us",1us)("µs",1us)("m",1min)("h",1h);

  if (parse(s.begin(), s.end(), qi::int_ >> unit >> qi::eoi, magnitude, factor))
    v = duration {magnitude * factor};
  else
    throw po::invalid_option_value(s);
}

Hiç yorum yok:

Yorum Gönder