Hypercell ein ] Hypercell aus ] Zeige Navigation ] Verstecke Navigation ]
c++.de  
   
Forentreff 2012     
Bücher-Shop mit Amazon (Buchkategorien)C++ : Referenzen zu C++ : C++ Builder : Visual C++ : C# : Java : Spieleprogrammierung : Systemprogrammierung Linux : Software-Entwicklung : .NET : Compilertechnik : Algorithmen & Datenstrukturen : Objektorientierung : Entwurfsmuster : UML : eXtreme Programming : Scrum : Projektmanagement : Software-Testing : Datenbanken : Tom DeMarco : Dilbert : User Friendly
C/C++ Forum :: Die Artikel ::  Boost::Spirit - Eine Einführung     Zeige alle Beiträge auf einer Seite Thema geschlossen
Autor Nachricht
Tobias Gerg
Autor

Benutzerprofil
Anmeldungsdatum: 29.10.2001
Beiträge: 348
Beitrag Tobias Gerg Autor 13:22:21 06.03.2006   Titel:   Boost::Spirit - Eine Einführung            Zitieren

1 Grundlegendes

Dieser Artikel soll Sie in die Welt von Boost::Spirit einführen. Boost::Spirit ist ein Parser-Generator-Framework, das in C++ realisiert ist. Um die folgenden Beispiele kompilieren zu können, ist Boost::Spirit ab der Version 1.6.3 zu verwenden. Des Weiteren wird ein ISO C++-Compiler (gcc, Visual C++ (ab Version 7.1), ...) benötigt.

Anmerkung: Probleme bereiten unter anderem MS Visual C++ 6.0 und Open Watcom 1.3.

1.1 Von Dateien und anderen zu durchsuchenden Strukturen

Ein klassisches Beispiel für die Verwendung eines Parsers ist die Dateiverarbeitung. Wie oft kommt es vor, dass man ein bestimmtes Format oder eine bestimmte Struktur zu durchsuchen hat und keine geeigneten Klassen oder Strukturen zur Verfügung stehen.
XML-Dateien werden etwa anders verarbeitet als INI-Dateien. Mathematische Eingaben sollen anders interpretiert werden als bestimmte Stringfolgen. All das kennt man mittlerweile als Programmierer zur Genüge.
Auf diesem Gebiet hat Boost::Spirit seine unverkennbaren Stärken. Durch die einfache Definition von Regeln ist in null Komma nichts ein Parser entwickelt, der die Arbeit von alleine erledigt. Ob dabei zeilenweise durch eine Datei geparst wird oder mathematische Ausdrücke geparst werden, spielt keine große Rolle. Boost::Spirit kann in diese Richtung fast alles handhaben.



2 Installation von Boost::Spirit

Um das Framework von Boost::Spirit nutzen zu können, ist es erforderlich, die Header-Dateien bekannt zu machen. Am einfachsten ist es, den Ordner boost aus dem Verzeichnis spirit-1.6.3\boost sowie aus dem Verzeichnis spirit-1.6.3\boost\miniboost in das Include-Verzeichnis des Compilers / der Entwicklungsumgebung zu kopieren.
In ein Projekt müssen unbedingt folgende Zeilen eingebunden werden.

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
1
2
3
4
5
6
7
8
9
10
11
// ...
#include
<boost/spirit.hpp>
/*****************************************************************************/
/* Die obige Include Zeile ist nur zu verwenden, falls die Include-Dateien   */
/* systemweit bekannt gemacht wurden...                                      */
/* Falls Sie sich im Projektverzeichnis befinden, bitte                      */
/* #include "boost/spirit.hpp"                                               */
/* einbinden.                                                                */
/*****************************************************************************/
// ...

using namespace boost::spirit
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
// ...
#include
<boost/spirit.hpp>
/*****************************************************************************/
/* Die obige Include Zeile ist nur zu verwenden, falls die Include-Dateien */
/* systemweit bekannt gemacht wurden... */
/* Falls Sie sich im Projektverzeichnis befinden, bitte */
/* #include "boost/spirit.hpp" */
/* einbinden. */
/*****************************************************************************/
// ...

using namespace boost::spirit
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
// ...
#include
<boost/spirit.hpp>
/*****************************************************************************/
/* Die obige Include Zeile ist nur zu verwenden, falls die Include-Dateien   */
/* systemweit bekannt gemacht wurden...                                      */
/* Falls Sie sich im Projektverzeichnis befinden, bitte                      */
/* #include "boost/spirit.hpp"                                               */
/* einbinden.                                                                */
/*****************************************************************************/
// ...

using namespace boost::spirit


2.1 Theoretische Informatik

Für die folgenden Beispiele ist es vielleicht ganz interessant, sich etwas in die theoretische Informatik einzulesen.

Ein PDF das eine kurze Einführung in die Theoretische Informatik bietet, wird in Kürze nachgeliefert.



3 Die Grundlagen für Parseaktionen im Framework

Der Datentyp, der in den nächsten Beispielen immer wieder vorkommen wird, ist der rule<>-Datentyp. Mit diesem Datentyp baut man Parser auf. Spirit stellt von sich aus schon diverse Parser zur Verfügung, von denen ich einige in diesem Kapitel vorstellen möchte.

3.1 Die Literale strlit<> und chlit<>

C/C++ Code:
// ...
rule<> ch_A = chlit<>('A');
rule<> str_Hallo = strlit<>("Hallo");
// ...
C/C++ Code:
// ...
rule<> ch_A = chlit<>('A');
rule<> str_Hallo = strlit<>("Hallo");
// ...
C/C++ Code:
// ...
rule<> ch_A = chlit<>('A');
rule<> str_Hallo = strlit<>("Hallo");
// ...


Wie schon zu vermuten ist, werden hier ein Character Literal und ein String Literal erstellt. Es ist durchaus möglich, auch Variablen in der Regel anzugeben wie das folgende Beispiel zeigt.
C/C++ Code:
// ...
string myString = "Test";
rule<> str_Variabel = strlit<>(myString.c_str());  //char* übergeben
// Alternativ

rule<> str_Variable2 = strlit<string::const_iterator>(myString.begin(), myString.end());
// ...
C/C++ Code:
// ...
string myString = "Test";
rule<> str_Variabel = strlit<>(myString.c_str()); //char* übergeben
// Alternativ

rule<> str_Variable2 = strlit<string::const_iterator>(myString.begin(), myString.end());
// ...
C/C++ Code:
// ...
string myString = "Test";
rule<> str_Variabel = strlit<>(myString.c_str());  //char* übergeben
// Alternativ

rule<> str_Variable2 = strlit<string::const_iterator>(myString.begin(), myString.end());
// ...


3.2 Ranges

