Arguments requis et optionnels utilisant les Options du programme Boost Library

j'utilise Boost Program Options Library pour analyser les arguments en ligne de commande.

j'ai les exigences suivantes:

  1. une fois que l ' "aide" est fournie, toutes les autres options sont optionnelles;
  2. une fois que l '"aide" n'est pas fournie, toutes les autres options sont nécessaires.

comment je peux gérer ça? Voici le code qui gère ça, et j'ai trouvé que c'est très redondant, et je pense que là doit être facile à faire, non?

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

bool process_command_line(int argc, char** argv,
                          std::string& host,
                          std::string& port,
                          std::string& configDir)
{
    int iport;

    try
    {
        po::options_description desc("Program Usage", 1024, 512);
        desc.add_options()
          ("help",     "produce help message")
          ("host,h",   po::value<std::string>(&host),      "set the host server")
          ("port,p",   po::value<int>(&iport),             "set the server port")
          ("config,c", po::value<std::string>(&configDir), "set the config path")
        ;

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

        if (vm.count("help"))
        {
            std::cout << desc << "n";
            return false;
        }

        // There must be an easy way to handle the relationship between the
        // option "help" and "host"-"port"-"config"
        if (vm.count("host"))
        {
            std::cout << "host:   " << vm["host"].as<std::string>() << "n";
        }
        else
        {
            std::cout << ""host" is required!" << "n";
            return false;
        }

        if (vm.count("port"))
        {
            std::cout << "port:   " << vm["port"].as<int>() << "n";
        }
        else
        {
            std::cout << ""port" is required!" << "n";
            return false;
        }

        if (vm.count("config"))
        {
            std::cout << "config: " << vm["config"].as<std::string>() << "n";
        }
        else
        {
            std::cout << ""config" is required!" << "n";
            return false;
        }
    }
    catch(std::exception& e)
    {
        std::cerr << "Error: " << e.what() << "n";
        return false;
    }
    catch(...)
    {
        std::cerr << "Unknown error!" << "n";
        return false;
    }

    std::stringstream ss;
    ss << iport;
    port = ss.str();

    return true;
}

int main(int argc, char** argv)
{
  std::string host;
  std::string port;
  std::string configDir;

  bool result = process_command_line(argc, argv, host, port, configDir);
  if (!result)
      return 1;

  // Do the main routine here
}
69
demandé sur rcollyer 2011-03-22 20:37:57

4 réponses

j'ai moi-même rencontré ce problème. La solution réside dans le fait que la fonction po::store remplit la fonction variables_map tandis que po::notify génère toutes les erreurs rencontrées, de sorte que vm peut être utilisé avant l'envoi de toute notification.

donc, comme dans Tim , définissez chaque option à required, comme désiré, mais Lancez po::notify(vm) après avoir traité avec l'option d'aide. De cette façon, il sortira sans aucune exception jeté. Maintenant, avec les options définies à required, une option manquante provoquera une exception required_option et en utilisant sa méthode get_option_name vous pouvez réduire votre code d'erreur à un bloc catch relativement simple.

comme note supplémentaire, vos variables d'option sont définies directement via le mécanisme po::value< -type- >( &var_name ) , de sorte que vous n'avez pas à y accéder via vm["opt_name"].as< -type- >() .

90
répondu rcollyer 2017-05-23 10:31:33

voici le programme complet selon rcollyer et Tim, à qui les crédits vont:

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

bool process_command_line(int argc, char** argv,
                          std::string& host,
                          std::string& port,
                          std::string& configDir)
{
    int iport;

    try
    {
        po::options_description desc("Program Usage", 1024, 512);
        desc.add_options()
          ("help",     "produce help message")
          ("host,h",   po::value<std::string>(&host)->required(),      "set the host server")
          ("port,p",   po::value<int>(&iport)->required(),             "set the server port")
          ("config,c", po::value<std::string>(&configDir)->required(), "set the config path")
        ;

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

        if (vm.count("help"))
        {
            std::cout << desc << "\n";
            return false;
        }

        // There must be an easy way to handle the relationship between the
        // option "help" and "host"-"port"-"config"
        // Yes, the magic is putting the po::notify after "help" option check
        po::notify(vm);
    }
    catch(std::exception& e)
    {
        std::cerr << "Error: " << e.what() << "\n";
        return false;
    }
    catch(...)
    {
        std::cerr << "Unknown error!" << "\n";
        return false;
    }

    std::stringstream ss;
    ss << iport;
    port = ss.str();

    return true;
}

