2015-09-13 1 views
0

La fonction boost::spirit::qi::parse() s'attend à ce que deux itérateurs définissent la plage d'entrée. Cela fonctionne bien si j'essaye d'analyser std::string ou std::istream. Maintenant, je veux implémenter une interface plus générique pour mon analyseur. Une approche consistait à utiliser boost::any_range pour définir l'entrée. Voici mon code de test qu'il compile mais lance une exception: "string iterator not dereferencable".Combine boost :: spirit et boost :: any_range?

Deuxième question. Comment puis-je combiner boost::any_range avec boost::spirit::classic::position_iterator pour détecter une position d'erreur possible?

#include <boost/range/any_range.hpp> 
#include <boost/spirit/include/classic_position_iterator.hpp> 
#include <boost/spirit/include/qi.hpp> 
#include <boost/spirit/include/support_multi_pass.hpp> 

namespace qi = boost::spirit::qi; 

typedef boost::any_range< 
    char, 
    boost::forward_traversal_tag, 
    char, 
    std::ptrdiff_t 
> input_type; 

template < typename _Iterator > 
    struct decode 
    : qi::grammar<_Iterator> 
    { 
     decode() : decode::base_type(m_rule) 
     { 
      m_rule = qi::int_; 

      BOOST_SPIRIT_DEBUG_NODES((m_rule)) 
     } 

     qi::rule<_Iterator> m_rule; 
    }; 

bool parse(const input_type& in, int& out) 
{ 
    // We use a stream iterator to access the given stream: 
    typedef boost::spirit::multi_pass< 
     input_type::const_iterator 
    > stream_iterator; 

    // Create begin iterator for given stream: 
    stream_iterator sBegin = boost::spirit::make_default_multi_pass(input_type::const_iterator(in.begin())); 
    stream_iterator sEnd = boost::spirit::make_default_multi_pass(input_type::const_iterator()); 

    // Create an instance of the used grammar: 
    decode< 
     stream_iterator 
    > gr; 

    // Try to decode the data stored within the stream according the grammar and store the result in the out variable: 
    bool r = boost::spirit::qi::parse(sBegin, 
             sEnd, 
             gr, 
             out); 

    return r && sBegin == sEnd; 
} 

void main() 
{ 
    std::string in = "12345"; int out; 

    parse(in, out); 
} 

Mise à jour

1.) Je suis d'accord qu'il ya une erreur dans la valeur par défaut construit sEnd iterator. Par conséquent, j'ai simplifié mon exemple et je pense que j'ai mal compris comment utiliser l'itérateur multi_pass. Dans ce cas c0 est false (attendue) et c1 est true (pas prévu). Alors, quelle est la bonne façon d'utiliser l'itérateur multi_pass?

#include <boost/range/any_range.hpp> 
#include <boost/spirit/include/classic_position_iterator.hpp> 
#include <boost/spirit/include/qi.hpp> 
#include <boost/spirit/include/support_multi_pass.hpp> 

namespace qi = boost::spirit::qi; 

typedef boost::any_range< 
    char, 
    boost::forward_traversal_tag, 
    char, 
    std::ptrdiff_t 
> input_type; 

bool parse(const input_type& in, int& out) 
{ 
    //for(input_type::iterator i = in.begin(); i != in.end(); ++i) 
    //{ 
    // std::cout << *i; 
    //} 

    // We use a stream iterator to access the given stream: 
    typedef boost::spirit::multi_pass< 
     input_type::const_iterator, 
     boost::spirit::iterator_policies::default_policy<    // Defaults: 
      boost::spirit::iterator_policies::ref_counted,    // OwnershipPolicy: ref_counted 
      boost::spirit::iterator_policies::buf_id_check,    // CheckingPolicy : buf_id_check 
      boost::spirit::iterator_policies::buffering_input_iterator, // InputPolicy : buffering_input_iterator 
      boost::spirit::iterator_policies::split_std_deque   // StoragePolicy : split_std_deque 
     > 
    > stream_iterator; 

    bool c0 = in.begin() == in.end(); 

    // Create begin iterator for given stream: 
    stream_iterator sBegin(in.begin()); 
    stream_iterator sEnd( in.end() ); 

    bool c1 = sBegin == sEnd; 

    //for(stream_iterator i = sBegin; i != sEnd; ++i) 
    //{ 
    // std::cout << *i; 
    //} 

    return false; 
} 
void main() 
{ 
    std::string in = "12345"; int out; 

    parse(in, out); 
} 

2.) Oui, je peux compiler une nouvelle instance de grammaire pour chaque type d'itérateur d'entrée. Mon idée était de cacher le détail de l'implémentation (= boost::spirit) de l'utilisateur et de lui donner une interface générique. Par conséquent, je voudrais éviter une fonction de modèle.

3.) Oui j'ai oublié d'exposer l'attribut. Ce n'était qu'un exemple rapide. Merci pour l'indice.

+0

Je vais ignorer "deuxième question". Une question à la fois. Les deux sont beaucoup compliqués par eux-mêmes. – sehe

Répondre

1

L'itérateur construit par défaut n'est pas équivalent à un itérateur de fin pour votre plage.

Cette convention est généralement suivie seulement par les itérateurs d'entrée.

L'analyseur continue à lire. Heureusement, vous êtes sur une sorte d'implémentation de compilateur/bibliothèque qui détecte l'accès passé-fin.


En réalité, vous ne pouvez pas compiler simplement une nouvelle grammaire (decode<>) instance pour itérateurs d'entrée? C'est tout l'intérêt de la programmation générique en C++.


MISE À JOUR

C'est ce que je ferais:

  • Notez que do_parse (et tout esprit, ou bien, Boost lié) peut être caché dans le cpp

Live On Coliru

#include <boost/spirit/include/qi.hpp> 

namespace mylib { 
    struct public_api { 
     int parse(std::string const& input); 
     int parse(std::istream& stream); 
    }; 

    template<typename It> 
    static int do_parse(It f, It l) { 
     namespace qi = boost::spirit::qi; 

     int result; 
     if (qi::parse(f, l, qi::int_, result)) 
      return result; 

     throw std::runtime_error("parse failure"); 
    } 

    int public_api::parse(std::string const& input) { 
     return do_parse(input.begin(), input.end()); 
    } 

    int public_api::parse(std::istream& stream) { 
     boost::spirit::istream_iterator f(stream >> std::noskipws), l; 
     return do_parse(f, l); 
    } 
} 

int main() 
{ 
    std::istringstream iss("12345"); 
    std::string const s("23456"); 

    mylib::public_api api; 
    std::cout << api.parse(s) << "\n"; 
    std::cout << api.parse(iss) << "\n"; 
} 

Prints

23456 
12345 
+0

Mis à jour ma question selon votre réponse. – Mark

+0

Vous semblez vouloir forcer multi_pass à fonctionner avec des itérateurs "réguliers". Bien. Vous pouvez juste vous [écrire votre propre politique d'entrée] (http://www.boost.org/doc/libs/1_59_0/libs/spirit/doc/html/spirit/support/multi_pass.html). Personnellement, je pense que c'est de la folie. Ça va tout rendre inefficace. Quelles autres bibliothèques connaissez-vous qui font que les flux se comportent comme des plages? – sehe

+0

Ajout d'une esquisse de ce que je ferais dans une API publique à ma réponse ([Live aussi] (http://coliru.stacked-crooked.com/a/0d3a1fc21c944744)) – sehe