Um einen Bereich abzudecken, gibt es den range<>-Parser.
C/C++ Code:
//...
rule<> rng = range<>('A', 'Z');
//...
C/C++ Code:
//...
rule<> rng = range<>('A', 'Z');
//...
C/C++ Code:
//...
rule<> rng = range<>('A', 'Z');
//...

Die Regel rng deckt die Buchstaben von 'A' bis 'Z' ab.

3.3 Zahlen

Es gibt in Spirit diverse Zahlenparser. Einer der wichtigeren ist wohl der Integerparser.
C/C++ Code:
//...
rule<> integer = int_p;
//...
C/C++ Code:
//...
rule<> integer = int_p;
//...
C/C++ Code:
//...
rule<> integer = int_p;
//...

Erlaubt eine beliebige Ganzzahl.

3.4 Operatoren

Es gibt in Spirit eine Reihe von Operatoren zum Verknüpfen von Regeln. Dazu gehören unter anderem
Code:
<a> | <b>    // <a> oder <b> oder beide werden gematched
<a> - <b>    // <a> ohne <b>
<a> >> <b>    // <a> gefolgt von <b>
!<a>        // 0 oder 1 mal <a>
*<a>        // 0 oder beliebig oft mal <a>
+<a>        // 1 oder beliebig oft mal <a>
~<a>        // nicht <a>
Code:
<a> | <b> // <a> oder <b> oder beide werden gematched
<a> - <b> // <a> ohne <b>
<a> >> <b> // <a> gefolgt von <b>
!<a> // 0 oder 1 mal <a>
*<a> // 0 oder beliebig oft mal <a>
+<a> // 1 oder beliebig oft mal <a>
~<a> // nicht <a>
Code:
<a> | <b>    // <a> oder <b> oder beide werden gematched
<a> - <b>    // <a> ohne <b>
<a> >> <b>    // <a> gefolgt von <b>
!<a>        // 0 oder 1 mal <a>
*<a>        // 0 oder beliebig oft mal <a>
+<a>        // 1 oder beliebig oft mal <a>
~<a>        // nicht <a>


Weiterführendes Material findet sich unter der Spirit Doku.



4 Boost::Spirit anhand eines INI-Datei-Parsers

Zur Erläuterung der Funktionsweise von Boost::Spirit werden wir uns im Folgenden einen INI-Fileparser zusammenbauen. Dazu definieren wir das Aussehen einer INI-Datei.
Definition:
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[Sektionsname{1}]
variable{1}=wert{1}
variable{2}=wert{2}
.
.
.
variable{n}=wert{n}
[Sektionsname{2}]
variable{1}=wert{1}
variable{2}=wert{2}
.
.
.
variable{n}=wert{n}
.
.
.
[Sektionsname{n}]
variable{1}=wert{1}
variable{2}=wert{2}
.
.
.
variable{n}=wert{n}
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[Sektionsname{1}]
variable{1}=wert{1}
variable{2}=wert{2}
.
.
.
variable{n}=wert{n}
[Sektionsname{2}]
variable{1}=wert{1}
variable{2}=wert{2}
.
.
.
variable{n}=wert{n}
.
.
.
[Sektionsname{n}]
variable{1}=wert{1}
variable{2}=wert{2}
.
.
.
variable{n}=wert{n}
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[Sektionsname{1}]
variable{1}=wert{1}
variable{2}=wert{2}
.
.
.
variable{n}=wert{n}
[Sektionsname{2}]
variable{1}=wert{1}
variable{2}=wert{2}
.
.
.
variable{n}=wert{n}
.
.
.
[Sektionsname{n}]
variable{1}=wert{1}
variable{2}=wert{2}
.
.
.
variable{n}=wert{n}


Für diese Definition überlegen wir uns im Folgenden Regeln, die wir in Spirit abbilden und die uns das Parsen erlauben.

