Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

boost program options: does custom validator require overloading operator>>?

In the example (regex.cpp), the author of the library created a custom struct (magic_number) and a validate function for this struct to show how custom struct can be integrated into program options. I followed his example to create a validate function for a custom class (MyClass). Compiler complains that a lexical_cast is not available for MyClass. I then implemented std::istream& operator>>(std::istream& in, MyClass& d), removed void validate(.., MyClass*, ..), the code compiles. Can anyone explain why the example doesn't require operator>>, while mine doesn't require validate?

EDIT:

#include <MyLib/MyClass.h>

std::istream& operator>>(std::istream& in, MyClass& obj) {
    // some code to populate obj
    return in;
}


po::variables_map parseCommandLine(int argc, char* argv[]) {

    po::options_description options("Options");
    options.add_options()
        ("help", "produce help message")
        ("obj", po::value<MyClass>(), "")
        ;
    po::variables_map vm;
    store(po::command_line_parser(argc, argv)
        .options(options).run(), vm);
    notify(vm);

    return vm;
}

int main(int argc, char* argv[]) {

    try {
        po::variables_map vm = parseCommandLine(argc, argv);

        MyClass obj = vm["my"].as<MyClass>();

        cout << obj << endl;
    } catch(std::exception& e) {
        cout << e.what() << "\n";
        return 1;
    }   
    return 0;
}
  • the code compiles without validate.

I also tried making minimum change to regex.cpp:

  1. remove magic_number
  2. add #include <MyLib/MyClass.h>
  3. replace all occurance of magic_number by MyClass.
  4. comment out all code in validate.
  5. This does not compile.

EDIT: add validate. None of them solved the compiler error.

void validate(boost::any& v, 
              const std::vector<std::string>& values,
              std::vector<MyClass>*, int)
{
}

void validate(boost::any& v, 
              const std::vector<std::string>& values,
              MyClass*, long)
{
}

void validate(boost::any& v, 
              const std::vector<std::string>& values,
              MyClass*, int)
{      
}

EDIT: It may relate to namespaces.

After I surrounded the validate function by namespace boost { namespace program_options { }}, the code compiled without overloading op>>. It also works if validate is put into the same namespace as MyClass. Can anyone explain this?

like image 828
Candy Chiu Avatar asked Nov 21 '12 13:11

Candy Chiu


1 Answers

The basic problem you are facing is that C++ doesn't offer any facility to convent a string to an arbitrary user object (I mean without writing any code).

To solve the problem, program_options offers two possibilities:

  • you implement operator>>, which is the standard C++ way of doing so, but which may have impact in some other areas (i.e. you may want to parse your object in a specific way except on the command line). Internally, boost::lexical_cast is used to implement the conversion and will throw an error if op>> is not found.
  • you implement the validate function, which is specific to program_options but that has no impact outside option management.

I guess it uses template meta programming to find out whether you have provided validate or it will default to lexical_cast.

I can't help you why your attempt with validate failed since you didn't provide the code for it.

Here is a working example, though:

#include <boost/program_options.hpp>
#include <vector>
#include <string>

namespace po = boost::program_options;

namespace lib {
   class MyClass
    {
    public:
        int a;
    };

    void validate(boost::any& v,
                  const std::vector<std::string>& values,
                  MyClass*, int)
    {
        po::validators::check_first_occurrence(v);
        const string& s = po::validators::get_single_string(values);
        v = boost::any(MyClass { boost::lexical_cast<int>(s) } );
    }
}


po::variables_map parseCommandLine(int argc, char* argv[])
{
    po::options_description options("Options");
    options.add_options()
        ("help", "produce help message")
        ("obj", po::value<lib::MyClass>(), "")
        ;
    po::variables_map vm;
    store(po::command_line_parser(argc, argv)
        .options(options).run(), vm);
    notify(vm);

    return vm;
}

int main(int argc, char* argv[])
{
    try {
        po::variables_map vm = parseCommandLine(argc, argv);
        lib::MyClass obj = vm["obj"].as<lib::MyClass>();
        cout << obj.a << endl;
    } catch(std::exception& e) {
        cout << e.what() << "\n";
        return 1;
    }
    return 0;
}
  • Update

With a namespace, both the class and validate must belong to the same namespace.

like image 155
J.N. Avatar answered Sep 28 '22 05:09

J.N.