<i id='YeYjN'><tr id='YeYjN'><dt id='YeYjN'><q id='YeYjN'><span id='YeYjN'><b id='YeYjN'><form id='YeYjN'><ins id='YeYjN'></ins><ul id='YeYjN'></ul><sub id='YeYjN'></sub></form><legend id='YeYjN'></legend><bdo id='YeYjN'><pre id='YeYjN'><center id='YeYjN'></center></pre></bdo></b><th id='YeYjN'></th></span></q></dt></tr></i><div id='YeYjN'><tfoot id='YeYjN'></tfoot><dl id='YeYjN'><fieldset id='YeYjN'></fieldset></dl></div>

      <small id='YeYjN'></small><noframes id='YeYjN'>

    1. <tfoot id='YeYjN'></tfoot>
    2. <legend id='YeYjN'><style id='YeYjN'><dir id='YeYjN'><q id='YeYjN'></q></dir></style></legend>

      • <bdo id='YeYjN'></bdo><ul id='YeYjN'></ul>

      如何在 boost::spirit::qi 解析器中使用多态属性?

      时间:2023-06-29
      1. <i id='iGb2Y'><tr id='iGb2Y'><dt id='iGb2Y'><q id='iGb2Y'><span id='iGb2Y'><b id='iGb2Y'><form id='iGb2Y'><ins id='iGb2Y'></ins><ul id='iGb2Y'></ul><sub id='iGb2Y'></sub></form><legend id='iGb2Y'></legend><bdo id='iGb2Y'><pre id='iGb2Y'><center id='iGb2Y'></center></pre></bdo></b><th id='iGb2Y'></th></span></q></dt></tr></i><div id='iGb2Y'><tfoot id='iGb2Y'></tfoot><dl id='iGb2Y'><fieldset id='iGb2Y'></fieldset></dl></div>

        1. <legend id='iGb2Y'><style id='iGb2Y'><dir id='iGb2Y'><q id='iGb2Y'></q></dir></style></legend>

              <tbody id='iGb2Y'></tbody>
              <bdo id='iGb2Y'></bdo><ul id='iGb2Y'></ul>

                <tfoot id='iGb2Y'></tfoot>

                <small id='iGb2Y'></small><noframes id='iGb2Y'>

                本文介绍了如何在 boost::spirit::qi 解析器中使用多态属性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着跟版网的小编来一起学习吧!

                问题描述

                我希望我的基于 boost::spirit 的解析器能够解析文件,将解析的规则转换为不同的类型,并发出一个包含它找到的所有匹配项的向量.所有作为属性发出的类型都应该从基类型继承,例如:

                #include #include #include #include 结构体命令库{虚拟无效 commandAction(){std::cout <<这是一个基本命令.你永远不应该看到这个!"<<std::endl;//Boost::spirit 如果我把它变成纯虚拟的,似乎会生气.显然我做错了.}};struct CommandTypeA : 公共命令库{整数值A;整数值B;虚拟无效 commandAction(){std::cout <<命令类型 A!值 A:" <<值A<<" 值 B: " <<值B<<std::endl;}};struct CommandTypeB : 公共命令库{双值A;std::vector值B;虚拟无效 commandAction(){std::cout <<命令类型 B!valueA:" <<值A<<" 字符串:" <<std::string(valueB.begin(), valueB.end()) <<std::endl;}};struct CommandTypeC : 公共命令库{//代表一种子程序"类型,其中多个命令可以组合在一起std::vector标签名称;std::vector>命令;虚拟无效 commandAction(){std::cout <<子程序:" <<std::string(labelName.start(), labelName.end())<<" 有 " <<命令.大小()<<<" 命令:" <<std::endl;BOOST_FOREACH(boost::shared_ptr<CommandBase>c, 命令){c->commandAction();}}};

                现在,我的尝试解析器代码:

                namespace ascii = boost::spirit::ascii;命名空间 qi = boost::spirit::qi;使用 qi::lit_;BOOST_FUSION_ADAPT_STRUCT(命令类型A,(整数,值A)(整数,值B))BOOST_FUSION_ADAPT_STRUCT(命令类型B,(双,值A)(std::vector, valueB))BOOST_FUSION_ADAPT_STRUCT(命令类型C,(std::vector, labelName)(std::vector<boost::shared_ptr<CommandBase> >, 命令))templatestruct CommandParser : qi::grammar>(), 船长>{上市:CommandParser() : CommandParser()::base_type(commands){CommandARule = qi::int_ >>qi::int_>>点亮(CMD_A");CommandBRule = qi::int_ >>+(qi::char_) >>点亮(CMD_B");CommandCRule = qi::char_(':') >>lexeme[+(qi::char_ - ';' - ascii::space) >>+ascii::space] >>命令 >>qi::char_(';');命令 = +(CommandARule | CommandBRule | CommandCRule);}受保护:qi::rule<Iterator, boost::shared_ptr<CommandTypeA>, Skipper>命令规则;qi::rule<Iterator, boost::shared_ptr<CommandTypeB>, Skipper>命令规则;qi::rule<Iterator, boost::shared_ptr<CommandTypeC>, Skipper>命令规则;qi::rule<Iterator, std::vector<boost::shared_ptr<CommandBase>>,船长>命令;};std::vector>命令列表;bool 成功 = qi::phrase_parse(StartIterator, EndIterator, CommandParser, ascii::space, commandList);BOOST_FOREACH(boost::shared_ptr<CommandBase>c, commandList){c->commandAction();}

                现在,这段代码肯定无法编译,但我希望它能够理解我正在尝试做的事情.

                主要的问题是 qi::rules 似乎想要发出实际的结构,而不是对它的引用.

                我的问题是:

                是否可以强制 qi::rule 发出多态兼容的引用,就像我正在尝试(如果是,如何),这是我尝试完成的最佳方法(即表示解析命令及其参数的可执行对象列表)?

                解决方案

                Spirit 对编译时多态性更友好

                typedef 变体命令;

                但是,假设您真的想做老式的多态性事情...

                不过,在解析过程中即时更新多态对象是一种可靠的方法

                • 让你的解析器因语义动作而变得臃肿
                • 在语法规则的回溯中造成大量内存泄漏
                • 使解析速度非常慢(因为您正在进行各种动态分配).
                • 最糟糕的是,这些都不会被优化掉,即使您实际上并未将属性引用传递到顶级 parse API.(通常,所有属性处理神奇"地在编译时蒸发,这对于输入格式验证非常有用)

                因此,您需要为基本命令类或派生类的对象创建一个持有者.使持有者满足 RuleOfZero 并得到实际按类型擦除值.

                (除了解决意外"复杂性和限制 wrt 内存回收之外,这种抽象的一个好处是您仍然可以选择静态处理存储,因此您可以在堆分配中节省 [大量] 时间.)

                我会看看你的样本,看看我是否可以快速演示.

                这是我对持有者"类的意思(向 CommandBase 添加一个虚拟析构函数!):

                struct CommandHolder{模板 CommandHolder(命令 cmd):存储(新的concrete_store<命令>{ std::move(cmd) }) { }运算符 CommandBase&() { return storage->get();}私人的:结构 base_store {虚拟 ~base_store() {};虚拟 CommandBase&获取()= 0;};模板 结构混凝土存储:base_store {混凝土商店(T v):包裹(标准::移动(v)){}虚拟 CommandBase&get() { 返回包装;}私人的:T包裹;};boost::shared_ptr贮存;};

                正如你所看到的,我在这里选择了 unique_ptr 来实现简单的所有权语义(variant 将避免一些分配开销作为以后的优化).我无法让 unique_ptr 与 Spirit 一起工作,因为 Spirit 根本没有移动感知能力.(Spirit X3 会).

                我们可以基于这个持有者轻松实现一个类型擦除 AnyCommand:

                struct AnyCommand : CommandBase{模板 AnyCommand(命令 cmd):持有人(标准::移动(cmd)){}虚拟 void commandAction() 覆盖 {static_cast<CommandBase&>(holder).commandAction();}私人的:CommandHolder 持有人;};

                所以现在您可以将任何命令分配"给 AnyCommand 并通过持有者多态地"使用它,即使持有者和 AnyCommand 具有完美的价值语义.

                这个示例语法可以:

                CommandParser() : CommandParser::base_type(commands){使用命名空间qi;CommandARule = int_>>内部_>>"CMD_A";CommandBRule = double_ >>lexeme[+(char_ - 空格)] >>"CMD_B";CommandCRule = ':' >>词位 [+graph - ';'] >>命令 >>';';命令 = CommandARule |命令规则 |命令规则;命令 = + 命令;}

                规则定义为:

                qi::rule命令规则;qi::rule命令规则;qi::rule命令规则;qi::rule命令;qi::rule命令;

                这是值语义和运行时多态性的令人愉快的组合:)

                测试主体

                int main(){std::string 常量输入 =":组
                "" 3.14 π CMD_B 
                "" -42 42 CMD_A 
                "" -inf -∞ CMD_B 
                "" +inf +∞ CMD_B 
                "";
                ""99 0 CMD_A";自动 f(begin(input)), l(end(input));std::vector命令列表;CommandParserp;bool 成功 = qi::phrase_parse(f, l, p, qi::space, commandList);如果(成功){BOOST_FOREACH(AnyCommand& c, commandList) {c.commandAction();}} 别的 {std::cout <<"解析失败
                ";}如果 (f!=l) {std::cout <<剩余未解析的输入"<<std::string(f,l) <<"'
                ";}}

                打印:

                子程序:组有4条命令:命令类型 B!值A:3.14 字符串:π命令类型 A!值A:-42 值B:42命令类型 B!值A:-inf 字符串:-∞命令类型 B!valueA:inf 字符串:+∞命令类型 A!值A:99 值B:0

                查看所有内容在 Coliru 上直播

                I would like my boost::spirit-based parser to be able to parse a file, convert the parsed rules into different types, and emit a vector containing all of the matches it found. All of the types that are emitted as attributes should be inherited from a base type, for example:

                #include <boost/spirit/include/qi.hpp>
                #include <boost/fusion/adapt_struct.hpp>
                #include <boost/shared_ptr.hpp>
                #include <boost/foreach.hpp>
                
                struct CommandBase
                {
                   virtual void commandAction()
                   {
                     std::cout << "This is a base command. You should never see this!" << std::endl;
                     //Boost::spirit seems to get mad if I make this purely virtual. Clearly I'm doing it wrong.
                   }
                };
                
                struct CommandTypeA : public CommandBase
                {
                   int valueA;
                   int valueB;
                   virtual void commandAction()
                   {
                      std::cout << "CommandType A! ValueA: " << valueA << " ValueB: " << valueB << std::endl;
                   }
                
                };
                
                struct CommandTypeB : public CommandBase
                {
                   double valueA;
                   std::vector<char> valueB;
                   virtual void commandAction()
                   {
                      std::cout << "CommandType B! valueA: " << valueA << " string: " << std::string(valueB.begin(), valueB.end()) << std::endl;
                   }
                };
                struct CommandTypeC : public CommandBase
                {
                  //Represents a sort of "subroutine" type where multiple commands can be grouped together
                  std::vector<char> labelName;
                  std::vector<boost::shared_ptr<CommandBase> > commands;
                  virtual void commandAction()
                  {
                      std::cout << "Subroutine: " << std::string(labelName.start(), labelName.end())
                                << " has " << commands.size() << " commands:" << std::endl;
                      BOOST_FOREACH(boost::shared_ptr<CommandBase> c, commands)
                      {
                           c->commandAction();
                      }          
                  }
                };
                

                Now, my attempted parser code:

                namespace ascii = boost::spirit::ascii;
                namespace qi = boost::spirit::qi;
                using qi::lit_;
                
                BOOST_FUSION_ADAPT_STRUCT(
                   CommandTypeA,
                   (int, valueA)
                   (int, valueB)
                )
                
                BOOST_FUSION_ADAPT_STRUCT(
                   CommandTypeB,
                   (double, valueA)
                   (std::vector<char>, valueB)
                )
                
                BOOST_FUSION_ADAPT_STRUCT(
                   CommandTypeC,
                   (std::vector<char>, labelName)
                   (std::vector<boost::shared_ptr<CommandBase> >, commands)
                )
                
                template<typename Iterator, typename Skipper = ascii::space_type>
                struct CommandParser : qi::grammar<Iterator, std::vector<boost::shared_ptr<CommandBase> >(), Skipper>
                {
                   public:
                   CommandParser() : CommandParser()::base_type(commands)
                   {
                      CommandARule = qi::int_ >> qi::int_ >> lit("CMD_A");
                      CommandBRule = qi::int_ >> +(qi::char_) >> lit("CMD_B");
                      CommandCRule = qi::char_(':') >> lexeme[+(qi::char_ - ';' - ascii::space) >> +ascii::space] >> commands >> qi::char_(';');
                
                      commands = +(CommandARule | CommandBRule | CommandCRule);
                   }
                   protected:
                   qi::rule<Iterator, boost::shared_ptr<CommandTypeA>, Skipper> CommandARule;
                   qi::rule<Iterator, boost::shared_ptr<CommandTypeB>, Skipper> CommandBRule;
                   qi::rule<Iterator, boost::shared_ptr<CommandTypeC>, Skipper> CommandCRule;
                   qi::rule<Iterator, std::vector<boost::shared_ptr<CommandBase> >, Skipper> commands;
                
                };
                
                
                std::vector<boost::shared_ptr<CommandBase> > commandList;
                bool success = qi::phrase_parse(StartIterator, EndIterator, CommandParser, ascii::space, commandList);
                
                BOOST_FOREACH(boost::shared_ptr<CommandBase> c, commandList)
                {
                    c->commandAction();
                }
                

                Now, this code definitely won't compile, but I hope it gets the gist across for what I'm attempting to do.

                The main hangup is that qi::rules seem to want to emit the actual struct, not a reference to it.

                My question is thus:

                Is it possible to force qi::rule to emit a polymorphism-compatible reference like I'm attempting (if so, how), and is this the best approach for what I'm attempting to accomplish (namely a list of executable objects representing the parsed commands and their parameters)?

                解决方案

                Spirit is a lot friendlier to compiletime-polymorphism

                typedef variant<Command1, Command2, Command3> Command;
                

                But, let's suppose you really want to do the old-fashioned polymorphism thing...

                Just newing-up the polymorphic objects on the fly during parsing, however, is a sure-fire way to

                • make your parser bloated with semantic actions
                • create lot of memory leaks on back-tracking in the grammar rules
                • make parsing awesomely slow (because you have all manner of dynamic allocation going on).
                • Worst of all, none of this would be optimized away, even when you're not actually passing an attribute reference into the top-level parse API. (Usually, all attribute handling "magically" vaporizes at compile-time, which is very useful for input format validation)

                So you'll want to create a holder for objects of your base-command class, or derived. Make the holder satisfy RuleOfZero and get the actual value out by type erasure.

                (Beyond solving the "accidental" complexity and limits w.r.t. memory reclamation, a bonus to this abstraction is that you you can still opt to handle the storage statically, so you save [a lot] of time in heap allocations.)

                I'll look at your sample to see whether I can demonstrate it quickly.

                Here is what I mean with a 'holder' class (add a virtual destructor to CommandBase!):

                struct CommandHolder
                {
                    template <typename Command> CommandHolder(Command cmd) 
                        : storage(new concrete_store<Command>{ std::move(cmd) }) { }
                
                    operator CommandBase&() { return storage->get(); }
                  private:
                    struct base_store {
                        virtual ~base_store() {}; 
                        virtual CommandBase& get() = 0;
                    };
                    template <typename T> struct concrete_store : base_store {
                        concrete_store(T v) : wrapped(std::move(v)) { }
                        virtual CommandBase& get() { return wrapped; }
                      private:
                        T wrapped; 
                    };
                
                    boost::shared_ptr<base_store> storage;
                };
                

                As you can see I opted for unique_ptr for simples ownership semantics here (a variant would avoid some allocation overhead as an optimization later). I couldn't make unique_ptr work with Spirit because Spirit is simply not move-aware. (Spirit X3 will be).

                We can trivially implement a type-erased AnyCommand based on this holder:

                struct AnyCommand : CommandBase
                {
                    template <typename Command> AnyCommand(Command cmd) 
                        : holder(std::move(cmd)) { }
                
                    virtual void commandAction() override { 
                        static_cast<CommandBase&>(holder).commandAction();
                    }
                  private:
                    CommandHolder holder;
                };
                

                So now you can "assign" any command to an AnyCommand and use it "polymorphically" through the holder, even though the holder and AnyCommand have perfect value-semantics.

                This sample grammar will do:

                CommandParser() : CommandParser::base_type(commands)
                {
                    using namespace qi;
                    CommandARule = int_    >> int_           >> "CMD_A";
                    CommandBRule = double_ >> lexeme[+(char_ - space)] >> "CMD_B";
                    CommandCRule = ':' >> lexeme [+graph - ';'] >> commands >> ';';
                
                    command  = CommandARule | CommandBRule | CommandCRule;
                    commands = +command;
                }
                

                With the rules defined as:

                qi::rule<Iterator, CommandTypeA(),            Skipper> CommandARule;
                qi::rule<Iterator, CommandTypeB(),            Skipper> CommandBRule;
                qi::rule<Iterator, CommandTypeC(),            Skipper> CommandCRule;
                qi::rule<Iterator, AnyCommand(),              Skipper> command;
                qi::rule<Iterator, std::vector<AnyCommand>(), Skipper> commands;
                

                This is quite a delightful mix of value-semantics and runtime-polymorphism :)

                The test main of

                int main()
                {
                    std::string const input =
                        ":group             
                "
                        "     3.14  π CMD_B 
                "
                        "     -42  42 CMD_A 
                "
                        "     -inf -∞ CMD_B 
                "
                        "     +inf +∞ CMD_B 
                "
                        ";                  
                "
                        "99 0 CMD_A";
                
                    auto f(begin(input)), l(end(input));
                
                    std::vector<AnyCommand> commandList;
                    CommandParser<std::string::const_iterator> p;
                    bool success = qi::phrase_parse(f, l, p, qi::space, commandList);
                
                    if (success) {
                        BOOST_FOREACH(AnyCommand& c, commandList) {
                            c.commandAction();
                        }
                    } else {
                        std::cout << "Parsing failed
                ";
                    }
                
                    if (f!=l) {
                        std::cout << "Remaining unparsed input '" << std::string(f,l) << "'
                ";
                    }
                }
                

                Prints:

                Subroutine: group has 4 commands:
                CommandType B! valueA: 3.14 string: π
                CommandType A! ValueA: -42 ValueB: 42
                CommandType B! valueA: -inf string: -∞
                CommandType B! valueA: inf string: +∞
                CommandType A! ValueA: 99 ValueB: 0
                

                See it all Live On Coliru

                这篇关于如何在 boost::spirit::qi 解析器中使用多态属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!

                上一篇:如何组合输出流,以便输出一次到达多个位置? 下一篇:如何使用 clang++/libc++ 编译/链接 Boost?

                相关文章

                  <tfoot id='WV9oI'></tfoot>
                    <bdo id='WV9oI'></bdo><ul id='WV9oI'></ul>
                  <i id='WV9oI'><tr id='WV9oI'><dt id='WV9oI'><q id='WV9oI'><span id='WV9oI'><b id='WV9oI'><form id='WV9oI'><ins id='WV9oI'></ins><ul id='WV9oI'></ul><sub id='WV9oI'></sub></form><legend id='WV9oI'></legend><bdo id='WV9oI'><pre id='WV9oI'><center id='WV9oI'></center></pre></bdo></b><th id='WV9oI'></th></span></q></dt></tr></i><div id='WV9oI'><tfoot id='WV9oI'></tfoot><dl id='WV9oI'><fieldset id='WV9oI'></fieldset></dl></div>
                  <legend id='WV9oI'><style id='WV9oI'><dir id='WV9oI'><q id='WV9oI'></q></dir></style></legend>
                  1. <small id='WV9oI'></small><noframes id='WV9oI'>