2013-08-22 2 views
5

Ich möchte eine Befehlszeilen-ähnliche Schnittstelle in meinem Programm implementieren. Daher erhalte ich Zeichenfolgen, die der normalen Befehlszeilensyntax folgen (z. B. "-G foo -dp bar --help"). Da ich den Parser nicht mehr implementieren möchte, möchte ich Boost verwenden.Verwenden Sie Boost Program Options, um eine beliebige Zeichenfolge zu analysieren

Die Frage ist: Wie kann ich eine Zeichenfolge zu Boost Programmoptionen statt einer Kombination von argCount und argValues ​​übergeben. Muss ich zuerst den Text in eine Zahl (argCount) und ein char * -Array (argValues) umwandeln? Und wenn ja ... gibt es einen einfachen Weg dies zu tun?

Vielen Dank im Voraus.

+0

warum würden Sie eine Zeichenfolge übergeben? Sie erhalten die Optionen als Char ** bereits in Ihrem C++ Programm? – codeling

+0

Ich verwende UNIX-Sockets (asio :: local), um eine std :: string zu übergeben. Jetzt möchte ich diese Zeichenfolge mit den Programmoptionen analysieren. Das Problem ist, dass das Beispiel nur po :: parse_command_line (ac, av, desc) enthält, aber ich habe keine av. Ich habe eine vollständige Zeichenfolge, die die Argumente enthält. – Darneas

+0

@codeling Weil ich einen Komponententest schreibe. – Eyal

Antwort

7

Ein Ansatz besteht darin, std::string in eine std::vector<std::string> zu tokenieren und dann das Ergebnis an Boost.ProgramOptions command_line_parser weiterzuleiten. Die documentation von Boost.ProgramOption deckt diesen Ansatz kurz ab. Zusätzlich verwende ich einen ähnlichen Ansatz in Teilen der Antwort.

Hier ist ein minimal komplettes Beispiel:

#include <algorithm> 
#include <iostream> 
#include <iterator> 
#include <string> 
#include <vector> 

#include <boost/bind.hpp> 
#include <boost/program_options.hpp> 
#include <boost/tokenizer.hpp> 

// copy_if was left out of the C++03 standard, so mimic the C++11 
// behavior to support all predicate types. The alternative is to 
// use remove_copy_if, but it only works for adaptable functors. 
template <typename InputIterator, 
      typename OutputIterator, 
      typename Predicate> 
OutputIterator 
copy_if(InputIterator first, 
     InputIterator last, 
     OutputIterator result, 
     Predicate pred) 
{ 
    while(first != last) 
    { 
    if(pred(*first)) 
     *result++ = *first; 
    ++first; 
    } 
    return result; 
} 

/// @brief Tokenize a string. The tokens will be separated by each non-quoted 
///  space or equal character. Empty tokens are removed. 
/// 
/// @param input The string to tokenize. 
/// 
/// @return Vector of tokens. 
std::vector<std::string> tokenize(const std::string& input) 
{ 
    typedef boost::escaped_list_separator<char> separator_type; 
    separator_type separator("\\", // The escape characters. 
          "= ", // The separator characters. 
          "\"\'"); // The quote characters. 

    // Tokenize the intput. 
    boost::tokenizer<separator_type> tokens(input, separator); 

    // Copy non-empty tokens from the tokenizer into the result. 
    std::vector<std::string> result; 
    copy_if(tokens.begin(), tokens.end(), std::back_inserter(result), 
      !boost::bind(&std::string::empty, _1)); 
    return result; 
} 

int main() 
{ 
    // Variables that will store parsed values. 
    std::string address; 
    unsigned int port;  

    // Setup options. 
    namespace po = boost::program_options; 
    po::options_description desc("Options"); 
    desc.add_options() 
    ("address", po::value<std::string>(&address)) 
    ("port", po::value<unsigned int>(&port)) 
    ; 

    // Mock up input. 
    std::string input = "--address 127.0.0.1 --port 12345"; 

    // Parse mocked up input. 
    po::variables_map vm; 
    po::store(po::command_line_parser(tokenize(input)) 
       .options(desc).run(), vm); 
    po::notify(vm); 

    // Output. 
    std::cout << "address = " << address << "\n" 
       "port = " << port << std::endl; 
} 

, die die folgende Ausgabe erzeugt:

address = 127.0.0.1 
port = 12345 
+0

Wir brauchen kein eigenes Tokenize unter Linux, benutze Boost split_unix. – FaceBro