2016-06-23 9 views
2

Ich möchte mit Boost.Program_options ein Präfix-Programm wie strace erstellen. Präfix bedeutet, dass mein Programm vor eine andere beliebige command [args] gestellt wird. Daher sollte mein Programm eine Anzahl von Schlüsselwortargumenten/Flags akzeptieren, die definiert sind. Das erste Positionsargument gibt den Befehl an, dem ich vorangehe. Diesem Befehl kann selbst irgendeine Kombination von Parametern folgen, die ich nicht kenne und die sich mit den Parametern meines Programms überschneiden könnte. Deshalb ist dieses erste Positions Argument und alles, was kommt, nachdem es in einem am Ende soll std::vector<std::string>:Erstellen Sie einen Präfix-Befehl mit Boost.Program_options

./foo --bar 13 command1      # Should run fine 
./foo command2 positional     # Should run fine 
./foo --bar 13 command3 --unknown argument # Should run fine 
./foo --unknown command4      # should fail 
./foo --bar 13 command5 --bar 42    # Should work but set bar to 13 
./foo command6 --bar 42      # Should not set bar at all 
./foo --bar 13 -- command7 --bar 42 --unknown argument  # Should work 

Eine korrekte Verwendung ist es, zunächst eine des definierten Schlüsselwort angeben Grundsätzlich in einer korrekten Form, gibt es zunächst eine Reihe von meine definiert eine beliebige Anzahl von vordefinierten

ich habe versucht, zwei Ansätze nehmen sollten:

1) Mit allow_unregistered:

#include <iostream> 
#include <boost/program_options.hpp> 

namespace po = boost::program_options; 

int main(int argc, const char** argv) 
{ 
    int bar = 0; 
    po::options_description desc("Allowed options"); 

    desc.add_options() 
      ("bar", po::value(&bar), "bar"); 


    po::variables_map vm; 
    po::parsed_options parsed = 
     po::command_line_parser(argc, argv).options(desc).allow_unregistered().run(); 
    po::store(parsed, vm); 
    po::notify(vm); 

    auto command = po::collect_unrecognized(parsed.options, po::include_positional); 
    std::cout << "bar: " << bar << ", command:"; 
    for (const auto& c : command) std::cout << " " << c; 
    std::cout << std::endl; 
} 

Dies schlägt fehl für Befehle 4,5,6

2) Eine Positionsmöglichkeiten mit unbegrenztem Vorkommen

std::vector<std::string> command; 

desc.add_options() 
     ("bar", po::value(&bar), "bar") 
     ("command", po::value(&command)); 

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

po::variables_map vm; 
po::parsed_options parsed = 
    po::command_line_parser(argc, argv).options(desc).positional(p).run(); 
po::store(parsed, vm); 
po::notify(vm); 

Diese 3,5,6 für Befehle fehlschlägt.

+0

Bitte beschreiben Sie genauer, wie es funktionieren soll und welche Kombinationen erlaubt und nicht erlaubt sind, es ist nicht klar aus diesen 7 Beispielen – alexeykuzmin0

+0

@ alexeykuzmin0 siehe mein Update: "Präfix bedeutet, dass mein Programm vor ein anderes willkürlich gestellt wird 'command [args]'. Daher sollte mein Programm eine Anzahl von Schlüsselwortargumenten/Flags akzeptieren, die definiert sind.Das erste Positionsargument gibt den Befehl an, dem ich vorangehe.Diesem Befehl kann selbst irgendeine Kombination von Parametern folgen, die ich tue nicht kennen und könnten sich mit den Parametern meines Programms überschneiden, daher sollte dieses erste Positionsargument und alles, was danach kommt, in einem 'std :: vector ' enden. Die Beispiele repräsentieren jeweils spezifische Eckfälle. – Zulan

+0

Können die Argumente Leerzeichen enthalten? Sind die Argumente in diesem Fall? – alexeykuzmin0

Antwort

2

Es gibt eine Feature-Anforderung für dieses auf Boost Trac, einschließlich Patches: https://svn.boost.org/trac/boost/ticket/6991. Es gab nicht viel Bewegung auf dem Ticket, aber der Patch gilt immer noch sauber für Boost (ab 1.61.0, der neuesten Version).

Wenn Ihr Build-System es erlaubt, können Sie den Patch auf Ihre lokale Kopie von Boost anwenden; Andernfalls können Sie boost::program_options::detail::cmdline aus cmdline.hpp, detail/cmdline.hpp und cmdline.cpp in Ihren eigenen Namespace extrahieren, diese Komponente patchen und Ihre gepatchte Komponente anstelle von boost::program_options::cmdline verwenden.

Eine weitere Option ist das Verhalten von boost::program_options::detail::cmdline zu hacken, mit seinen extra_style_parser Parameter:

po::detail::cmdline cmdline(argc, argv); 
cmdline.set_options_description(desc); 
cmdline.set_positional_options(p); 
std::vector<po::detail::cmdline::style_parser> style_parsers{ 
    [&](auto& args) { return cmdline.parse_long_option(args); }, 
    [&](auto& args) { return cmdline.parse_short_option(args); }}; 
cmdline.extra_style_parser([&](std::vector<std::string>& args) { 
    auto const current_size = args.size(); 
    std::vector<po::option> result; 
    for (auto const& parser : style_parsers) { 
     auto const next = parser(args); 
     result.insert(result.end(), next.begin(), next.end()); 
     if (args.size() != current_size) 
      return result; 
    } 
    if (args.size() && args[0] != "--") args.insert(args.begin(), "--"); 
    auto const next = cmdline.parse_terminator(args); 
    result.insert(result.end(), next.begin(), next.end()); 
    return result; 
}); 
po::parsed_options parsed{&desc}; 
parsed.options = cmdline.run(); 
po::store(parsed, vm); 
po::notify(vm); 

Example. Warnung: Dies ist Hacking mit undokumentierten Bibliothek Interna und konnte jederzeit brechen.

+0

Sieht so aus, als würde ich genau das brauchen. Es ist traurig zu sehen, dass dieser Patch nie abgehoben hat. Ich werde nachsehen, ob es für mich funktioniert. – Zulan