gpt4 book ai didi

c++ - 卡住了移植旧版boost::spirit代码

转载 作者:行者123 更新时间:2023-12-02 09:53:24 25 4
gpt4 key购买 nike

我正在将一些旧代码从VS2010&boost1.53移植到VS2017&boost1.71。

我在尝试编译它的最后两个小时被卡住了。

代码是:

#include <string>
#include <vector>
#include <fstream>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

namespace qi = boost::spirit::qi;
using qi::_1; using qi::_2; using qi::_3; using qi::_4;

enum TYPE { SEND, CHECK, COMMENT };

struct Command
{
TYPE type;
std::string id;
std::string arg1;
std::string arg2;
bool checking;
};

class Parser
{
typedef boost::spirit::istream_iterator It;
typedef std::vector<Command> Commands;

struct deferred_fill
{
template <typename R, typename S, typename T, typename U> struct result { typedef void type; };//Not really sure still necessary
typedef void result_type;//Not really sure still necessary

void operator() (boost::iterator_range<It> const& id, boost::iterator_range<It> const& arg1, bool checking, Command& command) const
{
command.type = TYPE::SEND;
command.id.assign(id.begin(), id.end());
command.arg1.assign(arg1.begin(), arg1.end());
command.checking = checking;
}
};

private:
qi::symbols<char, bool> value;
qi::rule<It> ignore;
qi::rule<It, Command()> send;
qi::rule<It, Commands()> start;
boost::phoenix::function<deferred_fill> fill;

public:
std::vector<Command> commands;

Parser()
{
using namespace qi;
using boost::phoenix::push_back;

value.add("TRUE", true)
("FALSE", false);

send = ("SEND_CONFIRM" >> *blank >> '(' >> *blank >> raw[*~char_(',')] >> ','
>> *blank >> raw[*~char_(',')] >> ','
>> *blank >> value >> *blank >> ')' >> *blank >> ';')[fill(_1, _2, _3, _val)];

ignore = *~char_("\r\n");

start = (send[push_back(_val, _1)] | ignore) % eol;
}

void parse(const std::string& path)
{
std::ifstream in(path, std::ios_base::in);
if (!in) return;

in >> std::noskipws;//No white space skipping
boost::spirit::istream_iterator first(in);
boost::spirit::istream_iterator last;

qi::parse(first, last, start, commands);
}
};

int main(int argc, char* argv[])
{
Parser parser;
parser.parse("file.txt");

return 0;
}

编译器以另一种方式抱怨(仅复制第一行):
1>z:\externos\boost_1_71_0\boost\phoenix\core\detail\function_eval.hpp(116): error C2039: 'type': no es un miembro de 'boost::result_of<const Parser::deferred_fill (std::vector<Value,std::allocator<char>> &,std::vector<Value,std::allocator<char>> &,boost::iterator_range<Parser::It> &,Command &)>'
1> with
1> [
1> Value=char
1> ]
1>z:\externos\boost_1_71_0\boost\phoenix\core\detail\function_eval.hpp(114): note: vea la declaración de 'boost::result_of<const Parser::deferred_fill (std::vector<Value,std::allocator<char>> &,std::vector<Value,std::allocator<char>> &,boost::iterator_range<Parser::It> &,Command &)>'
1> with
1> [
1> Value=char
1> ]
1>z:\externos\boost_1_71_0\boost\phoenix\core\detail\function_eval.hpp(89): note: vea la referencia a la creación de instancias de plantilla clase de 'boost::phoenix::detail::function_eval::result_impl<F,void (Head,const boost::phoenix::actor<boost::spirit::argument<1>>&,const boost::phoenix::actor<boost::spirit::argument<2>>&,const boost::phoenix::actor<boost::spirit::attribute<0>>&),const boost::phoenix::vector2<Env,Actions> &>' que se está compilando
1> with
1> [
1> F=const boost::proto::exprns_::basic_expr<boost::proto::tagns_::tag::terminal,boost::proto::argsns_::term<Parser::deferred_fill>,0> &,
1> Head=const boost::phoenix::actor<boost::spirit::argument<0>> &,
1> Env=boost::phoenix::vector4<const boost::phoenix::actor<boost::proto::exprns_::basic_expr<boost::phoenix::detail::tag::function_eval,boost::proto::argsns_::list5<boost::proto::exprns_::basic_expr<boost::proto::tagns_::tag::terminal,boost::proto::argsns_::term<Parser::deferred_fill>,0>,boost::phoenix::actor<boost::spirit::argument<0>>,boost::phoenix::actor<boost::spirit::argument<1>>,boost::phoenix::actor<boost::spirit::argument<2>>,boost::phoenix::actor<boost::spirit::attribute<0>>>,5>> *,boost::fusion::vector<std::vector<char,std::allocator<char>>,std::vector<char,std::allocator<char>>,boost::iterator_range<Parser::It>,std::vector<char,std::allocator<char>>,boost::iterator_range<Parser::It>,std::vector<char,std::allocator<char>>,bool,std::vector<char,std::allocator<char>>,std::vector<char,std::allocator<char>>> &,boost::spirit::context<boost::fusion::cons<Command &,boost::fusion::nil_>,boost::fusion::vector<>> &,bool &> &,
1> Actions=const boost::phoenix::default_actions &
1> ]

我猜该错误与使用boost::spirit::istream_iterator而不是char *有关,但是我不知道如何修复它才能再次工作。

我的想法已经用完了,任何人都可以看到我的错误在哪里?

最佳答案

w你在做很棒的事情。不幸的是,它过于复杂。

因此,让我们先修复,然后简化。

错误

就像你说的

void operator() (boost::iterator_range<It> const& id, boost::iterator_range<It> const& arg1, bool checking, Command& command) const

与实际调用的内容不匹配:

void Parser::deferred_fill::operator()(T&& ...) const [with T =
{std::vector<char>&, std::vector<char>&,
boost::iterator_range<boost::spirit::basic_istream_iterator<...> >&,
Command&}]



原因不是迭代器(如您所见,它是 boost::spirit__istream_iterator)。

但这是因为您正在获取其他东西作为属性。原来 *blank将属性公开为 vector<char>。因此,您可以通过 omit[]对这些代码进行“修复”。让我们将其包装在诸如 ignore的无属性规则中,以减少困惑。

现在调用与

void Parser::deferred_fill::operator()(T&& ...) const [with T = {boost::iterator_range<It>&, boost::iterator_range<It>&, bool&, Command&}]



因此它是兼容的并且可以编译。解析:
SEND_CONFIRM("this is the id part", "this is arg1", TRUE);


Parser parser;
parser.parse("file.txt");

std::cout << std::boolalpha;
for (auto& cmd : parser.commands) {
std::cout << '{' << cmd.id << ", "
<< cmd.arg1 << ", "
<< cmd.arg2 << ", "
<< cmd.checking << "}\n";
}

版画
{"this is the id part", "this is arg1", , TRUE}

