C++ 使用 Boost 库程序选项的必需参数和可选参数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5395503/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 18:04:26  来源:igfitidea点击:

Required and Optional Arguments Using Boost Library Program Options

c++boostboost-program-optionsrequiredoptional

提问by Peter Lee

I'm using Boost Program Options Library to parse the command line arguments.

我正在使用 Boost 程序选项库来解析命令行参数。

I have the following requirements:

我有以下要求:

  1. Once "help" is provided, all the other options are optional;
  2. Once "help" is not provided, all the other options are required.
  1. 一旦提供了“帮助”,所有其他选项都是可选的;
  2. 一旦未提供“帮助”,则需要所有其他选项。

How I can deal with this? Here is the my code handling this, and I found it's very redundant, and I think there must be an easy to do, right?

我该如何处理?这是我处理这个的代码,我发现它非常多余,我认为一定很容易做到,对吧?

#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
}

回答by rcollyer

I've run into this issue myself. The key to a solution is that the function po::storepopulates the variables_mapwhile po::notifyraises any errors encountered, so vmcan be used prior to any notifications being sent.

我自己也遇到过这个问题。解决方案的关键是该函数po::store填充variables_mapwhilepo::notify引发遇到的任何错误,因此vm可以在发送任何通知之前使用。

So, as per Tim, set each option to required, as desired, but run po::notify(vm)after you've dealt with the help option. This way it will exit without any exceptions thrown. Now, with the options set to required, a missing option will cause a required_optionexception to be thrown and using its get_option_namemethod you can reduce your error code to a relatively simple catchblock.

因此,根据Tim,根据需要将每个选项设置为 required,但po::notify(vm)在处理了 help 选项后运行。这样它就会退出而不会抛出任何异常。现在,使用设置为所需的选项,缺少选项将导致required_option抛出异常并使用其get_option_name方法将错误代码减少到相对简单的catch块。

As an additional note, your option variables are set directly via the po::value< -type- >( &var_name )mechanism, so you don't have to access them through vm["opt_name"].as< -type- >().

作为附加说明,您的选项变量是通过po::value< -type- >( &var_name )机制直接设置的,因此您不必通过vm["opt_name"].as< -type- >().

A code exampleis provided in Peters answer

Peters answer 中提供了一个代码示例

回答by Peter Lee

Here is complete program as per rcollyer and Tim, whom the credits go to:

以下是 rcollyer 和 Tim 的完整程序,功劳归于:

#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:\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:\Debug>boost.exe
Error: missing required option config

C:\Debug>boost.exe --host localhost
Error: missing required option config

C:\Debug>boost.exe --config .
Error: missing required option host

C:\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:\Debug>boost.exe --host 127.0.0.1 --port 31528 --config .
host:   127.0.0.1
port:   31528
config: .

C:\Debug>boost.exe -h 127.0.0.1 -p 31528 -c .
host:   127.0.0.1
port:   31528
config: .
*/

回答by Tim Sylvester

You can specify that an option is required easily enough [1], e.g.,:

您可以很容易地指定需要一个选项 [ 1],例如:

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

but as far as I know there's no way to represent relationships between different options to the program_options library.

但据我所知,没有办法表示 program_options 库的不同选项之间的关系。

One possibility is to parse the command line multiple times with different option sets, then if you've already checked for "help" you can parse again with the three other options all set as required. I'm not sure I'd consider that an improvement over what you have, though.

一种可能性是使用不同的选项集多次解析命令行,然后如果您已经检查了“帮助”,您可以使用所有其他三个选项都按需要设置再次解析。不过,我不确定我会认为这比你现有的有所改进。

回答by Edgard Lima

    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;
        }
    }