现在,我尝试了解析器代码:
@H_403_2@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(); }现在,这段代码肯定不会编译,但是我希望它能够解决我正在尝试做的事情.
主要的问题是qi :: rules似乎想要发出实际的结构,而不是它的引用.
我的问题是:
是否有可能强制qi :: rule发出与我正在尝试的多态性兼容的引用(如果是,如何),这是我尝试完成的最佳方法(即表示可执行对象的列表)解析的命令及其参数)?
解决方法
但是,让我们假设你真的想要做老式的多态性事情……
然而,在解析过程中即时刷新多态对象是一种可靠的方法
>使用语义动作使解析器变得臃肿
>在语法规则中的反向跟踪上创建大量内存泄漏
>使解析非常慢(因为你有各种动态分配方式).
>最糟糕的是,即使您实际上没有将属性引用传递给顶级解析API,也不会对其进行优化. (通常,所有属性处理“神奇地”在编译时蒸发,这对输入格式验证非常有用)
因此,您需要为基本命令类或派生的对象创建持有者.使支架满足RuleOfZero并通过类型擦除获得实际值.
(除了解决“偶然”复杂性并限制内存回收之外,这种抽象的一个好处是您仍然可以选择静态处理存储,因此可以在堆分配中节省大量时间.)
我会看看你的样本,看看我能否快速展示它.
这就是我对’holder’类的意思(向CommandBase添加一个虚拟析构函数!):
@H_403_2@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; };正如您所看到的,我在这里为simples所有权语义选择了unique_ptr(一种变体可以避免一些分配开销作为后来的优化).我无法使Unique_ptr与Spirit一起工作,因为Spirit根本就不知道移动. (精神X3将是).
我们可以基于此持有者轻松实现类型擦除的AnyCommand:
@H_403_2@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; };因此,现在您可以将任何命令“分配”给AnyCommand并通过持有者“多态”使用它,即使持有者和AnyCommand具有完美的价值语义.
这个示例语法将:
@H_403_2@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; }规则定义为:
@H_403_2@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;这是值语义和运行时多态的非常令人愉快的组合:)
测试主要是
@H_403_2@int main() { std::string const input = ":group \n" " 3.14 π CMD_B \n" " -42 42 CMD_A \n" " -inf -∞ CMD_B \n" " +inf +∞ CMD_B \n" "; \n" "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\n"; } if (f!=l) { std::cout << "Remaining unparsed input '" << std::string(f,l) << "'\n"; } }打印:
@H_403_2@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查看全部Live On Coliru