簡體   English   中英

需要幫助將成員函數用作 Boost Spirit QI 的操作

[英]Need help using member functions as action with Boost Spirit QI

我無法讓成員函數綁定到語法定義中。 編譯錯誤結果。

簡而言之:

struct my_functor_word
{
  // This code
  void print ( std::string const& s, qi::unused_type, qi::unused_type ) const
  // Gives the compiler error seen below. 
  // This code works fine:
  // void operator()( std::string const& s, qi::unused_type, qi::unused_type ) const
  {
    std::cout << "word:" << s << std::endl;
  }
};

template <typename Iterator>
struct bd_parse_grammar : qi::grammar<Iterator>
{
  template <typename TokenDef> bd_parse_grammar( TokenDef const& tok )
    : bd_parse_grammar::base_type( start )
  {
    my_functor_word mfw;

    start =  *(
      // This code
      tok.word          [boost::bind(&my_functor_word::print, &mfw, qi::_1)]
      // gives:
      // {aka void (my_functor_word::*)(const std::basic_string<char>&, boost::spirit::unused_type, boost::spirit::unused_type) const}' is not a class, struct, or union type
      //       function_apply;
      //       ^~~~~~~~~~~~~~
      ///usr/include/boost/spirit/home/phoenix/core/detail/function_eval.hpp:126:13: error: 'boost::remove_reference<void (my_functor_word::*)(const std::basic_string<char>&, boost::spirit::unused_type, boost::spirit::unused_type) const>::type {aka void (my_functor_word::*)(const std::basic_string<char>&, boost::spirit::unused_type, boost::spirit::unused_type) const}' is not a class, struct, or union type
      //             type;
      //             ^~~~

      // This:
      // tok.word          [boost::bind(&my_functor_word::print, &mfw, qi::_1)]

      // similarly gives:
      // /usr/include/boost/bind/bind.hpp:69:37: error: 'void (my_functor_word::*)(const std::basic_string<char>&, boost::spirit::unused_type, boost::spirit::unused_type) const' is not a class, struct, or union type
      // typedef typename F::result_type type;
      
      // This works OK:
      // tok.word          [my_functor_word()] 
      ) ;
  }
  qi::rule<Iterator> start;
};

這是整個程序。 它使用函子而不是成員函數正確編譯和運行:

#include <boost/config/warning_disable.hpp>

#include <boost/spirit/include/qi.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_statement.hpp>
#include <boost/spirit/include/phoenix_container.hpp>
#include <boost/bind.hpp>

#include <iostream>
#include <string>

using namespace boost::spirit;
using namespace boost::spirit::ascii;

template <typename Lexer>
struct bd_parse_tokens : lex::lexer<Lexer>
{
  bd_parse_tokens()
  {
    // define patterns (lexer macros) to be used during token definition
    this->self.add_pattern( "WORD", "[a-zA-Z._]+" );

    // define tokens and associate them with the lexer
    word = "{WORD}";    // reference the pattern 'WORD' as defined above

    this->self.add ( word );
  }

  // the token 'word' exposes the matched string as its parser attribute
  lex::token_def<std::string> word;
};

///////////////////////////////////////////////////////////////////////////////
//  Grammar definition
///////////////////////////////////////////////////////////////////////////////

struct my_functor_word
{
  // This code
  void print ( std::string const& s, qi::unused_type, qi::unused_type ) const
  // Gives the compiler error seen below. 
  // This code works fine:
  // void operator()( std::string const& s, qi::unused_type, qi::unused_type ) const
  {
    std::cout << "word:" << s << std::endl;
  }
};

