Boost Library 프로그램 옵션을 사용하는 필수 및 선택적 인수


83

Boost Program Options Library를 사용하여 명령 줄 인수를 구문 분석하고 있습니다.

다음과 같은 요구 사항이 있습니다.

  1. "도움말"이 제공되면 다른 모든 옵션은 선택 사항입니다.
  2. "도움말"이 제공되지 않으면 다른 모든 옵션이 필요합니다.

어떻게 처리 할 수 ​​있습니까? 이것을 처리하는 내 코드는 다음과 같습니다. 매우 중복 적이며 수행하기 쉬운 것이 있어야한다고 생각합니다.

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

답변:


103

이 문제를 직접 겪었습니다. 솔루션의 핵심은 함수 po::store가 오류를 발생 시키는 variables_map동안 채우 po::notify므로 vm알림을 보내기 전에 사용할 수 있다는 것입니다.

따라서 Tim 에 따라 원하는대로 각 옵션을 required로 설정하되 po::notify(vm) 도움말 옵션을 처리 한 후에 실행 하십시오. 이렇게하면 예외가 발생하지 않고 종료됩니다. 이제 옵션이 required로 설정된 상태에서 누락 된 옵션으로 인해 required_option예외가 발생하고 해당 get_option_name메서드를 사용 하면 오류 코드를 비교적 간단한 catch블록 으로 줄일 수 있습니다 .

추가로, 옵션 변수는 po::value< -type- >( &var_name )메커니즘을 통해 직접 설정되므로을 통해 액세스 할 필요가 없습니다 vm["opt_name"].as< -type- >().

코드 예제가 제공됩니다 피터스 답변


답장을 보내 주셔서 감사합니다. 예상대로 작동한다고 생각합니다. 또한 좋은 예가 필요한 사람들을 위해 아래에 전체 프로그램을 게시했습니다.
Peter Lee

5
훌륭한 솔루션! 공식 문서는 예를 들어 명확하게해야합니다.
russoue

@rcollyer 전체 작업 예제를 제공 할 수 있습니까?
조나스 스타 인

@JonasStein 할 수 있지만 Peter는 괜찮은 것 같습니다. 충분하지 않은 경우 알려주십시오.
rcollyer

1
@rcollyer sx 웹 사이트는 두 가지 답변을 시각적으로 연결하지 않아서 놓쳤습니다. 메모를 추가했습니다. 마음에 들지 않으면 되 돌리십시오.
Jonas Stein

46

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: .
*/

3
boost::program_options::required_option필요한 옵션의 부족을 직접 잡는 대신 직접 처리 할 수 ​​있도록 잡아야 합니다 std::exception.
rcollyer

포트는 서명되지 않은 유형이어야합니다.
g33kz0r

2
boost :: program_options :: error 이것을 잡아야합니다.
CreativeMind

13

옵션이 충분히 쉽게 필요하도록 지정할 수 있습니다 [ 1 ], 예 :

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

하지만 내가 아는 한 program_options 라이브러리에 대한 다른 옵션 간의 관계를 표현할 방법이 없습니다.

한 가지 가능성은 다른 옵션 세트를 사용하여 명령 줄을 여러 번 구문 분석하는 것입니다. "도움말"을 이미 확인한 경우 필요에 따라 설정 한 다른 세 가지 옵션으로 다시 구문 분석 할 수 있습니다. 나는 그것이 당신이 가진 것보다 개선되었다고 생각할지 확신하지 못합니다.


2
예, 내가 넣을 수있는 권한이 ->required()있지만 --help다른 옵션이 필요하기 때문에 사용자는 (다른 모든 필수 옵션을 제공하지 않고) 도움말 정보를 얻을 수 없습니다 .
Peter Lee

@Peter 처음 에는 도움 을 구할 뿐이고 다른 옵션은 목록에 없을 것입니다. 그런 다음 도움말 옵션을 전달하지 않은 경우에만 구문 분석을 다시 실행하고 이번에는 도움말이 아닌 필수로 설정된 다른 세 가지 옵션을 전달합니다 . 이 접근 방식은 사용 정보를 인쇄하는 데 사용하기 위해 세 번째 옵션 세트가 필요할 것입니다. 나는 그것이 작동 할 것이라고 확신하지만 rcollyer의 접근 방식은 더 깨끗합니다.
Tim Sylvester 2011

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

그것은 확실히 흥미로운 대안입니다. 그러나 그것은 당신이 코드를 처리하는 도움말을 반복하도록 강요하고, 작지만 나는 그것을 피하는 경향이 있습니다.
rcollyer
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.