Die Boost C++ Bibliotheken

Regeln

In Boost.Spirit bestehen Parser aus Regeln. Da Regeln üblicherweise auf von Boost.Spirit angebotenen Parsern basieren, fällt eine klare Unterscheidung schwer. So kann boost::spirit::ascii::digit sowohl Parser als auch Regel sein. Grundsätzlich werden unter Regeln jedoch komplexere Ausdrücke wie qi::int_ % ',' verstanden.

In allen bisherigen Beispielen wurden Regeln direkt an boost::spirit::qi::parse() oder boost::spirit::qi::phrase_parse() übergeben. Mit boost::spirit::qi::rule bietet Boost.Spirit eine Klasse an, die zur Regeldefinition verwendet werden kann. Sollen Ihre Regeln zum Beispiel Eigenschaften einer Klasse sein, müssen Sie boost::spirit::qi::rule verwenden.

Beispiel 11.13. Regeln mit boost::spirit::qi::rule definieren
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
#include <iostream>

using namespace boost::spirit;

int main()
{
  std::string s;
  std::getline(std::cin, s);
  auto it = s.begin();
  qi::rule<std::string::iterator, std::vector<int>(),
    ascii::space_type> values = qi::int_ % ',';
  std::vector<int> v;
  if (qi::phrase_parse(it, s.end(), values, ascii::space, v))
  {
    std::ostream_iterator<int> out{std::cout, ";"};
    std::copy(v.begin(), v.end(), out);
  }
}

Beispiel 11.13 funktioniert wie Beispiel 11.12: Wenn Sie das Programm ausführen und mehrere Ganzzahlen durch Kommas getrennt eingeben, werden diese durch Semikolons getrennt ausgegeben. Im Gegensatz zum vorherigen Beispiel wird der Parser nicht direkt an boost::spirit::qi::phrase_parse() übergeben, sondern über eine Variable vom Typ boost::spirit::qi::rule.

boost::spirit::qi::rule ist ein Template. Sie müssen als einzigen Parameter den Iteratortyp des Strings angeben, der geparst werden soll. Im Beispiel sind zwei zusätzliche Parameter angegeben, die optional sind.

Der zweite Template-Parameter std::vector<int>() ist die Signatur einer Funktion. Die Funktion gibt einen Vektor vom Typ std::vector<int> zurück und erwartet keinen Parameter. Dieser Template-Parameter besagt, dass der Typ des Attributs, das geparst wird, ein int-Vektor ist.

Der dritte Template-Parameter ist der Typ des Skippers, der von boost::spirit::qi::phrase_parse() verwendet wird, um Zeichen zu überspringen. Im Beispiel wird boost::spirit::ascii::space an boost::spirit::qi::phrase_parse() übergeben, um Leerzeichen zu ignorieren. Der Typ dieses Skippers kann über boost::spirit::ascii::space_type erhalten werden und wird entsprechend als dritter Parameter an boost::spirit::qi::rule übergeben.

Anmerkung

Wenn Sie plattformunabhängigen Code schreiben möchten und in einer C++11-Entwicklungsumgebung arbeiten, sollten Sie boost::spirit::qi::rule dem Schlüsselwort auto vorziehen. Wenn Sie die Variable values mit auto definieren, funktioniert das Beispiel mit GCC und Clang wie erwartet. Mit Visual C++ 2013 wird jedoch nur die erste eingegebene Zahl geparst und auf die Standardausgabe ausgegeben.

Beispiel 11.14. Regeln verschachteln
#include <boost/spirit/include/qi.hpp>
#include <boost/variant.hpp>
#include <string>
#include <vector>
#include <algorithm>
#include <iostream>

using namespace boost::spirit;

struct print : public boost::static_visitor<>
{
  template <typename T>
  void operator()(T t) const
  {
    std::cout << std::boolalpha << t << ';';
  }
};

int main()
{
  std::string s;
  std::getline(std::cin, s);
  auto it = s.begin();
  qi::rule<std::string::iterator, boost::variant<int, bool>(),
    ascii::space_type> value = qi::int_ | qi::bool_;
  qi::rule<std::string::iterator, std::vector<boost::variant<int, bool>>(),
    ascii::space_type> values = value % ',';
  std::vector<boost::variant<int, bool>> v;
  if (qi::phrase_parse(it, s.end(), values, ascii::space, v))
  {
    for (const auto &elem : v)
      boost::apply_visitor(print{}, elem);
  }
}

Im Beispiel 11.14 kommen zwei Regeln zum Einsatz, von denen die eine auf die andere verweist: Während values mit value % ',' definiert ist, lautet die Definition von value qi::int_ | qi::bool_. values gibt an, dass beliebig viele Werte mit Kommas getrennt angegeben werden können. value wiederum definiert einen Wert als Ganzzahl oder Wahrheitswert. Zusammengenommen bedeutet dies, dass Ganzzahlen und Wahrheitswerte durch Kommas getrennt in einer beliebigen Reihenfolge geparst werden können.

Um beliebig viele Werte speichern zu können, wird ein Container vom Typ std::vector bereitgestellt. Da die Werte entweder eine Ganzzahl vom Typ int oder ein Wahrheitswert vom Typ bool sind, wird ein entsprechender Typ benötigt, der int und bool vereint. Wie in der Übersicht zu Attribut-Typen und Operatoren erkennbar muss dazu auf die Klasse boost::variant von Boost.Variant zugegriffen werden.

Wenn Sie das Beispiel ausführen und Ganzzahlen und Wahrheitswerte durch Kommas getrennt eingeben, werden diese durch Semikolons getrennt auf die Standardausgabe ausgegeben. Dies geschieht mit Hilfe der Funktion boost::apply_visitor(), die von Boost.Variant angeboten wird. Diese Funktion erwartet einen Visitor. Zu diesem Zweck wurde die Klasse print definiert.

Beachten Sie, dass Sie Wahrheitswerte mit true und false eingeben müssen.