2013-03-21 39 views
20

Chciałbym zaimplementować podkomendy do mojego programu. Potrzebuję również możliwości różnych opcji argumentów dla różnych podkomend. Jaki jest najlepszy sposób to zrobić przy użyciu Boost.Program_options?Jak zaimplementować podkomendy za pomocą opcji Boost.Program_oprogramowania?

Podkomisje są używane w programach takich jak svn, git i apt-get.

Na przykład w GIT niektóre z dostępnych podpoleceń są:

git status 
git push 
git add 
git pull 

Moje pytanie jest w zasadzie taki sam, jak ten facet: http://boost.2283326.n4.nabble.com/subcommands-with-program-options-like-svn-command-td2585537.html

Odpowiedz

38

Jeśli rozumiem problemu poprawnie, chcesz zanalizować opcji wiersza poleceń z poniższego formularza:

[--generic-option ...] cmd [--cmd-specific-option ... ] 

Oto mój przykład rozwiązanie. Dla jasności będę pomijał kod walidacyjny, ale mam nadzieję, że zobaczysz, jak zostałby dodany dość prosto.

W tym przykładzie mamy podkomendę "ls" i prawdopodobnie inne. Każda podkomenda ma określone opcje, a ponadto dostępne są opcje ogólne. Zacznijmy od analizowania ogólnych opcji i nazwy polecenia.

po::options_description global("Global options"); 
global.add_options() 
    ("debug", "Turn on debug output") 
    ("command", po::value<std::string>(), "command to execute") 
    ("subargs", po::value<std::vector<std::string> >(), "Arguments for command"); 

po::positional_options_description pos; 
pos.add("command", 1). 
    add("subargs", -1); 

po::variables_map vm; 

po::parsed_options parsed = po::command_line_parser(argc, argv). 
    options(global). 
    positional(pos). 
    allow_unregistered(). 
    run(); 

po::store(parsed, vm); 

Zauważ, że utworzyliśmy pojedynczą opcję pozycyjną dla nazwy polecenia oraz wiele opcji pozycyjnych dla opcji polecenia.

Teraz rozgałęziamy na odpowiednią nazwę polecenia i ponownie analizujemy. Zamiast podawać oryginalne argc i argv, teraz przekazujemy nierozpoznane opcje w postaci tablicy łańcuchów. Może to zapewnić funkcja collect_unrecognized - wystarczy, że usuniemy (pozycyjną) nazwę polecenia i ponownie przeanalizujemy odpowiedni numer options_description.

std::string cmd = vm["command"].as<std::string>(); 
if (cmd == "ls") 
{ 
    // ls command has the following options: 
    po::options_description ls_desc("ls options"); 
    ls_desc.add_options() 
     ("hidden", "Show hidden files") 
     ("path", po::value<std::string>(), "Path to list"); 

    // Collect all the unrecognized options from the first pass. This will include the 
    // (positional) command name, so we need to erase that. 
    std::vector<std::string> opts = po::collect_unrecognized(parsed.options, po::include_positional); 
    opts.erase(opts.begin()); 

    // Parse again... 
    po::store(po::command_line_parser(opts).options(ls_desc).run(), vm); 

Zauważ, że użyliśmy tego samego variables_map dla opcji polecenia specyficzne jak dla tych generycznych. Z tego możemy wykonać odpowiednie działania.

Fragmenty kodu pochodzą z kompilacyjnego pliku źródłowego zawierającego niektóre testy jednostkowe. Można go znaleźć na gist here. Pobierz i graj z nim.

+1

Doskonała odpowiedź, z pełnym przykładem do rozruchu. Dziękuję Ci! --DD – ddevienne

+0

Niesamowita odpowiedź! –

3

Można przyjąć nazwę podpolecenia z linii poleceń za pomocą pozycyjną opcje - patrz this tutorial.

Wygląda na to, że nie ma wbudowanej obsługi podpoleceń - musisz ustawić opcję allow_unregistered w parserze najwyższego poziomu, znaleźć nazwę polecenia, a następnie uruchomić ją za pomocą drugiego parsera, aby uzyskać podpolecenie -specyficzne opcje.

+2

Mam problem z uruchomieniem tego rozwiązania. W szczególności Boost wydaje się nie chcieć pozwolić, by cokolwiek nadejdzie * po * opcjach pozycyjnych. Tak więc, nawet przy allow_unregistered, boost narzeka, że ​​istnieje zbyt wiele opcji pozycyjnych (tj. "Zbyt wiele opcji pozycji zostało określonych w linii komend"), mimo że są to opcje nie-pozycyjne, które powinny być parsowane przez podrzędne dowództwo. – nomad

Powiązane problemy