it-swarm.com.de

Erforderliche und optionale Argumente unter Verwendung der Programmoptionen der Boost-Bibliothek

Ich verwende die Boost Program Options Library, um die Befehlszeilenargumente zu analysieren.

Ich habe folgende Anforderungen:

  1. Sobald "Hilfe" bereitgestellt wurde, sind alle anderen Optionen optional.
  2. Wenn "Hilfe" nicht bereitgestellt wird, sind alle anderen Optionen erforderlich.

Wie kann ich damit umgehen? Hier ist mein Code, der damit umgeht, und ich fand, dass er sehr redundant ist, und ich denke, es muss einfach sein, richtig?

#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
}
73
Peter Lee

Ich bin selbst auf dieses Problem gestoßen. Der Schlüssel zu einer Lösung besteht darin, dass die Funktion po::store Den variables_map Ausfüllt, während po::notify Alle aufgetretenen Fehler auslöst, sodass vm verwendet werden kann, bevor Benachrichtigungen gesendet werden geschickt.

Setzen Sie also gemäß Tim jede Option wie gewünscht auf erforderlich, aber führen Sie po::notify(vm) aus, nachdem Sie sich mit der Hilfeoption befasst haben. Auf diese Weise wird es ohne Ausnahmen beendet. Wenn die Optionen auf "Erforderlich" gesetzt sind, wird durch eine fehlende Option eine required_option - Ausnahme ausgelöst. Mit der get_option_name - Methode können Sie den Fehlercode auf "A" reduzieren relativ einfacher catch Block.

Als zusätzliche Anmerkung werden Ihre Optionsvariablen direkt über den Mechanismus po::value< -type- >( &var_name ) festgelegt, sodass Sie nicht über vm["opt_name"].as< -type- >() darauf zugreifen müssen.

93
rcollyer

Hier ist das komplette Programm nach rcollyer und Tim, an wen die Credits gehen:

#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: .
*/
35
Peter Lee

Sie können festlegen, dass eine Option einfach genug benötigt wird [ 1 ], z.

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

aber meines Wissens gibt es keine Möglichkeit, Beziehungen zwischen verschiedenen Optionen in der program_options-Bibliothek darzustellen.

Eine Möglichkeit besteht darin, die Befehlszeile mehrmals mit verschiedenen Optionssätzen zu analysieren. Wenn Sie bereits nach "Hilfe" gesucht haben, können Sie die anderen drei Optionen nach Bedarf erneut analysieren. Ich bin mir nicht sicher, ob ich das für eine Verbesserung gegenüber dem halten würde, was Sie haben.

12
Tim Sylvester
    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
Edgard Lima