template <typename Iterator>
struct bd_parse_grammar : qi::grammar<Iterator>
{
  template <typename TokenDef> bd_parse_grammar( TokenDef const& tok )
    : bd_parse_grammar::base_type( start )
  {
    my_functor_word mfw;

    start =  *(
      // This code
      tok.word          [boost::bind(&my_functor_word::print, &mfw, qi::_1)]
      // gives:
      // {aka void (my_functor_word::*)(const std::basic_string<char>&, boost::spirit::unused_type, boost::spirit::unused_type) const}' is not a class, struct, or union type
      //       function_apply;
      //       ^~~~~~~~~~~~~~
      ///usr/include/boost/spirit/home/phoenix/core/detail/function_eval.hpp:126:13: error: 'boost::remove_reference<void (my_functor_word::*)(const std::basic_string<char>&, boost::spirit::unused_type, boost::spirit::unused_type) const>::type {aka void (my_functor_word::*)(const std::basic_string<char>&, boost::spirit::unused_type, boost::spirit::unused_type) const}' is not a class, struct, or union type
      //             type;
      //             ^~~~

      // This:
      // tok.word          [boost::bind(&my_functor_word::print, &mfw, qi::_1)]

      // similarly gives:
      // /usr/include/boost/bind/bind.hpp:69:37: error: 'void (my_functor_word::*)(const std::basic_string<char>&, boost::spirit::unused_type, boost::spirit::unused_type) const' is not a class, struct, or union type
      // typedef typename F::result_type type;
      
      // This works OK:
      // tok.word          [my_functor_word()] 
      ) ;
  }
  qi::rule<Iterator> start;
};
///////////////////////////////////////////////////////////////////////////////

int main( int argc, char* argv[] )
{
  //  Define the token type to be used: `std::string` is available as the
  //   type of the token attribute
  typedef lex::lexertl::token < char const*, boost::mpl::vector<std::string> > token_type;

  //  Define the lexer type to use implementing the state machine
  typedef lex::lexertl::lexer<token_type> lexer_type;

  //  Define the iterator type exposed by the lexer type */
  typedef bd_parse_tokens<lexer_type>::iterator_type iterator_type;

  // now we use the types defined above to create the lexer and grammar
  // object instances needed to invoke the parsing process
  bd_parse_tokens<lexer_type> bd_parse;          // Our lexer
  bd_parse_grammar<iterator_type> g( bd_parse ); // Our parser

  // read in the file int memory
  std::string str( argv[1] );
  char const* first = str.c_str();
  char const* last = &first[str.size()];

  bool r = lex::tokenize_and_parse( first, last, bd_parse, g );

  if ( ! r )
  {
    std::string rest( first, last );
    std::cerr << "Parsing failed\n" << "stopped at: \""
          << rest << "\"\n";
  }

  return 0;
}

這個難題有很多方面。

首先,要綁定成員函數,您必須傳遞額外的前導實例參數( this對象)。

其次,語義動作是Phoenix Actors,所以是deferred functors。 boost::bind可能不是你想要的你不能qi::_1_type調用my_functor_word::print 相反,您可以使用phoenix::bind ,在這種情況下,您甚至不必處理“魔術”上下文參數:

struct my_functor_word {
    void print(std::string const& s) const {
        std::cout << "word:" << s << std::endl;
    }
};

start = *(tok.word[ //
    boost::phoenix::bind(&my_functor_word::print, &mfw, qi::_1)]);

那是嗎?

如果沒有更多的觀察,我不會讓你走的。

首先,綁定仍然被打破! 您將mfw綁定為this實例,但mfw是一個局部變量。 語義操作的本質(如上面所說的延遲參與者)是它將在解析期間調用:在構造函數完成后很久 mfw需要是成員變量。 或者更好,根本不成為語法的一部分。 我建議

///////////////////////////////////////////////////////////////////////////////
//  Grammar definition
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct bd_parse_grammar : qi::grammar<Iterator>
{
    template <typename TokenDef>
    bd_parse_grammar(TokenDef const& tok) : bd_parse_grammar::base_type(start)
    {
        using namespace qi::labels;
        start = *tok.word[px::bind(&my_functor_word::print, &mfw, _1)];
    }

  private:
    struct my_functor_word {
        void print(std::string const& s)
        {
            std::cout << "word:" << s << std::endl;
        }
    };

    mutable my_functor_word mfw;
    qi::rule<Iterator>      start;
};
///////////////////////////////////////////////////////////////////////////////

我看到了很多老式和有問題的風格。 例如,為什么要包含boost/bind.hpp (甚至boost/bind/bind.hpp )甚至boost/lambda.hpp

你為什么使用 Lex?

您正在自由地使用using namespace ,這是一個壞主意,尤其是在混合所有這些庫時(實際上它們都有自己的占位符命名1 etc )。 相反,只需創建一些別名:

