سؤال

So I have just started adding option support to my code. I can either do it myself or use boost's program options. The only thing that holds me back from using boost's is yet another dependency that I'm adding to the project. However, if it can handle parsing complex objects with ease, I'm more willing to pay the price.

I just tried something like this based on the examples and it does not work:

#include <boost/program_options.hpp>
namespace po = boost::program_options;


#include <iostream>
#include <fstream>
#include <iterator>
using namespace std;


struct Grid{
    double xmin, xmax;
};

int main(int ac, char* av[])
{
    try {
        Grid g;

        po::options_description cmd("Allowed options");
        cmd.add_options()
            ("help", "produce help message")
            ("Grid", "grid information")
            ;

        po::variables_map vm;
        store(parse_command_line(ac, av, cmd), vm);
        notify(vm);

        if (vm.count("help")) {
            cout << cmd << "\n";
            return 0;
        }

        g = vm["Grid"].as<Grid>();
        cout << g.xmin << " " << g.xmax << endl;

    }
    catch(exception& e)
    {
        cout << e.what() << "\n";
        return 1;
    }    
return 0;

When I run the code with ./a.out --Grid {-1, 1}, I get boost::bad_any_cast: failed conversion using boost::any_cast. I do not understand what that means but my guess is I cannot properly tell boost of my object type Grid. How can I do this correctly with boost?

هل كانت مفيدة؟

المحلول

First, the simple way is to use po::value<std::vector<double>>()->multitoken(),: However, you need to pass arguments like this: --Grid -1 1

int main(int ac, char* av[])
{
    try {

        po::options_description cmd("Allowed options");
        cmd.add_options()
            ("help", "produce help message")
            ("Grid", po::value<std::vector<double>>()->multitoken(),
             "grid information")
            ;
        po::variables_map vm;
        store(parse_command_line(ac, av, cmd), vm);
        notify(vm);

        if (vm.count("help")) {
            cout << cmd << "\n";
            return 0;
        }

        Grid g{vm["Grid"].as<std::vector<double>>()[0],
               vm["Grid"].as<std::vector<double>>()[1]};
        cout << g.xmin << " " << g.xmax << endl;

    }
    catch(exception& e)
    {
        cout << e.what() << "\n";
        return 1;
    }
}

If you want to pass arguments like --Grid {-1,1}, you could add a operator>> and parse the std::string yourself:

std::istream& operator>>(std::istream &in, Grid &g)
{
    // Note that this code does not do any error checking, etc.
    // It is made simple on purpose to use as an example
    // A real program would be much more robust than this
    std::string line;
    std::getline(in, line);
    std::stringstream ss(line);
    char bracket, comma;
    double xmin, xmax;
    ss >> bracket >> xmin >> comma >> xmax;
    g = Grid{xmin, xmax};
    return in;
}

//...
Grid g;
try {

//...
cmd.add_options()
   ("help", "produce help message")
   ("Grid", po::value(&g), "grid information")
;
//...

cout << g.xmin << " " << g.xmax << endl;

Also, notice that in --Grid {-1,1}, there is no space: -1,1, not -1, 1. This is because line will only contain {-1,.

If you want the space, here is an example that can parse --Grid {-1, 1}, using a custom validator and multitoken:

// Showing only changes, rest of the code is the same
void validate(boost::any& v, const std::vector<std::string>& val,
              Grid*, double)
{
    std::stringstream ss(val[0]);
    char bracket;
    double xmin, xmax;
    ss >> bracket >> xmin;
    ss.str(val[1]);
    ss >> xmax;
    v = Grid{xmin, xmax};
}

po::options_description cmd("Allowed options");
        cmd.add_options()
        ("help", "produce help message")
        ("Grid", po::value(&g)->multitoken(), "grid information")
        ;
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top