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.
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.
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.
#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.