10

In the code below, I used program options to read parameters from command-line or file. In addition, options can be set programatically at runtime through ConfigProxy::setConfig

po::options_description desc("Allowed options");
desc.add_options()
    ...
    ("compression", po::value<int>(), "set compression level");

po::variables_map vm;

class ConfigProxy
{
     template< typename T>
     void setConfig( const std::string key, const T value ){
          ... // check if the key exists in variable map "vm"

          // key exists, set the value
          runtimeConfig[key] = po::variable_value( boost::any(value), false);
     }

     po::variable_value& operator[] (const std::string key) const{
          ...
          // if exists in runtimeConfig return the value in runtimeConfig
          // of type program_options::variable_value
          ...
          // else return value in variable map "vm"
     }

     std::map<std::string, boost::program_options::variable_value> runtimeConfig;
}

through ConfigProxy, the option value is retrieved

if( vm.count("compression") ){
    int value = proxyConfig["compression"].as<int>();
    ...
}

However, if the "compression" option value provided by the user is in wrong type, for example

configProxy.setConfig("compression", "12" );
...
int value = configProxy["compression"].as<int>(); // was set as string

then exception is thrown

what():  boost::bad_any_cast: failed conversion using boost::any_cast

The exception clearly shows the type cast problem. But the message seems not so helpful to users for finding out which option is responsible for the error.

Is there a better way to inform users about this type of error, instead of throwing bad_any_cast exception?

----- Edit --------------------------

Thanks to Luc Danton and Tony, I have found how Program options shows the errors.

void validate(boost::any& v,
              const std::vector< std::basic_string<charT> >& xs,
              T*, long)
{
    validators::check_first_occurrence(v);
    std::basic_string<charT> s(validators::get_single_string(xs));
    try {
        v = any(lexical_cast<T>(s));
    }
    catch(const bad_lexical_cast&) {
        boost::throw_exception(invalid_option_value(s));
    }
}

I think, by implementing the logic, I can get rid of the bad_any_cast exception.

user1492900
  • 575
  • 1
  • 8
  • 16

2 Answers2

4

Have you tried it?

("compression", po::value<int>(), "set compression level");

Notice po::value<int>(). You specify here that the associated value has type int. When the user passes something that Boost.ProgramOptions can't convert to int, the program fails with an error message:

error: in option 'compression': invalid option value

That's one of the roles of the library after all.

The reason you do have to do vm["compression"].as<int>() is because the the type of compression is specified in a function call (the triplet in parentheses), something in the runtime world. This can't influence the return type of vm["compression"], so it needs to be some dynamic type emulation. Hence the boost::any_cast_failed exception when you query a type that you did not specify.

Luc Danton
  • 34,649
  • 6
  • 70
  • 114
  • thank you for your help. You are right, the program options should fail with "invalid option value" error message. I got bad_any_cast because the way I use program option is wrong. – user1492900 Jun 08 '11 at 23:13
  • I failed to clearly state how I used program_options. I have updated my question. thanks. – user1492900 Jun 08 '11 at 23:52
2

Can you not put this:

if( vm.count("compression") ){
    int value = vm["compression"].as<int>();
    ...
}

in a try catch block where you catch the bad_any_cast exception thrown by boost is caught and then show a message to the user of your own, by either throwing (retrow) a new exception of a type you have created or by writing a message to the std::cout, I guess much depending on your application.

so:

try {
    if( vm.count("compression") ){
        int value = vm["compression"].as<int>();
        ...
    }
}
catch(const boost::bad_any_cast& ex)
{
//rethrow exception of your own to show to the user or print msg to std::cout
}
Tony The Lion
  • 61,704
  • 67
  • 242
  • 415
  • thank you for your help. Catching bad_any_cast would help. I was curious to know whether there are other ways to do this, instead of putting try-catch around every "int" configuration item. – user1492900 Jun 08 '11 at 22:52
  • I failed to clearly state how I used program_options. I have updated my question. thanks. – user1492900 Jun 08 '11 at 23:53