Es folgen die Regeln abgebildet in Spirit.
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// ...
int main(int argc, char** argv)
{
   
    rule<> _VALUE = *(anychar_p - eol_p - end_p);                          //[1]
    rule<> _VAR = +(anychar_p - space_p - chlit<char>('='));               //[2]
    rule<> _VAR_DECLARATION = _VAR >> chlit<char>('=') >> _VALUE >> *eol_p;//[3]
    rule<> _SECTION = chlit<char>('[') >> *(anychar_p - chlit<char>(']'))  
                      >> chlit<char>(']') >> *eol_p;                      
    rule<> _INI_ROW = _SECTION | _VAR_DECLARATION | eol_p;                 //[4]
   
    // ...

   
    if(parse(<zeile aus der .ini Datei>, _INI_ROW).full)                   //[5]
    {
        cout << "Valid";
    }
   
    // ...
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// ...
int main(int argc, char** argv)
{

rule<> _VALUE = *(anychar_p - eol_p - end_p); //[1]
rule<> _VAR = +(anychar_p - space_p - chlit<char>('=')); //[2]
rule<> _VAR_DECLARATION = _VAR >> chlit<char>('=') >> _VALUE >> *eol_p;//[3]
rule<> _SECTION = chlit<char>('[') >> *(anychar_p - chlit<char>(']'))
>> chlit<char>(']') >> *eol_p;
rule<> _INI_ROW = _SECTION | _VAR_DECLARATION | eol_p; //[4]

// ...


if(parse(<zeile aus der .ini Datei>, _INI_ROW).full) //[5]
{
cout << "Valid";
}

// ...
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// ...
int main(int argc, char** argv)
{
   
    rule<> _VALUE = *(anychar_p - eol_p - end_p);                          //[1]
    rule<> _VAR = +(anychar_p - space_p - chlit<char>('='));               //[2]
    rule<> _VAR_DECLARATION = _VAR >> chlit<char>('=') >> _VALUE >> *eol_p;//[3]
    rule<> _SECTION = chlit<char>('[') >> *(anychar_p - chlit<char>(']'))  
                      >> chlit<char>(']') >> *eol_p;                      
    rule<> _INI_ROW = _SECTION | _VAR_DECLARATION | eol_p;                 //[4]
   
    // ...

   
    if(parse(<zeile aus der .ini Datei>, _INI_ROW).full)                   //[5]
    {
        cout << "Valid";
    }
   
    // ...
}



Erklärung:
C/C++ Code:
rule<> Variablename = Zuweisung
C/C++ Code:
rule<> Variablename = Zuweisung
C/C++ Code:
rule<> Variablename = Zuweisung


Der rule<> Datentyp
Der Variablentyp rule<> ist einer der wichtigsten Datentypen, wenn man mit Boost::Spirit in Kontakt kommt. Der rule<>-Datentyp legt das "Aussehen" eines Parseausdruck fest.

Zu [1]:
Es wird die Regel _VALUE definiert. Wir legen fest, das _VALUE jedes Zeichen außer ein "End of Line" und ein "Eingabe Ende" beinhalten darf. Der * vor der Klammer ist der so genannte "Kleene Star" und besagt, das 0 bis beliebig viele Zeichen vorkommen können.

Zu [2]:
Die Regel _VAR legt fest, dass vor dem '=' mindestens ein Zeichen (+) stehen muss, das kein '=' enthalten darf. Des Weiteren dürfen keine Leerzeichen, keine Returns, keine Whitespaces und derartige Dinge vorkommen (space_p-Parser).

Zu [3]:
In der Regel _VAR_DECLARATION haben wir unser erstes "gefolgt von"-Zeichen. Wir definieren, dass eine Variablen-Deklaration wie folgt aussieht. Die Regel _VAR gefolgt von (>>) einem Character Literal ('=') gefolgt von (>>) der Regel _VALUE gefolgt von (>>) einem möglichen "End of Line".

Zu [4]:
Die Regel _INI_ROW ist unsere so genannte "Startregel". Sie ist es, die die Parse-Funktion bedient. In ihr legen wir fest, wie eine Zeile in dem INI-File auszusehen hat. Entweder es kommt eine _SECTION oder (|) eine _VAR_DECLARATION oder (|) ein "End of Line".

Zu [5]:
Die Funktion parse liefert ein Parse-Ergebnis, auf das wir später noch genauer eingehen werden. Vorerst ist es nur wichtig zu wissen, dass die parse-Funktion in unserem Beispiel zwei Argumente benötigt. Nämlich den zu parsenden Ausdruck und die "Startregel". Das .full zeigt uns, ob wir den Ausdruck komplett durchlaufen haben. Ist das geschehen, geben wir "Valid" aus.

Zusammenfassend zu diesem Beispiel:

  • Der Datentyp rule<> als einer der wichtigsten Datentypen in Boost::Spirit.
  • Es gibt verschiedene vorgefertigte Parser wie eol_p, space_p, anychar_p ...
  • Wichtige Operatoren sind: *, +, |, >>
  • Die parse-Funktion übernimmt die Arbeit und liefert als Ergebnis einen "true" oder "false" Wert.



5 Die Parse-Funktion

Wie im obigen Beispiel gesehen, ist es die parse-Funktion, die den eigentlichen Parsevorgang übernimmt. Diese Funktion liefert in Wirklichkeit mehr als nur einen boolschen Wert. Der Rückgabewert ist eine Variable vom Typ parse_info.
Die Struktur enthält folgende Membervariablen:

  • stop
  • hit
  • full
  • length


Über diese Variablen lassen sich einige Informationen extrahieren, die Aufschluss über den Parsevorgang geben.


  • Über stop findet man die Position im geparsten Ausdruck, wo aufgehört wurde zu parsen.

  • hit ist true oder false. Sprich entweder wurde mindestens ein Teilausdruck geparst oder es wurde kein einziger Ausdruck geparst.

  • full ist ebenfalls true oder false. Es sagt aus, ob der ganze Parseausdruck geparst wurde oder nicht.

  • length gibt die Anzahl der Zeichen an, die geparst wurden.


Bsp:
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ...

// Eine Regel, die eine Liste von Zahlen getrennt durch Kommas liest

rule<> r = int_p >> *(chlit<char>(',') >> int_p);

// Das ParseInfo-Objekt
parse_info<> pI;

// Parsen eines Ausdrucks, der nicht nur Zahlen enthält
pI = parse("1,2,3,a,b,0", r);

// Ausgabe der Werte von ParseInfo
std::cout << pI.stop << std::endl
          << pI.full << std::endl
          << pI.hit << std::endl
          << pI.length << std::endl;

// ...
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ...

// Eine Regel, die eine Liste von Zahlen getrennt durch Kommas liest

rule<> r = int_p >> *(chlit<char>(',') >> int_p);

// Das ParseInfo-Objekt
parse_info<> pI;

// Parsen eines Ausdrucks, der nicht nur Zahlen enthält
pI = parse("1,2,3,a,b,0", r);

// Ausgabe der Werte von ParseInfo
std::cout << pI.stop << std::endl
<< pI.full << std::endl
<< pI.hit << std::endl
<< pI.length << std::endl;

// ...
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ...

// Eine Regel, die eine Liste von Zahlen getrennt durch Kommas liest

rule<> r = int_p >> *(chlit<char>(',') >> int_p);

// Das ParseInfo-Objekt
parse_info<> pI;

// Parsen eines Ausdrucks, der nicht nur Zahlen enthält
pI = parse("1,2,3,a,b,0", r);

// Ausgabe der Werte von ParseInfo
std::cout << pI.stop << std::endl
          << pI.full << std::endl
          << pI.hit << std::endl
          << pI.length << std::endl;

// ...


Die Ausgabe des Programms
Code:
,a,b,0      // Hier wurde aufgehört zu parsen
0           // Ausdruck nicht vollständig geparst
1           // Teilausdruck wurde erkannt
5           // 5 Zeichen wurden geparst
Code:
,a,b,0 // Hier wurde aufgehört zu parsen
0 // Ausdruck nicht vollständig geparst
1 // Teilausdruck wurde erkannt
5 // 5 Zeichen wurden geparst
Code:
,a,b,0      // Hier wurde aufgehört zu parsen
0           // Ausdruck nicht vollständig geparst
1           // Teilausdruck wurde erkannt
5           // 5 Zeichen wurden geparst


Mit diesen Angaben kann man unter anderem einschränken, wo der zu parsende Ausdruck "fehlerhaft" war.



6 Anzeigen von Daten aus dem Parsevorgang mit Funktionen

Nun ist es ja gut und schön, wenn man weiß, dass ein zu parsender Ausdruck den Regeln entspricht. Aber wie an die Daten kommen, die in dem Ausdruck vorhanden sind?

Dafür gibt es in Boost::Spirit unter anderem Funktionen, die im Grundgerüst wie folgt definiert sind.

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
template<IteratorT>
void <funktionsname>(IteratorT begin, IteratorT end)
{
  // Verarbeitung der Iteratoren
}


// oder falls nur ein Iterator zur Verfügung steht, z.B. bei Zahlenparsern von Spirit
void <funktionsname>(IteratorT begin)
{
  // Verarbeitung des Iterators
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
template<IteratorT>
void <funktionsname>(IteratorT begin, IteratorT end)
{
// Verarbeitung der Iteratoren
}


// oder falls nur ein Iterator zur Verfügung steht, z.B. bei Zahlenparsern von Spirit
void <funktionsname>(IteratorT begin)
{
// Verarbeitung des Iterators
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
template<IteratorT>
void <funktionsname>(IteratorT begin, IteratorT end)
{
  // Verarbeitung der Iteratoren
}


// oder falls nur ein Iterator zur Verfügung steht, z.B. bei Zahlenparsern von Spirit
void <funktionsname>(IteratorT begin)
{
  // Verarbeitung des Iterators
}


Als Beispiel nehmen wir erneut den INI-File-Parser. Es wird im folgenden Beispiel eine Funktion definiert, die uns beispielhaft die Sektionen ausgibt.

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include
<string>
using namespace std;
// ...
template<typename IteratorT>
void DebugOut(IteratorT begin, IteratorT end)
{
    string str(begin, end);
    cout << str << endl;
    /* Alternative */
    /*
    while(begin != end)
        cout << *begin++;
    cout << endl;
    */

}

int main(int argc, char** argv)
{
   
    rule<> _VALUE = *(anychar_p - eol_p - end_p);                          
    rule<> _VAR = +(anychar_p - space_p - chlit<char>('='));              
    rule<> _VAR_DECLARATION = _VAR >> chlit<char>('=') >> _VALUE >> *eol_p;
    rule<> _SECTION = chlit<char>('[') >> *(anychar_p - chlit<char>(']'))  
                      >> chlit<char>(']') >> *eol_p;
                      //Hier kommt die Debug Ausgabe                      
    rule<> _INI_ROW = _SECTION[&DebugOut<const char*>] | _VAR_DECLARATION | eol_p;                
   
    // ...
   
    if(parse(<zeile aus der .ini Datei>, _INI_ROW).full)                    
    {
        cout << "Valid";
    }
   
    // ...
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include
<string>
using namespace std;
// ...
template<typename IteratorT>
void DebugOut(IteratorT begin, IteratorT end)
{
string str(begin, end);
cout << str << endl;
/* Alternative */
/*
while(begin != end)
cout << *begin++;
cout << endl;
*/

}

int main(int argc, char** argv)
{

rule<> _VALUE = *(anychar_p - eol_p - end_p);
rule<> _VAR = +(anychar_p - space_p - chlit<char>('='));
rule<> _VAR_DECLARATION = _VAR >> chlit<char>('=') >> _VALUE >> *eol_p;
rule<> _SECTION = chlit<char>('[') >> *(anychar_p - chlit<char>(']'))
>> chlit<char>(']') >> *eol_p;
//Hier kommt die Debug Ausgabe
rule<> _INI_ROW = _SECTION[&DebugOut<const char*>] | _VAR_DECLARATION | eol_p;

// ...

if(parse(<zeile aus der .ini Datei>, _INI_ROW).full)
{
cout << "Valid";
}

// ...
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include
<string>
using namespace std;
// ...
template<typename IteratorT>
void DebugOut(IteratorT begin, IteratorT end)
{
    string str(begin, end);
    cout << str << endl;
    /* Alternative */
    /*
    while(begin != end)
        cout << *begin++;
    cout << endl;
    */

}

int main(int argc, char** argv)
{
   
    rule<> _VALUE = *(anychar_p - eol_p - end_p);                          
    rule<> _VAR = +(anychar_p - space_p - chlit<char>('='));              
    rule<> _VAR_DECLARATION = _VAR >> chlit<char>('=') >> _VALUE >> *eol_p;
    rule<> _SECTION = chlit<char>('[') >> *(anychar_p - chlit<char>(']'))  
                      >> chlit<char>(']') >> *eol_p;
                      //Hier kommt die Debug Ausgabe                      
    rule<> _INI_ROW = _SECTION[&DebugOut<const char*>] | _VAR_DECLARATION | eol_p;                
   
    // ...
   
    if(parse(<zeile aus der .ini Datei>, _INI_ROW).full)                    
    {
        cout << "Valid";
    }
   
    // ...
}


Mit diesem Konstrukt haben Sie die Möglichkeit sehr einfache Ausgaben zu erzeugen. Dies kann für Debug-Informationen, die ausgegeben werden sollen, sehr nützlich sein.

Zur Ergänzung möchte ich Ihnen nun noch ein Beispiel für Zahlenparser (ein Iterator) liefern.

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ...

template<typename IteratorT>
void DebugOut(IteratorT only_one)
{
    cout << only_one << endl;
    cout << only_one << " + 1 = " << only_one+1 << endl;
}

int main(int argc, char* argv)
{

    rule<> count_val = int_p[&DebugOut<int>];
    if(parse("1", count_val).full)
        cout << "Supi";


}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ...

template<typename IteratorT>
void DebugOut(IteratorT only_one)
{
cout << only_one << endl;
cout << only_one << " + 1 = " << only_one+1 << endl;
}

int main(int argc, char* argv)
{

rule<> count_val = int_p[&DebugOut<int>];
if(parse("1", count_val).full)
cout << "Supi";


}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ...

template<typename IteratorT>
void DebugOut(IteratorT only_one)
{
    cout << only_one << endl;
    cout << only_one << " + 1 = " << only_one+1 << endl;
}

int main(int argc, char* argv)
{

    rule<> count_val = int_p[&DebugOut<int>];
    if(parse("1", count_val).full)
        cout << "Supi";


}


Nun ist das Anzeigen von Daten zwar wie oben genannt in manchen Fällen nützlich, jedoch ist es fast immer der Fall, dass man Informationen speichern muss. Spirit regelt diesen Vorgang über Funktionsobjekte.



7 Funktionsobjekte oder "Wie speichert man Dateien aus einem Parsevorgang?"

Spirit bietet zum Speichern der Daten Funktionsobjekte.

Das Grundgerüst eines Funktionsobjektes sieht je nach Anzahl der Iteratoren wie folgt aus:
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct <Funktionsobjektname>
{

    template<typename IteratorT>
    void operator()(IteratorT begin, IteratorT end) const
    {
        //...
    }
}

struct <Funktionsobjektname>
{

    template<typename IteratorT>
    void operator()(IteratorT only_one) const
    {
        //...
    }
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct <Funktionsobjektname>
{

template<typename IteratorT>
void operator()(IteratorT begin, IteratorT end) const
{
//...
}
}

struct <Funktionsobjektname>
{

template<typename IteratorT>
void operator()(IteratorT only_one) const
{
//...
}
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct <Funktionsobjektname>
{

    template<typename IteratorT>
    void operator()(IteratorT begin, IteratorT end) const
    {
        //...
    }
}

struct <Funktionsobjektname>
{

    template<typename IteratorT>
    void operator()(IteratorT only_one) const
    {
        //...
    }
}



Als Ausgangspunkt dient erneut der INI-File-Parser
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// ...
int main(int argc, char** argv)
{
   
    rule<> _VALUE = *(anychar_p - eol_p - end_p);                          
    rule<> _VAR = +(anychar_p - space_p - chlit<char>('='));              
    rule<> _VAR_DECLARATION = _VAR >> chlit<char>('=') >> _VALUE >> *eol_p;
    rule<> _SECTION = chlit<char>('[') >> *(anychar_p - chlit<char>(']'))  
                      >> chlit<char>(']') >> *eol_p;                      
    rule<> _INI_ROW = _SECTION[&DebugOut<const char*>] | _VAR_DECLARATION | eol_p;                
   
    // ...
   
    if(parse(<zeile aus der .ini Datei>, _INI_ROW).full)                    
    {
        cout << "Valid";
    }
   
    // ...
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// ...
int main(int argc, char** argv)
{

rule<> _VALUE = *(anychar_p - eol_p - end_p);
rule<> _VAR = +(anychar_p - space_p - chlit<char>('='));
rule<> _VAR_DECLARATION = _VAR >> chlit<char>('=') >> _VALUE >> *eol_p;
rule<> _SECTION = chlit<char>('[') >> *(anychar_p - chlit<char>(']'))
>> chlit<char>(']') >> *eol_p;
rule<> _INI_ROW = _SECTION[&DebugOut<const char*>] | _VAR_DECLARATION | eol_p;

// ...

if(parse(<zeile aus der .ini Datei>, _INI_ROW).full)
{
cout << "Valid";
}

// ...
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// ...
int main(int argc, char** argv)
{
   
    rule<> _VALUE = *(anychar_p - eol_p - end_p);                          
    rule<> _VAR = +(anychar_p - space_p - chlit<char>('='));              
    rule<> _VAR_DECLARATION = _VAR >> chlit<char>('=') >> _VALUE >> *eol_p;
    rule<> _SECTION = chlit<char>('[') >> *(anychar_p - chlit<char>(']'))  
                      >> chlit<char>(']') >> *eol_p;                      
    rule<> _INI_ROW = _SECTION[&DebugOut<const char*>] | _VAR_DECLARATION | eol_p;                
   
    // ...
   
    if(parse(<zeile aus der .ini Datei>, _INI_ROW).full)                    
    {
        cout << "Valid";
    }
   
    // ...
}


Für den Parser definieren wir uns nun ein Funktionsobjekt sowie die geeignete Speicherstruktur

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//...

// Die Speicherstruktur

typedef struct
{
   string section;
   string variable;
   string value;
}
INISTRUCT;

// Wo bin ich in der Regelabarbeitung
enum whereIAm {Section, Variable, Value};

// Das Funktionsobjekt
struct getData
{
    //Konstruktor: Call by Reference um Objekte verändern zu können
    getData(vector<INISTRUCT>& ini_File_, enum whereIAm& switcher_, INISTRUCT& actItem_) :
                               ini_File(ini_File_), switcher(switcher_), actItem(actItem_)
    {

    }

    // Funktionsoperator überladen
    template<typename IteratorT>
    void operator()(IteratorT begin, IteratorT end) const
    {
        // String erzeugen
        string str(begin, end);
        // Wo bin ich
        switch(switcher)
        {
            case Section:
            actItem.section = str; //Section
            break;

            case Variable:
            actItem.variable = str; //Variable
            break;

            case Value:
            actItem.value = str; //Value
            ini_File.push_back(actItem); // Element sichern
            break;
        }
    }
   
    // Benötigte Variablen
    vector<INISTRUCT>& ini_File;
    enum whereIAm& switcher;
    INISTRUCT& actItem;
};

// ...
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//...

// Die Speicherstruktur

typedef struct
{
string section;
string variable;
string value;
}
INISTRUCT;

// Wo bin ich in der Regelabarbeitung
enum whereIAm {Section, Variable, Value};

// Das Funktionsobjekt
struct getData
{
//Konstruktor: Call by Reference um Objekte verändern zu können
getData(vector<INISTRUCT>& ini_File_, enum whereIAm& switcher_, INISTRUCT& actItem_) :
ini_File(ini_File_), switcher(switcher_), actItem(actItem_)
{

}

// Funktionsoperator überladen
template<typename IteratorT>
void operator()(IteratorT begin, IteratorT end) const
{
// String erzeugen
string str(begin, end);
// Wo bin ich
switch(switcher)
{
case Section:
actItem.section = str; //Section
break;

case Variable:
actItem.variable = str; //Variable
break;

case Value:
actItem.value = str; //Value
ini_File.push_back(actItem); // Element sichern
break;
}
}

// Benötigte Variablen
vector<INISTRUCT>& ini_File;
enum whereIAm& switcher;
INISTRUCT& actItem;
};

// ...
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//...

// Die Speicherstruktur

typedef struct
{
   string section;
   string variable;
   string value;
}
INISTRUCT;

// Wo bin ich in der Regelabarbeitung
enum whereIAm {Section, Variable, Value};

// Das Funktionsobjekt
struct getData
{
    //Konstruktor: Call by Reference um Objekte verändern zu können
    getData(vector<INISTRUCT>& ini_File_, enum whereIAm& switcher_, INISTRUCT& actItem_) :
                               ini_File(ini_File_), switcher(switcher_), actItem(actItem_)
    {

    }

    // Funktionsoperator überladen
    template<typename IteratorT>
    void operator()(IteratorT begin, IteratorT end) const
    {
        // String erzeugen
        string str(begin, end);
        // Wo bin ich
        switch(switcher)
        {
            case Section:
            actItem.section = str; //Section
            break;

            case Variable:
            actItem.variable = str; //Variable
            break;

            case Value:
            actItem.value = str; //Value
            ini_File.push_back(actItem); // Element sichern
            break;
        }
    }
   
    // Benötigte Variablen
    vector<INISTRUCT>& ini_File;
    enum whereIAm& switcher;
    INISTRUCT& actItem;
};

// ...


Erklärung:
Wir überladen den Funktionsoperator, um das Funktionsobjekt in der Regel aufrufen zu können. (Der Sicherungsmechanisums ist simpel gehalten und dient nur zur Veranschaulichung.) Call by Reference ist hier Pflicht, um mit der "Außenwelt" kommunizieren zu können.


Nun muss man das ganze Konstrukt noch aufrufen.
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//...

int main(int argc, char *argv[])
{
    // ...

    vector<INISTRUCT> ini_File;
    INISTRUCT actItem;
    enum whereIAm section = Section, variable = Variable, value = Value;

    // Erzeugen der Funktionsobjekte
    getData func_sec(ini_File, section, actItem);  //Section
    getData func_var(ini_File, variable, actItem); //Variable
    getData func_val(ini_File, value, actItem);    //Value

    rule<> _VALUE = *(anychar_p - eol_p - end_p);
    rule<> _VAR = *(anychar_p - space_p - chlit<char>('='));

                              //Aufruf von func_var                 Aufruf von func_val
    rule<> _VAR_DECLARATION = _VAR[func_var] >> chlit<char>('=') >> _VALUE[func_val] >> *eol_p;
   
   
    rule<> _SECTION = chlit<char>('[') >> *(anychar_p - chlit<char>(']')) >> chlit<char>(']')
                                       >> *eol_p;
   
    //                Aufruf von func_sec
    rule<> _INI_ROW = _SECTION[func_sec] | _VAR_DECLARATION | eol_p;

    // ...

    if(parse(<zeile aus der .ini Datei>, _INI_ROW).full)                    
    {
        cout << "Valid";
    }
   
    // ...
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//...

int main(int argc, char *argv[])
{
// ...

vector<INISTRUCT> ini_File;
INISTRUCT actItem;
enum whereIAm section = Section, variable = Variable, value = Value;

// Erzeugen der Funktionsobjekte
getData func_sec(ini_File, section, actItem); //Section
getData func_var(ini_File, variable, actItem); //Variable
getData func_val(ini_File, value, actItem); //Value

rule<> _VALUE = *(anychar_p - eol_p - end_p);
rule<> _VAR = *(anychar_p - space_p - chlit<char>('='));

//Aufruf von func_var Aufruf von func_val
rule<> _VAR_DECLARATION = _VAR[func_var] >> chlit<char>('=') >> _VALUE[func_val] >> *eol_p;


rule<> _SECTION = chlit<char>('[') >> *(anychar_p - chlit<char>(']')) >> chlit<char>(']')
>> *eol_p;

// Aufruf von func_sec
rule<> _INI_ROW = _SECTION[func_sec] | _VAR_DECLARATION | eol_p;

// ...

if(parse(<zeile aus der .ini Datei>, _INI_ROW).full)
{
cout << "Valid";
}

// ...
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//...

int main(int argc, char *argv[])
{
    // ...

    vector<INISTRUCT> ini_File;
    INISTRUCT actItem;
    enum whereIAm section = Section, variable = Variable, value = Value;

    // Erzeugen der Funktionsobjekte
    getData func_sec(ini_File, section, actItem);  //Section
    getData func_var(ini_File, variable, actItem); //Variable
    getData func_val(ini_File, value, actItem);    //Value

    rule<> _VALUE = *(anychar_p - eol_p - end_p);
    rule<> _VAR = *(anychar_p - space_p - chlit<char>('='));

                              //Aufruf von func_var                 Aufruf von func_val
    rule<> _VAR_DECLARATION = _VAR[func_var] >> chlit<char>('=') >> _VALUE[func_val] >> *eol_p;
   
   
    rule<> _SECTION = chlit<char>('[') >> *(anychar_p - chlit<char>(']')) >> chlit<char>(']')
                                       >> *eol_p;
   
    //                Aufruf von func_sec
    rule<> _INI_ROW = _SECTION[func_sec] | _VAR_DECLARATION | eol_p;

    // ...

    if(parse(<zeile aus der .ini Datei>, _INI_ROW).full)                    
    {
        cout << "Valid";
    }
   
    // ...
}


Erklärung:
Was ist passiert? Es werden drei Funktionsobjekte erzeugt (func_sec, func_var, func_val) und aufgerufen. Dies passiert jedes Mal, wenn der Parser in der Regel zur Aufrufstelle kommt.
Code:
... _SECTION[func_sec] ...
Code:
... _SECTION[func_sec]...
Code:
... _SECTION[func_sec] ...

Zum Verständnis für Funktionsobjekte möchte ich auf
http://cplus.kompf.de/artikel/functors.html und http://www.sgi.com/tech/stl/functors.html verweisen, da alleine die Erklärung von Funktionsobjekten wohl einen Artikel rechtfertigen würde.



Hiermit endet meine Einführung in Spirit. Ich hoffe, ich konnte einen kleinen Einblick in die Funktionsweise und die Struktur von Spirit geben.



8 Ausblick

Spirit 1.8.3 bietet einige interessante Neuerungen. Unter anderem Storable Rules, die es erlauben, die Regel dynamisch zu erweitern.

Bsp.:
C/C++ Code:
//...
stored_rule<> hallo = strlit<const char*>("Hallo");
rule<> welt = strlit<const char*>(" Welt!");

hallo = hallo.copy() >> welt;
//...
C/C++ Code:
//...
stored_rule<> hallo = strlit<const char*>("Hallo");
rule<> welt = strlit<const char*>(" Welt!");

hallo = hallo.copy() >> welt;
//...
C/C++ Code:
//...
stored_rule<> hallo = strlit<const char*>("Hallo");
rule<> welt = strlit<const char*>(" Welt!");

hallo = hallo.copy() >> welt;
//...


Diese Methode bietet sich hervorragend an, um beim Parsen eines C-Source-Files zum Beispiel die nativen Datentypen um typedef-Datentypen zu erweitern.


9 Weiterführende Links

Spirit 1.6.3
Spirit 1.8.3
http://www.codeproject.com/vcpp/stl/spirit_semantic_actions.asp

_________________
_ _ _________________ _ _
Wenn man keine Ahnung hat, einfach mal die Fresse halten - Dieter Nuhr


Zuletzt bearbeitet von Tobias Gerg am 19:33:04 19.05.2008, insgesamt 5-mal bearbeitet
bernd1
Unregistrierter




Beitrag bernd1 Unregistrierter 11:41:09 16.03.2006   Titel:              Zitieren

hallo,
klasse artikel!!!!!

was ich nicht versteh ist: rule<> _SECTION

cu
Tobias Gerg
Autor

Benutzerprofil
Anmeldungsdatum: 29.10.2001
Beiträge: 348
Beitrag Tobias Gerg Autor 21:08:02 16.03.2006   Titel:              Zitieren

Hallo bernd1,

danke...

Zu deiner Frage

C/C++ Code:
rule<> _SECTION
C/C++ Code:
rule<> _SECTION
C/C++ Code:
rule<> _SECTION


wird wie folgt aufgedröselt...

Der erste Buchstabe muss eine '[' sein dann können beliebige (*(anychar_p...) Zeichen kommen ausser ((... - chlit<char>(']')) eine ']'. Nach diesen Zeichen muss zwingend eine ']' kommen. Danach kann ein end of line kommen.

Ich hoffe ich konnte dir weiterhelfen...

Gruss
Tobi

_________________
_ _ _________________ _ _
Wenn man keine Ahnung hat, einfach mal die Fresse halten - Dieter Nuhr
Tobias Gerg
Autor

Benutzerprofil
Anmeldungsdatum: 29.10.2001
Beiträge: 348
Beitrag Tobias Gerg Autor 21:14:16 16.03.2006   Titel:              Zitieren

Hier nun das versprochene PDF zur Theoretischen Informatik
Klick Mich

_________________
_ _ _________________ _ _
Wenn man keine Ahnung hat, einfach mal die Fresse halten - Dieter Nuhr
Tobias Gerg
Autor

Benutzerprofil
Anmeldungsdatum: 29.10.2001
Beiträge: 348
Beitrag Tobias Gerg Autor 19:58:51 28.03.2006   Titel:              Zitieren

Hallo zusammen,

anbei eine Berichtigung des PDF's

kurz nach der Stelle
Code:
linke Seite -> rechte Seite
Code:
linke Seite -> rechte Seite
Code:
linke Seite -> rechte Seite

muss es heißen wie folgt heißen.

Die linke Seite besteht aus mindestens einer Variablen und beliebigen Terminalsymbolen bestehen.

Die Rechte Seite kann aus Variablen und Terminalsymbolen bestehen

Gruss
Tobi

_________________
_ _ _________________ _ _
Wenn man keine Ahnung hat, einfach mal die Fresse halten - Dieter Nuhr
Tobias Gerg
Autor

Benutzerprofil
Anmeldungsdatum: 29.10.2001
Beiträge: 348
Beitrag Tobias Gerg Autor 19:15:29 04.04.2006   Titel:              Zitieren

Die Berichtigung ist jetzt auch im PDF zu finden

Grüsse
Tobi

_________________
_ _ _________________ _ _
Wenn man keine Ahnung hat, einfach mal die Fresse halten - Dieter Nuhr
justin
Unregistrierter




Beitrag justin Unregistrierter 03:34:16 15.07.2006   Titel:   nettes tutorial, planst du noch ein weiteres tut zu boost spirit?            Zitieren

cu
justin
Unregistrierter




Beitrag justin Unregistrierter 03:35:42 15.07.2006   Titel:              Zitieren

bzw. kannst du den ganzen source code deiner demos irgendwo hochladen?
wäre nett!
cu
cplusplus.
Unregistrierter




Beitrag cplusplus. Unregistrierter 19:23:02 17.07.2006   Titel:              Zitieren

hi

warum ist die klasse rule eine template klasse? in einigen beispielen wurde ScannerT als typ verwenden...
C/C++ Code:
    template<
        typename ScannerT = scanner<>,
        typename ContextT = parser_context<>,
        typename TagT = parser_address_tag>
    class rule;
C/C++ Code:
template<
typename ScannerT = scanner<>,
typename ContextT = parser_context<>,
typename TagT = parser_address_tag>
class rule;
C/C++ Code:
    template<
        typename ScannerT = scanner<>,
        typename ContextT = parser_context<>,
        typename TagT = parser_address_tag>
    class rule;
Tobias Gerg(offline)
Unregistrierter




Beitrag Tobias Gerg(offline) Unregistrierter 10:46:35 09.08.2006   Titel:              Zitieren

@Justin:

hallo Justin, momentan ist kein weiterer Artikel meinerseits geplant kommt aber vielleicht noch.

Die Sourcen muss ich suchen. Wenn ich sie gefunden habe lade ich sie hoch.


@cplusplus.:
Hallo cplusplus. Die rule<> Klasse ist deshalb eine Templateklasse, weil du manchmal eben nicht mit einfachen plain vanilla parsern in der rule klar. Manchmal braucht man was "stärkers". :)
Da gibt's noch Grammatiken, Parsebäume und vieles mehr wo die rule<> parametrisiert werden muss.

Als einstieg googelst du dir vielleicht mal was zu Formalen Sprachen und Grammatiken.

Danach ziehst du dir die Doku von Spirit noch einmal rein.
Oder du gehst gleich in Richtung compiler bau (Drachenbuch)

Ich hoffe ich konnte das ansatzweise klären

Grüsse
Tobi
Paddy82
Mitglied

Benutzerprofil
Anmeldungsdatum: 06.01.2005
Beiträge: 113
Beitrag Paddy82 Mitglied 08:02:25 15.08.2006   Titel:              Zitieren

Wie performant ist spirit eigentlich?
Kann man einen kompletten Parser der die Syntaxanalyse für C++ durchführen soll damit realisieren oder sollte man lieber auf Lex und Yacc bzw. ANTLR zurückgreifen?

Das Framework sieht echt sehr interessant aus.
Guter Artikel...

Gruß Patrick
Tobias Gerg
Autor

Benutzerprofil
Anmeldungsdatum: 29.10.2001
Beiträge: 348
Beitrag Tobias Gerg Autor 13:06:54 01.09.2006   Titel:              Zitieren

Hallo Paddy82,

man kann durchaus in einer akzeptablen Zeit die Syntaxanalyse von Codedateiten bewerkstelligen. Allerdings bläst sich der Parser ziemlich auf.

Ich würde für solche Sachen mittlerweile wohl eher auf Lex und Yacc zurückgreifen.

Grüsse
Tobi

_________________
_ _ _________________ _ _
Wenn man keine Ahnung hat, einfach mal die Fresse halten - Dieter Nuhr
Artchi
Autor

Benutzerprofil
Anmeldungsdatum: 16.03.2002
Beiträge: 8571
Beitrag Artchi Autor 01:18:33 23.11.2006   Titel:              Zitieren

Habe mich mal an deinen Artikel versucht. Leider erhalte ich einen Compile-Error für das Debugout-Bsp. aus Abschnitt 6. Angemeckert wir die Zeile, in der _SECTION die DebugOut-Funktion übergeben bekommt:

Zitat:
Spirit1.cpp(27) : error C2896: 'boost::spirit::action<DerivedT,ParserT> boost::spirit::parser<DerivedT>::operator [](const ActionT &) const': Funktionsvorlage 'void DebugOut(IteratorT,IteratorT)' kann nicht als Funktionsargument verwendet werden
with
[
DerivedT=boost::spirit::rule<>
]
Spirit1.cpp(27) : error C2676: Binärer Operator '[': 'boost::spirit::rule<>' definiert diesen Operator oder eine Konvertierung in einen für den vordefinierten Operator geeigneten Typ nicht


Compiler: MSVC 7.1
Spirit: 1.8.x aus Boost 1.33.1

Laut MSDN ist Fehler C2896 so, das ein Funktionstemplate selbst kein Funktionstemplate verwenden kann. Hem...

Hat jemand ebenfalls diesen Fehler?

Wenn ich aus DebugOut keine Templatefunktion mache, funktioniert es, so wie es der Compiler haben will.

_________________
Bring back the Windows Start Menu Petition | GoPetition


Zuletzt bearbeitet von Artchi am 01:22:48 23.11.2006, insgesamt 2-mal bearbeitet
CStoll
Moderator

Benutzerprofil
Anmeldungsdatum: 17.10.2005
Beiträge: 17913
Beitrag CStoll Moderator 10:28:49 23.11.2006   Titel:              Zitieren

Du solltest einfach angeben, welche Spezialisierung deiner Template-Funkion verwendet werden soll (beachte das '_SECTION[&DebugOut<const char*>]' im Beispiel).

_________________
Wo ich bin, herrscht Chaos. Leider kann ich nicht überall sein.

Moderator im MFC- und C++-Board und Magazin-Autor
Artchi
Autor

Benutzerprofil
Anmeldungsdatum: 16.03.2002
Beiträge: 8571
Beitrag Artchi Autor 11:21:28 23.11.2006   Titel:              Zitieren

Das hatte ich ja gemacht, habe sogar vorsichtshalber den Source aus dem Artikel Copy&Paste gemacht. Meldung kam trotzdem.

_________________
Bring back the Windows Start Menu Petition | GoPetition
Artchi
Autor

Benutzerprofil
Anmeldungsdatum: 16.03.2002
Beiträge: 8571
Beitrag Artchi Autor 12:03:20 23.11.2006   Titel:              Zitieren

So, hab es jetzt mal mit MinGW ausprobiert. Da funktioniert es anstandslos. Fragt sich jetzt, ob MSVC7.1 hier eine Standardlücke hat...

_________________
Bring back the Windows Start Menu Petition | GoPetition
otze
Mitglied

Benutzerprofil
Anmeldungsdatum: 15.01.2004
Beiträge: 6653
Beitrag otze Mitglied 09:44:42 26.06.2007   Titel:              Zitieren

Tobias Gerg(offline) schrieb:
@Justin:
@cplusplus.:
Hallo cplusplus. Die rule<> Klasse ist deshalb eine Templateklasse, weil du manchmal eben nicht mit einfachen plain vanilla parsern in der rule klar. Manchmal braucht man was "stärkers". :)
Da gibt's noch Grammatiken, Parsebäume und vieles mehr wo die rule<> parametrisiert werden muss.

brauchte man nicht auch einen anderen scanner, wenn man mit anderen typen als char oder wchar_t arbeiten wollte?

_________________
Jesus Christus! Da blickt ja kein Mensch mehr durch.
Tobias Gerg(offline)
Unregistrierter




Beitrag Tobias Gerg(offline) Unregistrierter 11:50:08 26.06.2007   Titel:              Zitieren

Hallo Artchi,

ich check das noch mal gegen wenn ich den MSVC wieder mal zur Hand habe.

Gruß
Tobi

Hallo otze,

boah das ist ne gute Frage... Bin schon ziemlich lange wieder aus Spirit draußen... Daher bin ich mir nicht sicher... Ich probier das bei gelegenheit mal wieder...

Gruß
Tobi
CTLeser
Unregistrierter




Beitrag CTLeser Unregistrierter 12:54:53 06.02.2008   Titel:              Zitieren

der artikel ist btw in der CT 1/2008 im boost::spirit artikel als eine der (wenigen) referenzen angegeben :live:
markusrw
Mitglied

Benutzerprofil
Anmeldungsdatum: 01.11.2007
Beiträge: 110
Beitrag markusrw Mitglied 19:38:22 11.05.2008   Titel:              Zitieren

Artchi schrieb:
So, hab es jetzt mal mit MinGW ausprobiert. Da funktioniert es anstandslos. Fragt sich jetzt, ob MSVC7.1 hier eine Standardlücke hat...

Hab zwar eine neuere Version, glaub ich, aber ich bekomm den gleichen Fehler, wäre mal echt geil, wenn hier jemand eine Lösung parat hätte.^^
Möchte net wegen sowas direkt den Compiler wechseln...

[e]
K, hab es jetzt mit functoren probiert, die gehen, vielleicht sollte ich ich mal statt normalen Funktionen Funktionszeiger verwenden.
[e2]
So, wenn ich statt &f einfach einen Funktionszeiger auf die Funktion F verwende, dann funktionierts!
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
typedef void (*fun)(const char*,const char*);

void f(const char*,const char*);

int
main()
{
    fun _f = f; // funkt sowohl mit als auch ohne & vor f -> fun _f = &f
    // func f;

    rule<> r = *(real_p>>+(ch_p(',')));
    //rule<> r2 = r[func()];
    rule<> r2 = r[_f];
    if(parse("1,2,3,4,5,",r2).full)
        cout << "full" << endl;
    _getch();
    return 0;
}
void f(const char* beg, const char* end)
{
    string str(beg,end);
    cout << str << endl;
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
typedef void (*fun)(const char*,const char*);

void f(const char*,const char*);

int
main()
{
fun _f = f; // funkt sowohl mit als auch ohne & vor f -> fun _f = &f
// func f;

rule<> r = *(real_p>>+(ch_p(',')));
//rule<> r2 = r[func()];
rule<> r2 = r[_f];
if(parse("1,2,3,4,5,",r2).full)
cout << "full" << endl;
_getch();
return 0;
}
void f(const char* beg, const char* end)
{
string str(beg,end);
cout << str << endl;
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
typedef void (*fun)(const char*,const char*);

void f(const char*,const char*);

int
main()
{
    fun _f = f; // funkt sowohl mit als auch ohne & vor f -> fun _f = &f
    // func f;

    rule<> r = *(real_p>>+(ch_p(',')));
    //rule<> r2 = r[func()];
    rule<> r2 = r[_f];
    if(parse("1,2,3,4,5,",r2).full)
        cout << "full" << endl;
    _getch();
    return 0;
}
void f(const char* beg, const char* end)
{
    string str(beg,end);
    cout << str << endl;
}


Zuletzt bearbeitet von markusrw am 12:55:20 12.05.2008, insgesamt 3-mal bearbeitet
C/C++ Forum :: Die Artikel ::  Boost::Spirit - Eine Einführung   Thema geschlossen

Zeige alle Beiträge auf einer Seite




Nächstes Thema anzeigen
Vorheriges Thema anzeigen
Sie können keine Beiträge in dieses Forum schreiben.
Sie können auf Beiträge in diesem Forum antworten.
Sie können Ihre Beiträge in diesem Forum nicht bearbeiten.
Sie können Ihre Beiträge in diesem Forum nicht löschen.
Sie können an Umfragen in diesem Forum nicht mitmachen.

Powered by phpBB © 2001, 2002 phpBB Group :: FI Theme

c++.de ist Teilnehmer des Partnerprogramms von Amazon Europe S.à.r.l. und Partner des Werbeprogramms, das zur Bereitstellung eines Mediums für Websites konzipiert wurde, mittels dessen durch die Platzierung von Werbeanzeigen und Links zu amazon.de Werbekostenerstattung verdient werden kann.

Die Vervielfältigung der auf den Seiten www.c-plusplus.de, www.c-plusplus.info, www.c-sar.de, www.c-plusplus.net und www.baeckmann.de enthaltenen Informationen ohne eine schriftliche Genehmigung des Seitenbetreibers ist untersagt (vgl. §4 Urheberrechtsgesetz). Die Nutzung und Änderung der vorgestellten Strukturen und Verfahren in privaten und kommerziellen Softwareanwendungen ist ausdrücklich erlaubt, soweit keine Rechte Dritter verletzt werden. Der Seitenbetreiber übernimmt keine Gewähr für die Funktion einzelner Beiträge oder Programmfragmente, insbesondere übernimmt er keine Haftung für eventuelle aus dem Gebrauch entstehenden Folgeschäden.