Wie kann ich Boost::Spirit mit meiner Callback-Funktion füttern?
-
Hallo zusammen,
Ich habe mir mit Boost::Spirit einen Parser für BibTex-Dateien geschrieben, der im Prinzip bereits funktioniert. Nun möchte ich das gute Stück aber in eine Klasse verpacken, welche mir eine BibTex-Datei verwaltet. Dabei werden die Callback-Funktionen zum Problem.
Bis jetzt ging es so:std::string stack; rule<> rule_wert = (rule_quote >> rule_txtmatch_quote [out<const char*>(stack)] >> rule_quote)Dabei war out<typename itT>() ein Funktor, welcher dafür sorgt, dass Ausdrücke, die auf rule_txtmatch_quote passen im string Stack gespeichert werden.
Jetzt habe ich aber den Parser und die Callback-Funktionen als Methoden in meine BibTex-klasse gepackt. Die Callback Funktion soll die geparsten Ausdrücke in einem Containerobjekt ablegen. Das soll in etwa so aussehen:class bibliography{ ... private: row _columns; std::vector<row> _rows; template<typename iterator_t> void addValue(file_iterator begin, file_iterator end); ... template<typename iterator_t> bool parse(iterator_t begin, iterator_t end); }Aber wie kann ich jetzt innerhalb meiner parse()-Funktion die Funktion addValue() übergeben. Ich habe zahlreiche Varianten versucht:
rule<> wert = (_quote >> _txtmatch_quote[&this->addValue()] >> _quote); rule<> wert = (_quote >> _txtmatch_quote[&this->addValue<iterator_t>] >> _quote); rule<> wert = (_quote >> _txtmatch_quote[&this->addValue] >> _quote); rule<> wert = (_quote >> _txtmatch_quote[boost::bind<void>(&bibliography::addValue, this,_1, _2)] >> _quote); usw.Ich bekomme immer nur die verücktesten Fehlermeldungen. Also, wenn einer vielleicht eine Idee hat... Es ist zum Haaaareraufen

-
mem_fn_ptr oder so heißt die Funktion ... in Kombination mit bind ...
-
Hm, so ähnlich vielleicht:
rule<> rule_wert = (rule_quote >> rule_txtmatch_quote [boost::mem_fn<void, bibliography>((bibliography::addValue<iterator_t>) (iterator_t A1, iterator_t A2))] >> rule_quote);Das führt leider zu "D:\programmierung\aktuell\bibtexparser\bibtex.hpp|163|error: expected primary-expression before "A1"|" obwohl iterator_t eigentlich bekannt sein müsste