namespace qi  = boost::spirit::qi;
namespace lex = boost::spirit::lex;
namespace px  = boost::phoenix;

你有評論

// read in the file int memory

這與給出的代碼不匹配:

std::string str(argv[1]);
char const* first = str.c_str();
char const* last  = &first[str.size()];

周圍的一切都很奇怪。 為什么要使用帶有字符串的原始char* (它具有帶有begin()end()適當迭代器?)。 另外,也許您真的想讀取文件?

順便說一下,讓我們確保 `argv[1] 實際上有效

for (std::string fname : std::vector(argv + 1, argv + argc)) {
    std::ifstream     ifs(fname);

    // read in the file into memory
    std::string const str(std::istreambuf_iterator<char>(ifs), {});

    auto first = str.begin(), //
        last   = str.end();

    bool r = lex::tokenize_and_parse(first, last, bd_parse, g);

    if (!r) {
        std::string rest(first, last);
        std::cerr << "Parsing failed\n"
                  << "stopped at: \"" << rest << "\"\n";
    }
}

所以這是Coliru 上的現場演示

#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <fstream>
#include <iomanip>

namespace qi  = boost::spirit::qi;
namespace lex = boost::spirit::lex;
namespace px  = boost::phoenix;

template <typename Lexer> struct bd_parse_tokens : lex::lexer<Lexer> {
    bd_parse_tokens() {
        this->self.add_pattern("WORD", "[a-zA-Z._]+");
        word = "{WORD}";
        this->self.add(word);
    }

    // the token 'word' exposes the matched string as its parser attribute
    lex::token_def<std::string> word;
};

///////////////////////////////////////////////////////////////////////////////
//  Grammar definition
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct bd_parse_grammar : qi::grammar<Iterator>
{
    template <typename TokenDef>
    bd_parse_grammar(TokenDef const& tok) : bd_parse_grammar::base_type(start) {
        using namespace qi::labels;
        start = *tok.word[px::bind(&my_functor_word::print, &mfw, _1)];
    }

  private:
    struct my_functor_word {
        void print(std::string const& s) const { std::cout << "word:" << s << std::endl; }
    };

    mutable my_functor_word mfw;
    qi::rule<Iterator>      start;
};
///////////////////////////////////////////////////////////////////////////////

int main(int argc, char* argv[])
{
    // type of the token attribute
    using token_type    = lex::lexertl::token<std::string::const_iterator,
                                           boost::mpl::vector<std::string>>;
    using lexer_type    = lex::lexertl::/*actor_*/lexer<token_type>;
    using iterator_type = bd_parse_tokens<lexer_type>::iterator_type;

    bd_parse_tokens<lexer_type>     bd_parse;
    bd_parse_grammar<iterator_type> g(bd_parse);

    for (std::string fname : std::vector(argv + 1, argv + argc)) {
        std::ifstream ifs(fname);

        // read in the file into memory
        std::string const str(std::istreambuf_iterator<char>(ifs), {});

        auto first = str.begin();
        auto last  = str.end();

        bool ok = lex::tokenize_and_parse(first, last, bd_parse, g);

        std::cerr << "Parsing " << fname << " (length " << str.length() << ") "
                  << (ok ? "succeeded" : "failed") << "\n";

        if (first != last)
            std::cerr << "Stopped at #" << std::distance(str.begin(), first)
                      << "\n";
    }
}

印刷

word:includeboostspiritincludelex_lexertl.hppincludeboostspiritincludephoenix.hppincludeboostspiritincludeqi.hppincludefstreamincludeiomanipnamespaceqiboostspiritqinamespacelexboostspiritlexnamespacepxboostphoenixtemplatetypenameLexerstructbd_parse_tokenslexlexerLexerbd_parse_tokensthisself.add_patternWORDazAZ._wordWORDthisself.addwordthetokenwordexposesthematchedstringasitsparserattributelextoken_defstdstringwordGrammardefinitiontemplatetypenameIteratorstructbd_parse_grammarqigrammarIteratortemplatetypenameTokenDefbd_parse_grammarTokenDefconsttokbd_parse_grammarbase_typestartusingnamespaceqilabelsstarttok.wordpxbindmy_functor_wordprintmfw_privatestructmy_functor_wordvoidprintstdstringconstsconststdcoutwordsstdendlmutablemy_functor_wordmfwqiruleIteratorstartintmainintargccharargvtypeofthetokenattributeusingtoken_typelexlexertltokenstdstringconst_iteratorboostmplvectorstdstringusinglexer_typelexlexertlactor_lexertoken_typeusingiterator_typebd_parse_tokenslexer_typeiterator_typebd_parse_tokenslexer_typebd_parsebd_parse_grammariterator_typegbd_parseforstdstringfnamestdvectorargvargvargcstdifstreamifsfnamereadinthefileintomemorystdstringconststrstdistreambuf_iteratorcharifsautofirststr.beginautolaststr.endbooloklextokenize_and_parsefirstlastbd_parsegstdcerrParsingfnamelengthstr.lengthoksucceededfailedniffirstlaststdcerrStoppedatstddistancestr.beginfirstn
Parsing input.txt (length 1367) succeeded
Parsing main.cpp (length 2479) succeeded
Stopped at #0

沒有 Lex

我認為這會更簡單:

住在 Coliru http://coliru.stacked-crooked.com/a/7da12fcf1c360f75

#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <fstream>
#include <iomanip>

namespace qi  = boost::spirit::qi;
namespace px  = boost::phoenix;

template <typename Iterator>
struct bd_parse_grammar : qi::grammar<Iterator> {
    bd_parse_grammar() : bd_parse_grammar::base_type(start)
    {
        using namespace qi::labels;
        word  = +qi::char_("a-zA-Z_.");
        start = *word[px::bind(&my_functor_word::print, &mfw, _1)];
    }

  private:
    struct my_functor_word {
        void print(std::string const& s) const { std::cout << "word:" << s << std::endl; }
    };

    mutable my_functor_word mfw;
    qi::rule<Iterator>                start;
    qi::rule<Iterator, std::string()> word;
};

int main(int argc, char* argv[]) {
    bd_parse_grammar<std::string::const_iterator> const g;

    for (std::string fname : std::vector(argv + 1, argv + argc)) {
        std::ifstream ifs(fname);

        // read the file into memory
        std::string const str(std::istreambuf_iterator<char>(ifs), {});

        auto first = str.begin();
        auto last  = str.end();

        bool ok = qi::parse(first, last, g);

        std::cerr << "Parsing " << fname << " (length " << str.length() << ") "
                  << (ok ? "succeeded" : "failed") << "\n";

        if (first != last)
            std::cerr << "Stopped at #" << std::distance(str.begin(), first)
                      << "\n";
    }
}

相同的輸出

沒有鳳凰綁定

使用一些 C++17 CTAD 和 phoenix::function:

住在 Coliru

template <typename Iterator> struct Parser : qi::grammar<Iterator> {
    Parser() : Parser::base_type(start) {
        using namespace qi::labels;
        px::function print{[](std::string const& s) {
            std::cout << "word:" << s << std::endl;
        }};

        word  = +qi::char_("a-zA-Z_.");
        start = *word[print(_1)];
    }
    qi::rule<Iterator>                start;
    qi::rule<Iterator, std::string()> word;
};

原來的代碼只有一半。

使用 X3

如果您無論如何都在使用 C++14,請考慮減少編譯時間:

住在 Coliru

#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <fstream>
namespace x3 = boost::spirit::x3;

namespace Parser {
    auto print = [](auto& ctx) {
        std::cout << "word:" << _attr(ctx) << std::endl;
    };
    auto word  = +x3::char_("a-zA-Z_.");
    auto start = *word[print];
} // namespace Parser

int main(int argc, char* argv[]) {
    for (std::string fname : std::vector(argv + 1, argv + argc)) {
        std::ifstream ifs(fname);

        // read the file into memory
        std::string const str(std::istreambuf_iterator<char>(ifs), {});

        auto first = str.begin();
        auto last  = str.end();

        bool ok = x3::parse(first, last, Parser::start);

        std::cerr << "Parsing " << fname << " (length " << str.length() << ") "
                  << (ok ? "succeeded" : "failed") << "\n";

        if (first != last)
            std::cerr << "Stopped at #" << std::distance(str.begin(), first) << "\n";
    }
}

還是一樣的輸出。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM