有很多让我困惑的地方。您说“我想要 XYZ”,没有任何理由说明为什么这样做更可取。此外,您对 不同 方法的声明相同,所以我不知道您更喜欢哪一种(以及为什么)。
示例代码定义
using stringvec = std::string;
这令人困惑,因为stringvec 顾名思义是vector<string>,而不是string?现在它看起来更像CVar 是“像一个字符串”,你的属性是vector<CVar>,即像一个字符串向量,而不是std::string。
总而言之,我可以给你以下提示:
-
一般来说,避免语义动作。它们对编译器很重,抽象泄漏,选择退出属性兼容性¹,在回溯下产生原子性问题(请参阅Boost Spirit: "Semantic actions are evil"?)
-
其次,如果您使用语义动作,请意识到更多解析器表达式的原始合成属性是融合序列/容器。
- 特别是要获得
std::string,请使用qi::as_string[]。如果你不使用语义动作,这种属性兼容性/转换确实是自动的¹。
- 类似地,要将单个项目解析为显式容器,请使用
repeat(1)[p]
-
除了依赖语义操作的所有缺点之外,构造函数的工作方式与您在 phx::construct<> 中展示的一样
侧面观察:您是否注意到我通过将std::string 替换为char 来减少this previous answer 中的解析器/AST 摩擦?
应用答案:
问。实际上,我本来想省略围绕类的向量,但我遇到了问题http://coliru.stacked-crooked.com/a/3719cee9c7594254 我以为我在某个时候设法将类匹配为树的根。错误消息看起来,就像适应结构真的想在某个时候调用 push_back。不知何故,这是有道理的,以防万一没有要存储的类。
使用as_string进行简化:Live On Coliru
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
#include <iostream>
namespace qi = boost::spirit::qi;
using Iterator = std::string::const_iterator;
using stringval = std::string;
struct CVar { stringval sVariable; };
BOOST_FUSION_ADAPT_STRUCT(CVar, sVariable)
struct TestGrammar : qi::grammar<Iterator, CVar()> {
TestGrammar() : TestGrammar::base_type(start) {
start = qi::as_string[qi::char_("a-z")];
}
private:
qi::rule<Iterator, CVar() > start;
};
void do_test(std::string const& input) {
CVar output;
static const TestGrammar p;
auto f = input.begin(), l = input.end();
qi::parse(f, l, p, output);
std::cout << std::quoted(input) << " -> " << std::quoted(output.sVariable) << "\n";
}
使用repeat(1):Live On Coliru
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
#include <iostream>
namespace qi = boost::spirit::qi;
using Iterator = std::string::const_iterator;
using stringval = std::string;
struct CVar { stringval sVariable; };
BOOST_FUSION_ADAPT_STRUCT(CVar, sVariable)
struct TestGrammar : qi::grammar<Iterator, CVar()> {
TestGrammar() : TestGrammar::base_type(start) {
cvar = qi::repeat(1)[qi::char_("a-z")];
start = cvar;
}
private:
qi::rule<Iterator, CVar()> start;
qi::rule<Iterator, std::string()> cvar;
};
void do_test(std::string const& input) {
CVar output;
static const TestGrammar p;
auto f = input.begin(), l = input.end();
qi::parse(f, l, p, output);
std::cout << std::quoted(input) << " -> " << std::quoted(output.sVariable) << "\n";
}
不使用 ADAPT_STRUCT:minimal change 与:simplified
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
#include <iostream>
namespace qi = boost::spirit::qi;
using Iterator = std::string::const_iterator;
using stringval = std::string;
struct CVar {
CVar(std::string v = {}) : sVariable(std::move(v)) {}
stringval sVariable;
};
struct TestGrammar : qi::grammar<Iterator, CVar()> {
TestGrammar() : TestGrammar::base_type(start) {
start = qi::as_string[qi::lower];
}
private:
qi::rule<Iterator, CVar()> start;
};
使用语义动作(不推荐):4 种实时模式
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <iomanip>
#include <iostream>
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
using Iterator = std::string::const_iterator;
using stringval = std::string;
struct CVar {
CVar(std::string v = {}) : sVariable(std::move(v)) {}
stringval sVariable;
};
enum mode {
AS_STRING_CONSTRUCT = 1,
DIRECT_ASSIGN = 2,
USING_ACTOR = 3,
TRANSPARENT_CXX14_LAMBDA = 4,
};
struct TestGrammar : qi::grammar<Iterator, CVar()> {
TestGrammar(mode m) : TestGrammar::base_type(start) {
switch (m) {
case AS_STRING_CONSTRUCT: {
using namespace qi::labels;
start = qi::as_string[qi::lower][_val = px::construct<CVar>(_1)];
break;
}
case DIRECT_ASSIGN: {
// or directly
using namespace qi::labels;
start = qi::lower[_val = px::construct<std::string>(1ull, _1)];
break;
}
case USING_ACTOR: {
// or... indeed
using namespace qi::labels;
px::function as_cvar = [](char var) -> CVar { return {{var}}; };
start = qi::lower[_val = as_cvar(_1)];
break;
}
case TRANSPARENT_CXX14_LAMBDA: {
// or even more bespoke: (this doesn't require qi::labels or phoenix.hpp)
auto propagate = [](auto& attr, auto& ctx) {
at_c<0>(ctx.attributes) = {{attr}};
};
start = qi::lower[propagate];
break;
}
}
}
private:
qi::rule<Iterator, CVar()> start;
};
void do_test(std::string const& input, mode m) {
CVar output;
const TestGrammar p(m);
auto f = input.begin(), l = input.end();
qi::parse(f, l, p, output);
std::cout << std::quoted(input) << " -> " << std::quoted(output.sVariable) << "\n";
}
int main() {
for (mode m : {AS_STRING_CONSTRUCT, DIRECT_ASSIGN, USING_ACTOR,
TRANSPARENT_CXX14_LAMBDA}) {
std::cout << " ==== mode #" << static_cast<int>(m) << " === \n";
for (auto s : {"a", "d", "ac"})
do_test(s, m);
}
}
只是为了演示后两种方法如何both do without the constructor 或even without any phoenix support。
和以前一样,到那时我还是建议使用 C++14 和 Boost Spirit X3:http://coliru.stacked-crooked.com/a/dbd61823354ea8b6 甚至 20 LoC:http://coliru.stacked-crooked.com/a/b26b3db6115c14d4
¹有一个非主要的“hack”可以通过定义BOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT来帮助你