cxxopts
                                
                                 cxxopts copied to clipboard
                                
                                    cxxopts copied to clipboard
                            
                            
                            
                        Optional and mandatory options
I think it would be nice to have optional and mandatory options that way no validation is needed to be done by the user
As I've said in several previous issues #44 #25, I don't think that this sort of logic belongs in this library. The main reasoning is that usually options are not just mandatory or optional., but something more complicated. Take for example tar, it has a mutually exclusive set of options, and a bunch of options that only go with other options.
tar: You must specify one of the '-Acdtrux', '--delete' or '--test-label' options
That said, since this has been requested a few times, I think a separate validation library would be appreciated by a few people. I will work on this at some point, but I can't guarantee getting to it straight away.
Thanks! Some how I missed #44 when searching the issues.
I agree a validation should be on different defined combinations of the arguments In-order to decide if a set of arguments is valid or not.
I'll be happy to contribute, let me know.
I think a separate validation library is not necessary. If each option could optionally specify required and/or excluded arguments, resolving conflicts is then left to the user to define and the validation simply enforces the supplied rules.
For simple cases it would be very useful to have required options (like boost::program_options::value<>()->required()). At least it allows to drop annoying checks for an every required option.
Of course, for more complicated cases, users can figure out it with own logic.
I think this is critical issue it breaks open/close principle. e.g.: script calls: app.exe -p "test1" after some time I've updated application with the additional parameter and now script calls:" app.exe -p "test1" -x "test2" -> previous version of the app.exe can not be used with the extended command line how to fix this problem? Library does not allow any extensions -> and forces to update all external environment my case: somehow try to retrieve app.exe_version if ( app.exe_version == 1 ) app.exe -p "test1" else if ( app.exe_version == 2 ) app.exe -p "test1" -x "test2"
I think this is critical issue it breaks open/close principle. e.g.: script calls: app.exe -p "test1" after some time I've updated application with the additional parameter and now script calls:" app.exe -p "test1" -x "test2" -> previous version of the app.exe can not be used with the extended command line how to fix this problem?
I don't understand the problem. I don't see how this is related to whether the library throws an exception when a mandatory option is missing.
Are you talking about allowing unrecognised options without raising an error?
>Are you talking about allowing unrecognised options without raising an error? I do expect, that mine old app.exe will work even some options are unrecognized. Yes, error should not be raised, but command line also must be parsed. -> should I be forced to update all the old app.exe binary versions in case of any command line extension for the new versions? I do not think so. Or maybe command line versioning must be tracked by the script developer? I do not think so.
That's a separate discussion then which I will move to another issue.
Although it is easy to do, the main problem with unrecognised options is how to recognise their arguments. For example is --foo bar an unrecognised option --foo with argument bar, or is it a boolean that takes no argument followed by a positional argument?
For simple cases it would be very useful to have required options (like
boost::program_options::value<>()->required()). At least it allows to drop annoying checks for an every required option. Of course, for more complicated cases, users can figure out it with own logic.
I'm not sure if this is still open to discussion or not, because this is quite an old issue, however the only thing that would make this library be perfect to me is the required arguments and if the required arguments were not given it would simply print the --help message.
Considering that this can be an optional parameter it wouldn't force users to use this but instead would allow small more linear programs to keep it's code simpler and avoid checking things manually.
I still have some plans to address this. I wanted to make a separate validator that you can run against the parsed options.
Hi! What's the status on this issue?
I am expecting this feature.
Here's a simple and elegant way to handle "required", "optional" and actually any complex rule: by using template expressions
Here's a small working example that handles constraints of type "option 'foo' must appear exactly once and option 'bar' exactly twice" :
#include <cxxopts.hpp>
#include <cstddef>
#include <iostream>
namespace cxxopts {
namespace rules {
// =============================================================
class Option
{
public:
  Option(std::string option_name) : name(option_name) {}
  inline size_t count(const ParseResult &parsing) const
  {
    return parsing.count(name);
  }
  std::string name;
};
// =============================================================
// =============================================================
class ExactCount
{
public:
  ExactCount(Option option, size_t target_count)
    : option(std::move(option)), target_count(target_count) {};
  inline bool operator () (const ParseResult& parsing) const
  {
    return option.count(parsing) == target_count;
  }
  inline explicit operator std::string () const
  {
    if (target_count == 1)
      return "option '--" + option.name + "' must be provided exactly once";
    else return "option '--" + option.name + "' must be provided exactly " + std::to_string(target_count) + " times";
  }
protected:
  Option option;
  size_t target_count;
};
ExactCount operator == (Option option, size_t target_count)
{
  return ExactCount(std::move(option), target_count);
}
// =============================================================
// =============================================================
template <class SubExpression1, class SubExpression2>
class AND
{
public:
  AND(SubExpression1 expr1, SubExpression2 expr2)
    : expr1(std::move(expr1)), expr2(std::move(expr2)) {}
  inline bool operator () (const ParseResult& parsing) const
  {
    return expr1(parsing) && expr2(parsing);
  }
  inline explicit operator std::string () const
  {
    return "(" + std::string(expr1) + ") and (" + std::string(expr2) + ")";
  }
protected:
  SubExpression1 expr1;
  SubExpression2 expr2;
};
template <class SubExpression1, class SubExpression2>
auto operator && (SubExpression1 expr1, SubExpression2 expr2)
{
  return AND(std::move(expr1), std::move(expr2));
}
// =============================================================
template <class Rule>
void enforce(const Rule& rule, const ParseResult& parsing)
{
  if (not rule(parsing))
    throw std::runtime_error("rule not respected: " + std::string(rule));
}
}
}
int main(int argc, char *argv[])
{
  cxxopts::Options options(
    "hash-id",
    "Advertising-ID hasher");
  options.add_options()
    ("i,input", "input", cxxopts::value<std::string>())
    ("o,output", "output", cxxopts::value<std::string>())
    ("h,help", "Print usage")
    ;
  auto parsing = options.parse(argc, argv);
  auto rule = (cxxopts::rules::Option("input") == 1) and (cxxopts::rules::Option("output") == 1);
  cxxopts::rules::enforce(rule, parsing);
  std::cout << parsing["output"].as<std::string>() << std::endl;
  return 0;
}
After implementing the or and not boolean operation, and all the comparison operations >, >=...  any rule can be expressed and everyone is happy !