我如何使用与升压::精神::气解析器多态的属性? [英] How can I use polymorphic attributes with boost::spirit::qi parsers?
问题描述
我想我的boost ::精神为基础,解析器能够解析文件,转换解析规则为不同的类型,并放出包含所有它找到一个匹配的载体。所有发射作为属性的类型应该从基类继承,例如:
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();
}
}
};
现在,我的尝试的解析器code:
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();
}
现在,这个code绝对不会编译,但我希望它得到跨越什么,我试图做的要点。
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.
我的问题是这样的:
是否有可能迫使齐::规则发出多态性兼容参考像我尝试(如果是,如何),而这又是为了什么,我试图完成的最佳方法(即可执行的对象的列表重新presenting所解析的命令及其参数)吗
推荐答案
精神是很多友好到编译时多态
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...
刚刚newing向上分析过程中动态的多态对象,但是,是一个安全可靠的方式,以
Just newing-up the polymorphic objects on the fly during parsing, however, is a sure-fire way to
- 请解析器臃肿语义动作
- 在语法规则创造很多回溯跟踪内存泄漏
- 请解析赫然慢(因为你的动态分配会在所有的方式)。
- 最糟糕的是,没有这将被优化掉,即使你没有真正传递一个属性引用到顶级
解析
API。 (通常情况下,所有的属性处理神奇蒸发在编译时,这是非常有用的输入格式验证)
- 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)
所以,你要创建一个持有人的基本命令类,或派生的对象。让持有人满意的 RuleOfZero 通过类型擦除得到实际值了。
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.
(除了解决偶然的复杂性和局限性WRT内存回收,奖金以这种抽象是你,你依然可以选择静态地处理存储,让您保存在堆中分配的时间[很多。)
(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.
下面是我用'持有人'类的意思(虚析构函数添加到 CommandBase
!):
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;
};
<击>正如你可以看到我选择了的unique_ptr
这里simples所有权语义(A 变量
将避免一些分配开销作为一个优化版本)击>。我不能让的unique_ptr
与工作精神,因为精神是根本就没有移动感知。 (灵X3会)。
As you can see I opted for . I couldn't make unique_ptr
for simples ownership semantics here (a variant
would avoid some allocation overhead as an optimization later)unique_ptr
work with Spirit because Spirit is simply not move-aware. (Spirit X3 will be).
我们可以平凡实施的类型擦除的 AnyCommand
在此基础上架:
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;
};
所以,现在你可以在分配的任何命令到AnyCommand,并用它多态,通过持有人,即使持有人,并AnyCommand有完善的价值的语义。
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.
这样的语法就可以了:
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;
}
通过定义的规则:
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 :)
的测试主要
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";
}
}
打印:
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
看到这一切的 住在Coliru
See it all Live On Coliru
这篇关于我如何使用与升压::精神::气解析器多态的属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!