让我们改善这个
  • 这需要一个船长
  • 这要求自动传播属性
  • 样式
  • 的其他一些元素

    船长

    让我们使用内置功能,而不是显式地“调用”一个船长:
    rule<It, Attr(), Skipper> x;

    定义一个规则,该规则跳过由 Skipper类型的解析器匹配的输入序列。您实际上需要传递该类型的船长。
  • 使用qi::phrase_parse代替qi::parse
  • 通过使用 qi::skip()指令来实现


  • 我一直提倡第二种方法,因为它使界面更友好,更不易出错。

    因此,声明船长类型:
    qi::rule<It, Command(), qi::blank_type> send;

    我们可以将规则简化为:
        send = (lit("SEND_CONFIRM") >> '(' 
    >> raw[*~char_(',')] >> ','
    >> raw[*~char_(',')] >> ','
    >> value >> ')' >> ';')
    [fill(_1, _2, _3, _val)];

    而不是通过start规则传递一个船长:
        start = skip(blank) [
    (send[push_back(_val, _1)] | ignore) % eol
    ];

    就这样。仍然编译和匹配相同。

    Live On Coliru

    跳过词缀

    仍然是同一主题,lexemes实际上禁止了船长¹,因此您不必raw[]。这也将公开的属性更改为vector<char>:
    void operator() (std::vector<char> const& id, std::vector<char> const& arg1, bool checking, Command& command) const

    Live On Coliru

    自动属性传播

    Qi具有语义 Action ,但其真正的优势在于它们是可选的:Boost Spirit: "Semantic actions are evil"?
  • push_back(_val, _1)实际上是*p+pp % delim²的自动属性传播语义,无论如何,因此请将其删除:
    start = skip(blank) [
    (send | ignore) % eol
    ];

    (请注意send|ignore实际上合成了optional<Command>,这对于自动传播非常有用)
  • std::vectorstd::string属性兼容,例如。因此,如果我们可以为arg2添加一个占位符,则可以匹配Command结构布局:
    send = lit("SEND_CONFIRM") >> '(' 
    >> attr(SEND) // fill type
    >> lexeme[*~char_(',')] >> ','
    >> lexeme[*~char_(',')] >> ','
    >> attr(std::string()) // fill for arg2
    >> value >> ')' >> ';'
    ;

    现在,为了删除fill及其实现,我们必须将Command修改为融合序列:
    BOOST_FUSION_ADAPT_STRUCT(Command, type, id, arg1, arg2, checking)

  • 风格1的要素

    为您的Command类型使用 namespace 可以使ADL轻松使用Command的 operator<<重载,例如,我们可以 std::cout << cmd;

    At this point, it all works in a fraction of the code: Live On Coliru



    风格2的元素
  • 如果可以,请使解析器为无状态。这意味着它可以是const,因此您可以:
  • 无需昂贵的构造即可重用它
  • 优化器还有更多与
  • 一起使用的功能
  • 更具可测试性(有状态的东西更难证明幂等)

  • 因此,与其让 commands成为成员,不如将它们返回。在此过程中,我们可以将 parse设为静态函数
  • 不用对迭代器类型进行硬编码,可以灵活地将其作为模板参数。这样,如果某个时候在multi_pass_adaptor缓冲区,istream_iteratorchar[]中有命令,那么您就不会受stringstring_view的开销的困扰。
  • 同样,使用合适的入口点从qi::grammar派生您的解析器意味着您可以将其用作解析器表达式(实际上是a non-terminal,就像rule<>一样),就像任何其他解析器一样。
  • 考虑启用规则调试(请参见示例)

  • 完整代码

    Live On Coliru
    #define BOOST_SPIRIT_DEBUG
    #include <boost/spirit/include/qi.hpp>
    #include <fstream>

    namespace qi = boost::spirit::qi;

    namespace Commands {
    enum TYPE { SEND, CHECK, COMMENT };
    enum BOOL { FALSE, TRUE };

    struct Command {
    TYPE type;
    std::string id;
    std::string arg1;
    std::string arg2;
    BOOL checking;
    };

    typedef std::vector<Command> Commands;

    // for (debug) output
    static inline std::ostream& operator<<(std::ostream& os, TYPE t) {
    switch (t) {
    case SEND: return os << "SEND";
    case CHECK: return os << "CHECK";
    case COMMENT: return os << "COMMENT";
    }
    return os << "(unknown)";
    }
    static inline std::ostream& operator<<(std::ostream& os, BOOL b) {
    return os << (b?"TRUE":"FALSE");
    }

    using boost::fusion::operator<<;
    }
    BOOST_FUSION_ADAPT_STRUCT(Commands::Command, type, id, arg1, arg2, checking)

    namespace Commands {
    template <typename It>
    class Parser : public qi::grammar<It, Commands()> {
    public:
    Commands commands;

    Parser() : Parser::base_type(start) {
    using namespace qi;

    value.add("TRUE", TRUE)
    ("FALSE", FALSE);

    send = lit("SEND_CONFIRM") >> '('
    >> attr(SEND) // fill type
    >> lexeme[*~char_(',')] >> ','
    >> lexeme[*~char_(',')] >> ','
    >> attr(std::string()) // fill for arg2
    >> value >> ')' >> ';'
    ;

    ignore = +~char_("\r\n");

    start = skip(blank) [
    (send | ignore) % eol
    ];

    BOOST_SPIRIT_DEBUG_NODES((start)(send)(ignore))
    }

    private:
    qi::symbols<char, BOOL> value;
    qi::rule<It> ignore;
    qi::rule<It, Command(), qi::blank_type> send;
    qi::rule<It, Commands()> start;
    };

    static Commands parse(std::istream& in) {
    using It = boost::spirit::istream_iterator;

    static const Parser<It> parser;

    It first(in >> std::noskipws), //No white space skipping
    last;

    Commands commands;
    if (!qi::parse(first, last, parser, commands)) {
    throw std::runtime_error("command parse error");
    }

    return commands; // c++11 move semantics
    }
    }

    int main() {
    try {
    for (auto& cmd : Commands::parse(std::cin))
    std::cout << cmd << "\n";
    } catch(std::exception const& e) {
    std::cout << e.what() << "\n";
    }
    }

    版画
    (SEND "this is the id part" "this is arg1"  TRUE)

    或者确实定义了BOOST_SPIRIT_DEBUG:

    <start>
    <try>SEND_CONFIRM("this i</try>
    <send>
    <try>SEND_CONFIRM("this i</try>
    <success>\n</success>
    <attributes>[[SEND, [", t, h, i, s, , i, s, , t, h, e, , i, d, , p, a, r, t, "], [", t, h, i, s, , i, s, , a, r, g, 1, "], [], TRUE]]</attributes>
    </send>
    <send>
    <try></try>
    <fail/>
    </send>
    <ignore>
    <try></try>
    <fail/>
    </ignore>
    <success>\n</success>
    <attributes>[[[SEND, [", t, h, i, s, , i, s, , t, h, e, , i, d, , p, a, r, t, "], [", t, h, i, s, , i, s, , a, r, g, 1, "], [], TRUE]]]</attributes>
    </start>

    ¹同时根据需要进行预跳过;见 Boost spirit skipper issues

    ²(然后是一些,但我们不要离题)

    关于c++ - 卡住了移植旧版boost::spirit代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62434203/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com