簡體   English   中英

增強精神2:按照qi :: parser的進度百分比。 我的代碼有什么不好?

[英]boost spirit 2 : following progression percentage on qi::parser. What's bad in my code?

我使用Boost Spirit 2進行開發,我嘗試遵循示例以在我的pgn解析器中獲得進展(稍后將添加語義動作)(另請參閱相關的先前問題 )。 但是我無法避免編譯錯誤: cpp

#include "pgn_games_extractor.h"
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <tuple>

#include <iostream>

BOOST_FUSION_ADAPT_STRUCT(loloof64::pgn_tag, key, value)
BOOST_FUSION_ADAPT_STRUCT(loloof64::game_move, move_number, white_move, black_move, result)
BOOST_FUSION_ADAPT_STRUCT(loloof64::pgn_game, header, moves)

namespace loloof64 {
namespace qi = boost::spirit::qi;

typedef std::tuple<std::size_t, game_move> move_t;
typedef std::tuple<std::vector<pgn_tag>, std::vector<move_t>> game_t;
typedef std::tuple<std::size_t, std::vector<game_t>> pgn_t;

template <typename Iterator> struct pgn_parser : qi::grammar<Iterator, std::vector<pgn_game>, qi::space_type> {
    pgn_parser() : pgn_parser::base_type(games) {
        using namespace qi;

        CurrentPos<Iterator> filepos;

        const std::string no_move;
        result.add
            ("1-0",     result_t::white_won)
            ("0-1",     result_t::black_won)
            ("1/2-1/2", result_t::draw)
            ("*",       result_t::undecided);

        quoted_string    = '"' >> *~char_('"') >> '"';
        tag              = '[' >> +alnum >> quoted_string >> ']';
        header           = +tag;
        regular_move     = lit("O-O-O") | "O-O" | (+char_("a-hNBRQK") >> +char_("a-h1-8x=NBRQK") >> -lit("e.p."));
        single_move      = raw [ regular_move >> -char_("+#") ];
        full_move        = filepos.current_pos >> uint_
            >> (lexeme["..." >> attr(no_move)] | "." >> single_move)
            >> (single_move | attr(no_move))
            >> -result;

        game_description = +full_move;
        single_game      = -header >> game_description;
        games            = filepos.save_start_pos >> *single_game;

        BOOST_SPIRIT_DEBUG_NODES(
                    (tag)(header)(quoted_string)(regular_move)(single_move)
                    (full_move)(game_description)(single_game)(games)
                )
    }

  private:
    qi::rule<Iterator, pgn_tag(),              qi::space_type> tag;
    qi::rule<Iterator, std::vector<pgn_tag>,   qi::space_type> header;

    qi::rule<Iterator, move_t(),               qi::space_type> full_move;
    qi::rule<Iterator, std::vector<move_t>,    qi::space_type> game_description;

    qi::rule<Iterator, game_t(),               qi::space_type> single_game;
    qi::rule<Iterator, pgn_t(),  qi::space_type> games;

    // lexemes
    qi::symbols<char, result_t> result;
    qi::rule<Iterator, std::string()> quoted_string;
    qi::rule<Iterator> regular_move;
    qi::rule<Iterator, std::string()> single_move;
};
}

loloof64::PgnGamesExtractor::PgnGamesExtractor(std::string inputFilePath) {
    std::ifstream inputFile(inputFilePath);
    parseInput(inputFile);
}

loloof64::PgnGamesExtractor::PgnGamesExtractor(std::istream &inputFile) { parseInput(inputFile); }

loloof64::PgnGamesExtractor::~PgnGamesExtractor() {
    // dtor
}

void loloof64::PgnGamesExtractor::parseInput(std::istream &inputFile) {
    if (inputFile.fail() || inputFile.bad())
        throw new InputFileException("Could not read the input file !");

    typedef boost::spirit::istream_iterator It;
    loloof64::pgn_parser<It> parser;
    std::vector<loloof64::pgn_game> temp_games;

    It iter(inputFile >> std::noskipws), end;

    //////////////////////////////////
    std::cout << "About to parse the file" << std::endl;
    //////////////////////////////////

    bool success = boost::spirit::qi::phrase_parse(iter, end, parser, boost::spirit::qi::space, temp_games);

    //////////////////////////////////
    std::cout << "Finished to parse the file" << std::endl;
    //////////////////////////////////

    if (success && iter == end) {
        games.swap(temp_games);
    } else {
        std::string error_fragment(iter, end);
        throw PgnParsingException("Failed to parse the input at :'" + error_fragment + "' !");
    }
}

和頭文件: header

#ifndef PGNGAMESEXTRACTOR_HPP
#define PGNGAMESEXTRACTOR_HPP