-
Das Problem ist dass addValue ein Template ist.
In so einem Fall muss man sich den Funktor schon selbst schreiben, sonst wird das nicht funktionieren.struct BibliographyAddValue { BibliographyAddValue(bibliography* bib) : m_bib(bib) { } template<typename iterator_t> void operator() (iterator_t begin, iterator_t end) const { m_bib->addValue(begin, end); } bibliography* m_bib; };
-
Hm, ok. Vielen Dank schonmal.
Leider klappt auch das nicht. So bekomme ich ein Problem mit der include-Reihenfolge. Für das bibliography-Objekt will der Compiler den Funktor schon kennen und umgekehrt. Da helfen auch säuberliche Includes + Include-Guard nicht. Und die Definition der Funktionen kann ich auch nicht aus den Headern rausnehmen, weil es ja alles Templates sind.
...
Also entweder ich verzichte auf Templates oder auf Objekte. So macht das echt kein Spaß.So habe ich das provisorisch versucht:
// struct.hpp #ifndef STRUCT_HPP #define STRUCT_HPP #include "bibtex.hpp" namespace bibtex{ enum typ_enum{ROW,KEY,FIELD,VALUE,STRING,COMMENT}; struct out { out(bibliography* bib) : _bib(bib) { } template<typename itT, typ_enum typ> void operator()(itT begin, itT end) const { switch(typ) { case ROW: _bib->newRow<itT>(begin, end); break; case KEY: _bib->addKey<itT>(begin, end); break; case FIELD: _bib->addField<itT>(begin, end); break; case VALUE: _bib->addValue<itT>(begin, end); break; case STRING: _bib->addString<itT>(begin, end); break; case COMMENT: _bib->addComment<itT>(begin, end); break; } } bibliography* _bib; }; }// namespace bibtexUnd:
//bibtex.hpp #ifndef BIBTEX_HPP #define BIBTEX_HPP ... #include "struct.hpp" namespace bibtex{ class bibliography{ public: ... template<typename iterator_t> bool parse(iterator_t begin, iterator_t end); //Hilfsfunktionen fürs Parsen template<typename iterator_t> void newRow(iterator_t begin, iterator_t end) const; template<typename iterator_t> void addKey(iterator_t begin, iterator_t end) const; ... };// class bibliography template<typename iterator_t> bool bibliography::parse(iterator_t begin, iterator_t end) { ... boost::spirit::rule<> wert = _quote >> _txtmatch_quote[out<const char*,VALUE>(this)] >> _quote; ... } }//namespace bibtexErgebnis: Bei dieser Variante kommt jetzt das heraus:
D:\programmierung\aktuell\bibtexparser\struct.hpp|17|error: expected)' before '*' token D:\\programmierung\\aktuell\\bibtexparser\\struct.hpp|46|error: ISO C++ forbids declaration ofbibliography' with no type
D:\programmierung\aktuell\bibtexparser\struct.hpp|46|error: expected `;' before '*' tokenZeile 17 ist diejenige, wo der Konstruktor von struct anfängt. Der Compiler mag also (bibliography* bib) nicht. Zeile 46 ist im obigen Code Zeile 43, also die mit "bibliography* _bib;"..
-
Du hast jetzt eine Cross-Referenz vorliegen, dh. die beiden Header-Dateien binden sich gegenseitig ein und das geht nicht.
Da du alles in Header-Dateien (wegen der Templates) implementieren mußt, hilft dir wohl nichts anderes, als eine Header-Datei zu machen und darin dann erst beide Klassen zu definieren und danach dann die Templatemethoden...
-
Statt boost::bind musst du in spirit afaik phoenix::bind benutzen.
-
Ok, nächster Versuch:
namespace bibtex{ enum typ_enum{ROW,KEY,FIELD,VALUE,STRING,COMMENT}; template <typename target_type, typ_enum typ> struct out { out(target_type* t) : _target(t) {} template<typename itT> void operator()(itT begin, itT end) const { // wäre "schöner" über spezialisierung zu lösen, aber der Einfachheit halber lasse ich es mal mit switch(typ) switch(typ) { case ROW: _target->newRow<itT>(begin, end); break; case KEY: _target->addKey<itT>(begin, end); break; case FIELD: _target->addField<itT>(begin, end); break; case VALUE: _target->addValue<itT>(begin, end); break; case STRING: _target->addString<itT>(begin, end); break; case COMMENT: _target->addComment<itT>(begin, end); break; default: terminate(); } } target_type* _target; }; }// namespace bibtex
-
Nicht schlecht

Das war auch gleich die erste Idee, die ich dann hatte. Also so:#ifndef STRUCT_HPP #define STRUCT_HPP #include "bibtex.hpp" namespace bibtex{ enum typ_enum{ROW,KEY,FIELD,VALUE,STRING,COMMENT}; template<typename target_type, typ_enum typ> struct out { out(target_type* bib) : _bib(bib) { } template<typename itT> void operator()(itT begin, itT end) const { switch(typ) { case ROW: _bib->newRow<itT>(begin, end); break; case KEY: _bib->addKey<itT>(begin, end); break; case FIELD: _bib->addField<itT>(begin, end); break; case VALUE: _bib->addValue<itT>(begin, end); break; case STRING: _bib->addString<itT>(begin, end); break; case COMMENT: _bib->addComment<itT>(begin, end); break; } } target_type* _bib; }; }// namespace bibtex #endifAber selbstverständlich geht auch das nicht. Dann sagt mein schnuckeliger gcc (diesmal stimmen die Zeilennummern):
D:\programmierung\aktuell\bibtexparser\struct.hpp||In member function `void bibtex::out<target_type, typ>::operator()(itT, itT) const':| D:\programmierung\aktuell\bibtexparser\struct.hpp|23|error: expected primary-expression before '>' token| D:\programmierung\aktuell\bibtexparser\struct.hpp|26|error: expected primary-expression before '>' token| D:\programmierung\aktuell\bibtexparser\struct.hpp|29|error: expected primary-expression before '>' token ...
-
Ich denke ich habe ein 'template' Keyword vergessen (MSVC ist da (leider) nicht so streng
)://_bib->newRow<itT>(begin, end); // -> _bib->template newRow<itT>(begin, end);
-
Problem gelöst: Das war nur eine triviale Kleinigkeit. Aber bei den Fehlermeldungen, die der Compiler ausschmeißt, sobald Boost im Spiel ist, denkt man ja man hätte weiß Gott was falsch gemacht. Vielen Dank jedenfalls für Eure Geduld.
Für die Nachwelt:
Der Fehler lag darin, dass ich den Callback Funktionen den falschen Parametertyp gegeben habe. Boost::Spirit wollte Funktionen, die nicht denselben Iterator neben, wie der Parser, sondern welche vom Typ const char*.
Es klappt problemlos, mit boost::bind() Klassenfunktionen zu übergeben, sogar wenn das Templates sind. Schöne Sache! Hier mal Auschnitte aus dem Code, der sich nicht nur Kompilieren lässt, sondern auch läuft:// bibtex.hpp namespace bibtex{ class bibliography{ ... private: ... //Die Parser-Funktion bool parse(std::string::iterator begin, std::string::iterator end); //Hilfsfunktionen fürs Parsen template<typename itT> void newRow(itT begin, itT end) const; void addKey(const char* begin, const char* end) const; ... };// class bibliograph }// namespace bibtex// bibtex.cpp namespace bibtex{ template<typename itT> void bibliography::newRow(itT begin, itT end) const { //noch leer } void bibliography::addKey(const char* begin, const char* end) const { //noch leer } bool bibliography::parse(std::string::iterator begin, std::string::iterator end) { ... //Hier wird die template-funktion aufgerufen boost::spirit::rule<> blocktyp = (* (_txtcore-space_p) )[bind(&bibliography::newRow<const char*>, this,_1, _2)]; //Hier die andere boost::spirit::rule<> blockkey = (* (_txtcore-space_p) )[bind(&bibliography::addKey, this,_1, _2)]; ... } }// namespace bibtexNetterweise beschwert sich gcc jetzt noch nicht einmal darüber, dass ich die template-funktion erst in der Quelltextdatei definiert habe. Wahrscheinlich, weil ich sie auch nur dort brauche. Bei einer private-Methode ist das ja erstmal unproblematisch.
Vielleicht hilft das ja nochmal jemandem
