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.