gpt4 book ai didi

c++ - 我如何将多态属性与 boost::spirit::qi 解析器一起使用?

转载 作者:可可西里 更新时间:2023-11-01 18:35:49 25 4
gpt4 key购买 nike

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

#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();
}
}
};

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

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 发出一个多态兼容的引用,就像我正在尝试的那样(如果是,如何),这是我试图完成的最好的方法吗(即表示已解析命令及其参数的可执行对象列表)?

最佳答案

Spirit 对编译时多态性要友好得多

typedef variant<Command1, Command2, Command3> Command;

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

然而,在解析过程中动态更新多态对象是一种可靠的方法

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

因此,您需要为您的基本命令类或派生类的对象创建一个容器。让holder满足 RuleOfZero 并通过类型删除得到实际值。

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

我会查看您的示例,看能否快速演示。

这就是我所说的“holder”类(向 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 来简化所有权语义( variant 将避免一些分配开销作为稍后的优化) .我无法让 unique_ptr 与 Spirit 一起工作,因为 Spirit 根本无法感知移动。 (灵X3会)。

我们可以简单地实现一个type-erased AnyCommand 基于这个 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,并通过 holder“多态”使用它,即使 holder 和 AnyCommand 具有完美的值语义。

此示例语法将执行:

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;

这是值语义和运行时多态性的完美结合:)

测试主要的

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

查看全部 Live On Coliru

关于c++ - 我如何将多态属性与 boost::spirit::qi 解析器一起使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23299722/

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