#include <string>
#include <vector>
#include <fstream>
#include <stdexcept>

#include <boost/fusion/adapted/std_tuple.hpp>
#include <boost/spirit/include/phoenix.hpp>

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

namespace loloof64 {

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


    /*
     * This class has been taken from http://marko-editor.com/articles/position_tracking/
    */
    template<typename Iterator>
    struct CurrentPos {
      CurrentPos() {
        save_start_pos = qi::omit[boost::spirit::repository::qi::iter_pos[
                phx::bind(&CurrentPos::setStartPos, this, qi::_1)]];
        current_pos = boost::spirit::repository::qi::iter_pos[
                qi::_val = phx::bind(&CurrentPos::getCurrentPos, this, qi::_1)];
      }

      qi::rule<Iterator> save_start_pos;
      qi::rule<Iterator, std::size_t()> current_pos;

    private:
      void setStartPos(const Iterator &iterator) {
        start_pos_ = iterator;
      }

      std::size_t getCurrentPos(const Iterator &iterator) {
        return std::distance(start_pos_, iterator);
      }

      Iterator start_pos_;
    };

    enum result_t { white_won, black_won, draw, undecided };

    struct pgn_tag {
        std::string key;
        std::string value;
    };

    struct game_move {
        unsigned move_number;
        std::string white_move;
        std::string black_move;
        result_t result;
    };

    struct pgn_game {
        std::vector<pgn_tag> header;
        std::vector<game_move> moves;
    };

    class PgnGamesExtractor {
      public:
        PgnGamesExtractor(std::string inputFilePath);
        PgnGamesExtractor(std::istream &inputFile);
        /*
        Both constructos may throw PgnParsingException (if bad pgn format) and InputFileException (if missing file)
        */
        std::vector<pgn_game> getGames() const { return games; }
        virtual ~PgnGamesExtractor();

      protected:
      private:
        std::vector<pgn_game> games;
        void parseInput(std::istream &inputFile);
    };

    class PgnParsingException : public virtual std::runtime_error {
      public:
        PgnParsingException(std::string message) : std::runtime_error(message) {}
    };

    class InputFileException : public virtual std::runtime_error {
      public:
        InputFileException(std::string message) : std::runtime_error(message) {}
    };
}

#endif // PGNGAMESEXTRACTOR_HPP

我沒有發布編譯錯誤,因為有太多錯誤並且可以輕松測試文件。

當然,它不能與流接口一起很好地工作。 可以保留啟動迭代器,但是

  1. 您將不會提前知道流的長度(除非您將其帶外)

  2. 每次計算當前位置(與起始迭代器的距離)都會非常低效。

既然您在注釋中提到過您正在解析文件,則應考慮使用內存映射(例如boost::iostream::mapped_file_sourcemmap )。 這樣,在隨機訪問迭代器上使用指針算法即可立即進行距離計算。