int main(int argc, char** argv)
{
  std::string host;
  std::string port;
  std::string configDir;

  bool result = process_command_line(argc, argv, host, port, configDir);
  if (!result)
      return 1;

  // else
  std::cout << "host:\t"   << host      << "\n";
  std::cout << "port:\t"   << port      << "\n";
  std::cout << "config:\t" << configDir << "\n";

  // Do the main routine here
}

/* Sample output:

C:\Documents and Settings\plee\My Documents\Visual Studio 2010\Projects\VCLearning\Debug>boost.exe --help
Program Usage:
  --help                produce help message
  -h [ --host ] arg     set the host server
  -p [ --port ] arg     set the server port
  -c [ --config ] arg   set the config path


C:\Documents and Settings\plee\My Documents\Visual Studio 2010\Projects\VCLearning\Debug>boost.exe
Error: missing required option config

C:\Documents and Settings\plee\My Documents\Visual Studio 2010\Projects\VCLearning\Debug>boost.exe --host localhost
Error: missing required option config

C:\Documents and Settings\plee\My Documents\Visual Studio 2010\Projects\VCLearning\Debug>boost.exe --config .
Error: missing required option host

C:\Documents and Settings\plee\My Documents\Visual Studio 2010\Projects\VCLearning\Debug>boost.exe --config . --help
Program Usage:
  --help                produce help message
  -h [ --host ] arg     set the host server
  -p [ --port ] arg     set the server port
  -c [ --config ] arg   set the config path


C:\Documents and Settings\plee\My Documents\Visual Studio 2010\Projects\VCLearning\Debug>boost.exe --host 127.0.0.1 --port 31528 --config .
host:   127.0.0.1
port:   31528
config: .

C:\Documents and Settings\plee\My Documents\Visual Studio 2010\Projects\VCLearning\Debug>boost.exe -h 127.0.0.1 -p 31528 -c .
host:   127.0.0.1
port:   31528
config: .
*/
34
répondu Peter Lee 2011-04-01 21:44:45

vous pouvez spécifier qu'une option est nécessaire assez facilement [ 1 ], par exemple:

..., value<string>()->required(), ...

mais pour autant que je sache, il n'y a aucun moyen de représenter les relations entre les différentes options de la bibliothèque program_options.

une possibilité est de parser la ligne de commande plusieurs fois avec des ensembles d'options différents, alors si vous avez déjà vérifié pour "l'aide" vous pouvez Parser à nouveau avec les trois autres options toutes définies comme requis. Je ne pense pas que ce soit une amélioration par rapport à ce que vous avez.

11
répondu Tim Sylvester 2011-04-01 18:27:05
    std::string conn_mngr_id;
    std::string conn_mngr_channel;
    int32_t priority;
    int32_t timeout;

    boost::program_options::options_description p_opts_desc("Program options");
    boost::program_options::variables_map p_opts_vm;

    try {

        p_opts_desc.add_options()
            ("help,h", "produce help message")
            ("id,i", boost::program_options::value<std::string>(&conn_mngr_id)->required(), "Id used to connect to ConnectionManager")
            ("channel,c", boost::program_options::value<std::string>(&conn_mngr_channel)->required(), "Channel to attach with ConnectionManager")
            ("priority,p", boost::program_options::value<int>(&priority)->default_value(1), "Channel to attach with ConnectionManager")
            ("timeout,t", boost::program_options::value<int>(&timeout)->default_value(15000), "Channel to attach with ConnectionManager")
        ;

        boost::program_options::store(boost::program_options::parse_command_line(argc, argv, p_opts_desc), p_opts_vm);

        boost::program_options::notify(p_opts_vm);

        if (p_opts_vm.count("help")) {
            std::cout << p_opts_desc << std::endl;
            return 1;
        }

    } catch (const boost::program_options::required_option & e) {
        if (p_opts_vm.count("help")) {
            std::cout << p_opts_desc << std::endl;
            return 1;
        } else {
            throw e;
        }
    }
1
répondu Edgard Lima 2016-02-10 19:08:30