बूस्ट लाइब्रेरी प्रोग्राम विकल्पों का उपयोग करके आवश्यक और वैकल्पिक तर्क


83

मैं कमांड लाइन तर्कों को पार्स करने के लिए Boost Program Options लाइब्रेरी का उपयोग कर रहा हूं।

मेरी निम्नलिखित ज़रूरतें हैं:

  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किसी भी सूचना को भेजे जाने से पहले उपयोग किया जा सकता है।

इसलिए, टिम के अनुसार , वांछित के रूप में प्रत्येक विकल्प को सेट करें, लेकिन po::notify(vm) मदद विकल्प से निपटने के बाद चलाएं । इस तरह यह बिना किसी अपवाद के बाहर निकल जाएगा। अब, आवश्यक विकल्पों के साथ, एक लापता विकल्प एक required_optionअपवाद को फेंक देगा और इसकी get_option_nameविधि का उपयोग करके आप अपने त्रुटि कोड को अपेक्षाकृत सरल catchब्लॉक में कम कर सकते हैं ।

एक अतिरिक्त नोट के रूप में, आपके विकल्प चर सीधे po::value< -type- >( &var_name )तंत्र के माध्यम से सेट किए जाते हैं , इसलिए आपको उनके माध्यम से एक्सेस नहीं करना पड़ता है vm["opt_name"].as< -type- >()

पीटर्स उत्तर में एक कोड उदाहरण दिया गया है


आपके जवाब के लिए धन्यवाद। मुझे लगता है कि यह उम्मीद के मुताबिक काम करता है। मैंने नीचे उन लोगों के लिए पूरा कार्यक्रम भी पोस्ट किया है जिन्हें एक अच्छे उदाहरण की आवश्यकता है।
पीटर ली

5
उत्कृष्ट समाधान! आधिकारिक दस्तावेज को एक उदाहरण के साथ स्पष्ट करना चाहिए।
रुसौई

@rcollyer क्या आप एक पूर्ण कार्य उदाहरण प्रदान कर सकते हैं, कृपया?
जोनास स्टीन

@JonasStein मैं कर सकता था, लेकिन पीटर का प्रतीत होना ठीक है। मुझे पता है कि अगर अपर्याप्त है।
rcollyer

1
@rcollyer sx वेबसाइट नेत्रहीन रूप से दो उत्तरों को नहीं जोड़ती है, इसलिए मैं चूक गया। मैंने एक नोट जोड़ा है। कृपया वापस लौटें, यदि आप इसके साथ सहज नहीं हैं।
जोनास स्टीन

46

यहां पूरा कार्यक्रम रैलर और टिम के अनुसार है, जिनके लिए क्रेडिट यहां जाता है:

#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
आपको बढ़ावा देना चाहिए :: program_options :: यह केवल त्रुटि।
क्रिएटिवमाइंड

13

आप यह निर्दिष्ट कर सकते हैं कि एक विकल्प की आवश्यकता आसानी से पर्याप्त है [ 1 ], जैसे:

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

लेकिन जहां तक ​​मुझे पता है कि program_options लाइब्रेरी में विभिन्न विकल्पों के बीच संबंधों का प्रतिनिधित्व करने का कोई तरीका नहीं है।

एक संभावना यह है कि कमांड लाइन को अलग-अलग विकल्प सेटों के साथ कई बार पार्स किया जाए, फिर यदि आपने "सहायता" के लिए पहले से ही जाँच कर ली है, तो आप तीन अन्य विकल्पों के साथ आवश्यकतानुसार फिर से पार्स कर सकते हैं। मुझे यकीन नहीं है कि मैं इस पर विचार करूंगा कि आपके पास क्या है, हालांकि इसमें सुधार होगा।


2
हां, वह सही है जो मैं डाल सकता हूं ->required(), लेकिन तब उपयोगकर्ता द्वारा --help(अन्य सभी आवश्यक विकल्प प्रदान किए बिना) सहायता की जानकारी नहीं मिल सकती है , क्योंकि अन्य विकल्पों की आवश्यकता है।
पीटर ली

@ पेटर आप पहली बार केवल मदद के लिए देखेंगे , अन्य विकल्प भी सूची में नहीं होंगे। फिर, अगर वे मदद के विकल्प में पास नहीं हुए, तो ही आप फिर से पार्सिंग चलाएंगे, इस बार अन्य तीन विकल्पों में पास करना, आवश्यक करना और मदद नहीं करना। इस दृष्टिकोण को संभवतः उपयोग जानकारी मुद्रित करने के लिए, उन सभी के साथ, विकल्पों में से तीसरे सेट की आवश्यकता होगी। मुझे पूरा यकीन है कि यह काम करेगा, लेकिन rcollyer का दृष्टिकोण क्लीनर है।
टिम सिल्वेस्टर

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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.