這是一個工作示例,具有以下更改/注釋:

  1. 使用內存映射的輸入數據3
  2. save_start_pos omit[]是無用的(沒有聲明的屬性)
  3. getCurrentPos效率低下(以至於在full_move規則中僅使用omit[current_pos] full_move解析速度降低了幾個數量級。

    這是因為boost::spirit::istream_iterator以雙端隊列保持所有先前讀取的狀態,並且在執行std::distance時遍歷它們並不是免費的

  4. 您的CurrentPos<Iterator> filepos; 實例在構建后超出范圍! 這意味着調用save_start_pos / current_pos未定義行為 ¹。 將其移出構造函數。

  5. 當您添加語義操作時, full_move %= ...一點是使用full_move %= ... (請參閱docsblog

  6. 您更改了某些規則的類型,以在AST類型旁邊包含位置信息。 這既沒有必要,也有缺陷:AST類型與規則的tuple<size_t, T>版本不兼容。

    此外,例如, games規則甚至都沒有公開位置,因為save_start_pos合成未使用的unused_type (無屬性)。

    因此,刪除整個元組業務,然后在您的語義動作中處理filepos成員的狀態:

      full_move %= omit[filepos.current_pos [ reportProgress(_1) ]] >> uint_ >> (lexeme["..." >> attr(no_move)] | "." >> single_move) >> (single_move | attr(no_move)) >> -result; 
  7. 最后,作為一個演示如何報告嚴格增加的進度指示²,我包括了一個簡單的鳳凰演員:

     struct reportProgress_f { size_t total_; mutable double pct = 0.0; reportProgress_f(size_t total) : total_(total) {} template<typename T> void operator()(T pos) const { double newpct = pos * 100.0 / total_; if ((newpct - pct) > 10) { //sleep(1); // because it's way too fast otherwise... pct = newpct; std::cerr << "\\rProgress " << std::fixed << std::setprecision(1) << pct << std::flush; }; } }; phx::function<reportProgress_f> reportProgress; 

    注意 reportProgress需要使用有關開始和結束迭代器的知識來構造,請參見pgn_parser的構造pgn_parser


¹在錄制的實時流中,您可以看到我在第一次閱讀時發現了錯誤,然后在進行編譯后就忘記了。 該程序崩潰,盡職:)然后我想起來了。

²即使面對回溯

3 (並非嚴格要求,但我想目標不是要使其變得如此緩慢以至於您實際上需要進度指示器嗎?)

生活在Coliru

#ifndef PGNGAMESEXTRACTOR_HPP
#define PGNGAMESEXTRACTOR_HPP

#include <string>
#include <vector>
#include <fstream>
#include <stdexcept>

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/repository/include/qi_iter_pos.hpp>

namespace loloof64 {

    namespace phx = boost::phoenix;
    namespace qi  = boost::spirit::qi;
    namespace qr  = boost::spirit::repository::qi;

    /*
     * This class has been taken from http://marko-editor.com/articles/position_tracking/
    */
    template<typename Iterator>
    struct CurrentPos {
        CurrentPos() {
            save_start_pos = qr::iter_pos [phx::bind(&CurrentPos::setStartPos, this, qi::_1)] >> qi::eps;
            current_pos    = qr::iter_pos [qi::_val = phx::bind(&CurrentPos::getCurrentPos, this, qi::_1)] >> qi::eps;
        }

        qi::rule<Iterator> save_start_pos;
        qi::rule<Iterator, std::size_t()> current_pos;

        private:
        void setStartPos(const Iterator &iterator) {
            start_pos_ = iterator;
        }

        std::size_t getCurrentPos(const Iterator &iterator) {
            return std::distance(start_pos_, iterator);
        }

        Iterator start_pos_;
    };

    enum result_t { white_won, black_won, draw, undecided };

    struct pgn_tag {
        std::string key;
        std::string value;
    };

    struct game_move {
        unsigned move_number;
        std::string white_move;
        std::string black_move;
        result_t result;
    };

    struct pgn_game {
        std::vector<pgn_tag> header;
        std::vector<game_move> moves;
    };

    class PgnGamesExtractor {
      public:
        PgnGamesExtractor(std::string const& inputFilePath);
        /*
        Both constructos may throw PgnParsingException (if bad pgn format) and InputFileException (if missing file)
        */
        std::vector<pgn_game> getGames() const { return games; }
        virtual ~PgnGamesExtractor();

      protected:
      private:
        std::vector<pgn_game> games;
        void parseInput(std::string const&);
    };

    class PgnParsingException : public virtual std::runtime_error {
      public:
        PgnParsingException(std::string message) : std::runtime_error(message) {}
    };

    class InputFileException : public virtual std::runtime_error {
      public:
        InputFileException(std::string message) : std::runtime_error(message) {}
    };
}

#endif // PGNGAMESEXTRACTOR_HPP
//#include "pgn_games_extractor.h"

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

#include <iostream>
#include <iomanip>

BOOST_FUSION_ADAPT_STRUCT(loloof64::pgn_tag, key, value)
BOOST_FUSION_ADAPT_STRUCT(loloof64::game_move, move_number, white_move, black_move, result)
BOOST_FUSION_ADAPT_STRUCT(loloof64::pgn_game, header, moves)

namespace loloof64 {
    namespace qi = boost::spirit::qi;

    template <typename Iterator> struct pgn_parser : qi::grammar<Iterator, std::vector<pgn_game>(), qi::space_type> {
        pgn_parser(Iterator start, Iterator end) 
            : pgn_parser::base_type(games),
              reportProgress(std::distance(start, end))
        {
            using namespace qi;

            const std::string no_move;
            result.add
                ("1-0",     result_t::white_won)
                ("0-1",     result_t::black_won)
                ("1/2-1/2", result_t::draw)
                ("*",       result_t::undecided);

            quoted_string    = '"' >> *~char_('"') >> '"';
            tag              = '[' >> +alnum >> quoted_string >> ']';
            header           = +tag;
            regular_move     = lit("O-O-O") | "O-O" | (+char_("a-hNBRQK") >> +char_("a-h1-8x=NBRQK") >> -lit("e.p."));
            single_move      = raw [ regular_move >> -char_("+#") ];
            full_move       %=
                                omit[filepos.current_pos [ reportProgress(_1) ]]  >> 
                                uint_
                                >> (lexeme["..." >> attr(no_move)] | "." >> single_move)
                                >> (single_move | attr(no_move))
                                >> -result;

            game_description = +full_move;
            single_game      = -header >> game_description;
            games            = filepos.save_start_pos >> *single_game;

            BOOST_SPIRIT_DEBUG_NODES(
                        (tag)(header)(quoted_string)(regular_move)(single_move)
                        (full_move)(game_description)(single_game)(games)
                    )
        }

    private:
        struct reportProgress_f {
            size_t total_;
            mutable double pct = 0.0;

            reportProgress_f(size_t total) : total_(total) {}

            template<typename T>
            void operator()(T pos) const { 
                double newpct = pos * 100.0 / total_;
                if ((newpct - pct) > 10) {
                    //sleep(1); // because it's way too fast otherwise...
                    pct = newpct;
                    std::cerr << "\rProgress " << std::fixed << std::setprecision(1) << pct << "    " << std::flush;
                };
            }
        };
        phx::function<reportProgress_f> reportProgress;

        CurrentPos<Iterator> filepos;

        qi::rule<Iterator, pgn_tag(),               qi::space_type> tag;
        qi::rule<Iterator, std::vector<pgn_tag>,    qi::space_type> header;

        qi::rule<Iterator, game_move(),             qi::space_type> full_move;
        qi::rule<Iterator, std::vector<game_move>,  qi::space_type> game_description;

        qi::rule<Iterator, pgn_game(),              qi::space_type> single_game;
        qi::rule<Iterator, std::vector<pgn_game>(), qi::space_type> games;

        // lexemes
        qi::symbols<char, result_t> result;
        qi::rule<Iterator, std::string()> quoted_string;
        qi::rule<Iterator> regular_move;
        qi::rule<Iterator, std::string()> single_move;
    };
}

#include <boost/iostreams/device/mapped_file.hpp>

loloof64::PgnGamesExtractor::~PgnGamesExtractor() {
    // dtor
}

loloof64::PgnGamesExtractor::PgnGamesExtractor(std::string const& inputFilePath) {
    parseInput(inputFilePath);
}

void loloof64::PgnGamesExtractor::parseInput(std::string const& inputFilePath) {
    boost::iostreams::mapped_file_source mf(inputFilePath);

    //if (inputFile.fail() || inputFile.bad())
        //throw new InputFileException("Could not read the input file !");

    typedef char const* It;
    std::vector<loloof64::pgn_game> temp_games;

    /* It iter(inputFile >> std::noskipws), end; */
    auto iter = mf.begin();
    auto end  = mf.end();
    loloof64::pgn_parser<It> parser(iter, end);

    //////////////////////////////////
    //std::cout << "About to parse the file" << std::endl;
    //////////////////////////////////

    bool success = boost::spirit::qi::phrase_parse(iter, end, parser, boost::spirit::qi::space, temp_games);

    //////////////////////////////////
    //std::cout << "Finished to parse the file" << std::endl;
    //////////////////////////////////

    if (success && iter == end) {
        games.swap(temp_games);
    } else {
        std::string error_fragment(iter, end);
        throw PgnParsingException("Failed to parse the input at :'" + error_fragment + "' !");
    }
}

int main() {
    loloof64::PgnGamesExtractor pge("ScotchGambit.pgn");
    std::cout << "Parsed " << pge.getGames().size() << " games\n";
    for (auto& g : pge.getGames())
        for (auto& m : g.moves)
            std::cout << m.move_number << ".\t" << m.white_move << "\t" << m.black_move << "\n";
}

帶樣品輸出

Progress 32.6
Progress 44.5
Progress 55.5
Progress 67.2
Progress 77.2
Progress 89.1
Progress 100.0Parsed 1 games
1.  e4  e5
2.  Nf3 Nc6
3.  d4  exd4
4.  Bc4 Qf6
5.  O-O d6
6.  Ng5 Nh6
7.  f4  Be7
8.  e5  Qg6
9.  exd6    cxd6
10. c3  dxc3
11. Nxc3    O-O
12. Nd5 Bd7
13. Rf3 Bg4
14. Bd3 Bxf3
15. Qxf3    f5
16. Bc4 Kh8
17. Nxe7    Nxe7
18. Qxb7    Qf6
19. Be3 Rfb8
20. Qd7 Rd8
21. Qb7 d5
22. Bb3 Nc6
23. Bxd5    Nd4
24. Rd1 Ne2+
25. Kf1 Rab8
26. Qxa7    Rxb2
27. Ne6 Qxe6
28. Bxe6    Rxd1+
29. Kf2 

請注意,在終端上,進度指示將使用回車鍵進行自我更新,而不是打印單獨的行

通過遵循此Sehe視頻教程解決了該問題。另外,應該注意的是,由於這次他使用的是boost :: iostreams :: mapped_file_source而不是我使用的ifstream,因此該過程確實在加速! 因此,此過程不再需要進度條。

Cpp文件Hpp文件

暫無
暫無

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

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