Boost Spirit模板专精失败
Boost Spirit template specialization failure
下面是我尝试使用 boost::spirit::qi.
编写的非常紧凑的语法版本
环境:VS2013,x86,Boost1.64
当#include 头文件时,编译器抱怨行
rBlock = "{" >> +(rInvocation) >> "}";
日志很长(我只复制了开头和结尾):
more than one partial specialization matches the template argument list
...
...
see reference to function template instantiation
'boost::spirit::qi::rule
&boost::spirit::qi::rule::operator =>(const Expr &)' being compiled
我的错误在哪里?
头文件:
//mygrammar.h
#pragma once
#include <boost/spirit/include/qi.hpp>
namespace myNS
{
typedef std::string Identifier;
typedef ::boost::spirit::qi::rule <const char*, Identifier()> myIdentifierRule;
typedef ::boost::variant<char, int> Expression;
typedef ::boost::spirit::qi::rule <const char*, Expression()> myExpressionRule;
struct IdntifierEqArgument
{
Identifier ident;
Expression arg;
};
typedef ::boost::variant < IdntifierEqArgument, Expression > Argument;
typedef ::boost::spirit::qi::rule <const char*, Argument()> myArgumentRule;
typedef ::std::vector<Argument> ArgumentList;
typedef ::boost::spirit::qi::rule <const char*, myNS::ArgumentList()> myArgumentListRule;
struct Invocation
{
Identifier identifier;
::boost::optional<ArgumentList> args;
};
typedef ::boost::spirit::qi::rule <const char*, Invocation()> myInvocationRule;
typedef ::std::vector<Invocation> Block;
typedef ::boost::spirit::qi::rule <const char*, myNS::Block()> myBlockRule;
}
BOOST_FUSION_ADAPT_STRUCT(
myNS::IdntifierEqArgument,
(auto, ident)
(auto, arg)
);
BOOST_FUSION_ADAPT_STRUCT(
myNS::Invocation,
(auto, identifier)
(auto, args)
);
namespace myNS
{
struct myRules
{
myIdentifierRule rIdentifier;
myExpressionRule rExpression;
myArgumentRule rArgument;
myArgumentListRule rArgumentList;
myInvocationRule rInvocation;
myBlockRule rBlock;
myRules()
{
using namespace ::boost::spirit;
using namespace ::boost::spirit::qi;
rIdentifier = as_string[((qi::alpha | '_') >> *(qi::alnum | '_'))];
rExpression = char_ | int_;
rArgument = (rIdentifier >> "=" >> rExpression) | rExpression;
rArgumentList = rArgument >> *("," >> rArgument);
rInvocation = rIdentifier >> "(" >> -rArgumentList >> ")";
rBlock = "{" >> +(rInvocation) >> "}";
}
};
}
我不太确定问题是在哪里触发的,但这显然是属性转发规则中太多歧义的症状。
Conceptually this could be triggered by your attribute types having similar/compatible layouts. In language theory, you're looking at a mismatch between C++'s nominative type system versus the approximation of structural typing in the attribute propagation system. But enough theorism :)
我认为 attr_cast<>
不会在这里拯救你,因为它可能在幕后使用相同的机制和启发式方法。
让我注意到 ArgumentList
可选是......不是很有用(因为空列表已经准确地反映了参数的缺失)。
所以我尝试简化规则:
rArgumentList = -(rArgument % ',');
rInvocation = rIdentifier >> '(' >> rArgumentList >> ')';
并且声明的属性类型可以简单地ArgumentList
而不是boost::optional::ArgumentList
。
事实证明,这消除了传播到 vector<Invocation>
时的歧义,所以......你得救了。
If this feels "accidental" to you, you should! What would I do if this hadn't removed the ambiguity "by chance"? I'd have created a semantic action to propagate the Invocation by simpler mechanics. There's a good chance that fusion::push_back(_val, _1)
or similar would have worked.
See also Boost Spirit: "Semantic actions are evil"?
审查和演示
在清理过的评论中,我展示了一些 fixes/improvements 和一个测试 运行 来转储已解析的 AST。
- 将 AST 与解析器分开(您不想在 AST 类型中使用
qi
。您特别不想在通用模板库中使用 using namespace
指令)
不要在 adapt 宏中使用 auto
。那不是一个功能。相反,由于您表面上可以使用 C++11,因此请使用基于 C++11 (decltype) 的宏
BOOST_FUSION_ADAPT_STRUCT(myAST::IdntifierEqArgument, ident,arg);
BOOST_FUSION_ADAPT_STRUCT(myAST::Invocation, identifier,args);
AST 领先(另外,为了清晰起见,更喜欢 c++11):
namespace myAST {
using Identifier = std::string;
using Expression = boost::variant<char, int>;
struct IdntifierEqArgument {
Identifier ident;
Expression arg;
};
using Argument = boost::variant<IdntifierEqArgument, Expression>;
using ArgumentList = std::vector<Argument>;
struct Invocation {
Identifier identifier;
ArgumentList args;
};
using Block = std::vector<Invocation>;
}
分开定义真好
关于解析器,
- 我更喜欢
qi::grammar
约定。另外,
- 您没有与船长一起声明任何规则。我 "guessed" 从上下文来看,空格在
Expression
和 Identifier
. 的规则之外是微不足道的
表情每char_
吃一次,所以也会吃')'
甚至'3'
。我只在测试时和调试后才注意到这一点:
//#define BOOST_SPIRIT_DEBUG
BOOST_SPIRIT_DEBUG_NODES((start)(rBlock)(rInvocation)(rIdentifier)(rArgumentList)(rArgument)(rExpression))
我强烈推荐使用这些设施
总而言之,解析器归结为
namespace myNS {
namespace qi = boost::spirit::qi;
template <typename Iterator = char const*>
struct myRules : qi::grammar<Iterator, myAST::Block()> {
myRules() : myRules::base_type(start) {
rIdentifier = qi::raw [(qi::alpha | '_') >> *(qi::alnum | '_')];
rExpression = qi::alpha | qi::int_;
rArgument = (rIdentifier >> '=' >> rExpression) | rExpression;
rArgumentList = -(rArgument % ',');
rInvocation = rIdentifier >> '(' >> rArgumentList >> ')';
rBlock = '{' >> +rInvocation >> '}';
start = qi::skip(qi::space) [ rBlock ];
BOOST_SPIRIT_DEBUG_NODES((start)(rBlock)(rInvocation)(rIdentifier)(rArgumentList)(rArgument)(rExpression))
}
private:
qi::rule<Iterator, myAST::Block()> start;
using Skipper = qi::space_type;
qi::rule<Iterator, myAST::Argument(), Skipper> rArgument;
qi::rule<Iterator, myAST::ArgumentList(), Skipper> rArgumentList;
qi::rule<Iterator, myAST::Invocation(), Skipper> rInvocation;
qi::rule<Iterator, myAST::Block(), Skipper> rBlock;
// implicit lexemes
qi::rule<Iterator, myAST::Identifier()> rIdentifier;
qi::rule<Iterator, myAST::Expression()> rExpression;
};
}
添加测试驱动程序
int main() {
std::string const input = R"(
{
foo()
bar(a, b, 42)
qux(someThing_awful01 = 9)
}
)";
auto f = input.data(), l = f + input.size();
myAST::Block block;
bool ok = parse(f, l, myNS::myRules<>{}, block);
if (ok) {
std::cout << "Parse success\n";
for (auto& invocation : block) {
std::cout << invocation.identifier << "(";
for (auto& arg : invocation.args) std::cout << arg << ",";
std::cout << ")\n";
}
}
else
std::cout << "Parse failed\n";
if (f!=l)
std::cout << "Remaining unparsed input: '" << std::string(f,l) << "'\n";
}
完成演示
//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
namespace myAST {
using Identifier = std::string;
using Expression = boost::variant<char, int>;
struct IdntifierEqArgument {
Identifier ident;
Expression arg;
};
using Argument = boost::variant<IdntifierEqArgument, Expression>;
using ArgumentList = std::vector<Argument>;
struct Invocation {
Identifier identifier;
ArgumentList args;
};
using Block = std::vector<Invocation>;
// for debug printing
static inline std::ostream& operator<<(std::ostream& os, myAST::IdntifierEqArgument const& named) {
return os << named.ident << "=" << named.arg;
}
}
BOOST_FUSION_ADAPT_STRUCT(myAST::IdntifierEqArgument, ident,arg);
BOOST_FUSION_ADAPT_STRUCT(myAST::Invocation, identifier,args);
namespace myNS {
namespace qi = boost::spirit::qi;
template <typename Iterator = char const*>
struct myRules : qi::grammar<Iterator, myAST::Block()> {
myRules() : myRules::base_type(start) {
rIdentifier = qi::raw [(qi::alpha | '_') >> *(qi::alnum | '_')];
rExpression = qi::alpha | qi::int_;
rArgument = (rIdentifier >> '=' >> rExpression) | rExpression;
rArgumentList = -(rArgument % ',');
rInvocation = rIdentifier >> '(' >> rArgumentList >> ')';
rBlock = '{' >> +rInvocation >> '}';
start = qi::skip(qi::space) [ rBlock ];
BOOST_SPIRIT_DEBUG_NODES((start)(rBlock)(rInvocation)(rIdentifier)(rArgumentList)(rArgument)(rExpression))
}
private:
qi::rule<Iterator, myAST::Block()> start;
using Skipper = qi::space_type;
qi::rule<Iterator, myAST::Argument(), Skipper> rArgument;
qi::rule<Iterator, myAST::ArgumentList(), Skipper> rArgumentList;
qi::rule<Iterator, myAST::Invocation(), Skipper> rInvocation;
qi::rule<Iterator, myAST::Block(), Skipper> rBlock;
// implicit lexemes
qi::rule<Iterator, myAST::Identifier()> rIdentifier;
qi::rule<Iterator, myAST::Expression()> rExpression;
};
}
int main() {
std::string const input = R"(
{
foo()
bar(a, b, 42)
qux(someThing_awful01 = 9)
}
)";
auto f = input.data(), l = f + input.size();
myAST::Block block;
bool ok = parse(f, l, myNS::myRules<>{}, block);
if (ok) {
std::cout << "Parse success\n";
for (auto& invocation : block) {
std::cout << invocation.identifier << "(";
for (auto& arg : invocation.args) std::cout << arg << ",";
std::cout << ")\n";
}
}
else
std::cout << "Parse failed\n";
if (f!=l)
std::cout << "Remaining unparsed input: '" << std::string(f,l) << "'\n";
}
打印输出
Parse success
foo()
bar(a,b,42,)
qux(someThing_awful01=9,)
Remaining unparsed input: '
'
下面是我尝试使用 boost::spirit::qi.
编写的非常紧凑的语法版本
环境:VS2013,x86,Boost1.64
当#include 头文件时,编译器抱怨行
rBlock = "{" >> +(rInvocation) >> "}";
日志很长(我只复制了开头和结尾):
more than one partial specialization matches the template argument list ...
...
see reference to function template instantiation 'boost::spirit::qi::rule &boost::spirit::qi::rule::operator =>(const Expr &)' being compiled
我的错误在哪里?
头文件:
//mygrammar.h
#pragma once
#include <boost/spirit/include/qi.hpp>
namespace myNS
{
typedef std::string Identifier;
typedef ::boost::spirit::qi::rule <const char*, Identifier()> myIdentifierRule;
typedef ::boost::variant<char, int> Expression;
typedef ::boost::spirit::qi::rule <const char*, Expression()> myExpressionRule;
struct IdntifierEqArgument
{
Identifier ident;
Expression arg;
};
typedef ::boost::variant < IdntifierEqArgument, Expression > Argument;
typedef ::boost::spirit::qi::rule <const char*, Argument()> myArgumentRule;
typedef ::std::vector<Argument> ArgumentList;
typedef ::boost::spirit::qi::rule <const char*, myNS::ArgumentList()> myArgumentListRule;
struct Invocation
{
Identifier identifier;
::boost::optional<ArgumentList> args;
};
typedef ::boost::spirit::qi::rule <const char*, Invocation()> myInvocationRule;
typedef ::std::vector<Invocation> Block;
typedef ::boost::spirit::qi::rule <const char*, myNS::Block()> myBlockRule;
}
BOOST_FUSION_ADAPT_STRUCT(
myNS::IdntifierEqArgument,
(auto, ident)
(auto, arg)
);
BOOST_FUSION_ADAPT_STRUCT(
myNS::Invocation,
(auto, identifier)
(auto, args)
);
namespace myNS
{
struct myRules
{
myIdentifierRule rIdentifier;
myExpressionRule rExpression;
myArgumentRule rArgument;
myArgumentListRule rArgumentList;
myInvocationRule rInvocation;
myBlockRule rBlock;
myRules()
{
using namespace ::boost::spirit;
using namespace ::boost::spirit::qi;
rIdentifier = as_string[((qi::alpha | '_') >> *(qi::alnum | '_'))];
rExpression = char_ | int_;
rArgument = (rIdentifier >> "=" >> rExpression) | rExpression;
rArgumentList = rArgument >> *("," >> rArgument);
rInvocation = rIdentifier >> "(" >> -rArgumentList >> ")";
rBlock = "{" >> +(rInvocation) >> "}";
}
};
}
我不太确定问题是在哪里触发的,但这显然是属性转发规则中太多歧义的症状。
Conceptually this could be triggered by your attribute types having similar/compatible layouts. In language theory, you're looking at a mismatch between C++'s nominative type system versus the approximation of structural typing in the attribute propagation system. But enough theorism :)
我认为 attr_cast<>
不会在这里拯救你,因为它可能在幕后使用相同的机制和启发式方法。
让我注意到 ArgumentList
可选是......不是很有用(因为空列表已经准确地反映了参数的缺失)。
所以我尝试简化规则:
rArgumentList = -(rArgument % ',');
rInvocation = rIdentifier >> '(' >> rArgumentList >> ')';
并且声明的属性类型可以简单地ArgumentList
而不是boost::optional::ArgumentList
。
事实证明,这消除了传播到 vector<Invocation>
时的歧义,所以......你得救了。
If this feels "accidental" to you, you should! What would I do if this hadn't removed the ambiguity "by chance"? I'd have created a semantic action to propagate the Invocation by simpler mechanics. There's a good chance that
fusion::push_back(_val, _1)
or similar would have worked.See also Boost Spirit: "Semantic actions are evil"?
审查和演示
在清理过的评论中,我展示了一些 fixes/improvements 和一个测试 运行 来转储已解析的 AST。
- 将 AST 与解析器分开(您不想在 AST 类型中使用
qi
。您特别不想在通用模板库中使用using namespace
指令) 不要在 adapt 宏中使用
auto
。那不是一个功能。相反,由于您表面上可以使用 C++11,因此请使用基于 C++11 (decltype) 的宏BOOST_FUSION_ADAPT_STRUCT(myAST::IdntifierEqArgument, ident,arg); BOOST_FUSION_ADAPT_STRUCT(myAST::Invocation, identifier,args);
AST 领先(另外,为了清晰起见,更喜欢 c++11):
namespace myAST { using Identifier = std::string; using Expression = boost::variant<char, int>; struct IdntifierEqArgument { Identifier ident; Expression arg; }; using Argument = boost::variant<IdntifierEqArgument, Expression>; using ArgumentList = std::vector<Argument>; struct Invocation { Identifier identifier; ArgumentList args; }; using Block = std::vector<Invocation>; }
分开定义真好
关于解析器,
- 我更喜欢
qi::grammar
约定。另外, - 您没有与船长一起声明任何规则。我 "guessed" 从上下文来看,空格在
Expression
和Identifier
. 的规则之外是微不足道的
表情每
char_
吃一次,所以也会吃')'
甚至'3'
。我只在测试时和调试后才注意到这一点://#define BOOST_SPIRIT_DEBUG BOOST_SPIRIT_DEBUG_NODES((start)(rBlock)(rInvocation)(rIdentifier)(rArgumentList)(rArgument)(rExpression))
我强烈推荐使用这些设施
- 我更喜欢
总而言之,解析器归结为
namespace myNS { namespace qi = boost::spirit::qi; template <typename Iterator = char const*> struct myRules : qi::grammar<Iterator, myAST::Block()> { myRules() : myRules::base_type(start) { rIdentifier = qi::raw [(qi::alpha | '_') >> *(qi::alnum | '_')]; rExpression = qi::alpha | qi::int_; rArgument = (rIdentifier >> '=' >> rExpression) | rExpression; rArgumentList = -(rArgument % ','); rInvocation = rIdentifier >> '(' >> rArgumentList >> ')'; rBlock = '{' >> +rInvocation >> '}'; start = qi::skip(qi::space) [ rBlock ]; BOOST_SPIRIT_DEBUG_NODES((start)(rBlock)(rInvocation)(rIdentifier)(rArgumentList)(rArgument)(rExpression)) } private: qi::rule<Iterator, myAST::Block()> start; using Skipper = qi::space_type; qi::rule<Iterator, myAST::Argument(), Skipper> rArgument; qi::rule<Iterator, myAST::ArgumentList(), Skipper> rArgumentList; qi::rule<Iterator, myAST::Invocation(), Skipper> rInvocation; qi::rule<Iterator, myAST::Block(), Skipper> rBlock; // implicit lexemes qi::rule<Iterator, myAST::Identifier()> rIdentifier; qi::rule<Iterator, myAST::Expression()> rExpression; }; }
添加测试驱动程序
int main() { std::string const input = R"( { foo() bar(a, b, 42) qux(someThing_awful01 = 9) } )"; auto f = input.data(), l = f + input.size(); myAST::Block block; bool ok = parse(f, l, myNS::myRules<>{}, block); if (ok) { std::cout << "Parse success\n"; for (auto& invocation : block) { std::cout << invocation.identifier << "("; for (auto& arg : invocation.args) std::cout << arg << ","; std::cout << ")\n"; } } else std::cout << "Parse failed\n"; if (f!=l) std::cout << "Remaining unparsed input: '" << std::string(f,l) << "'\n"; }
完成演示
//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
namespace myAST {
using Identifier = std::string;
using Expression = boost::variant<char, int>;
struct IdntifierEqArgument {
Identifier ident;
Expression arg;
};
using Argument = boost::variant<IdntifierEqArgument, Expression>;
using ArgumentList = std::vector<Argument>;
struct Invocation {
Identifier identifier;
ArgumentList args;
};
using Block = std::vector<Invocation>;
// for debug printing
static inline std::ostream& operator<<(std::ostream& os, myAST::IdntifierEqArgument const& named) {
return os << named.ident << "=" << named.arg;
}
}
BOOST_FUSION_ADAPT_STRUCT(myAST::IdntifierEqArgument, ident,arg);
BOOST_FUSION_ADAPT_STRUCT(myAST::Invocation, identifier,args);
namespace myNS {
namespace qi = boost::spirit::qi;
template <typename Iterator = char const*>
struct myRules : qi::grammar<Iterator, myAST::Block()> {
myRules() : myRules::base_type(start) {
rIdentifier = qi::raw [(qi::alpha | '_') >> *(qi::alnum | '_')];
rExpression = qi::alpha | qi::int_;
rArgument = (rIdentifier >> '=' >> rExpression) | rExpression;
rArgumentList = -(rArgument % ',');
rInvocation = rIdentifier >> '(' >> rArgumentList >> ')';
rBlock = '{' >> +rInvocation >> '}';
start = qi::skip(qi::space) [ rBlock ];
BOOST_SPIRIT_DEBUG_NODES((start)(rBlock)(rInvocation)(rIdentifier)(rArgumentList)(rArgument)(rExpression))
}
private:
qi::rule<Iterator, myAST::Block()> start;
using Skipper = qi::space_type;
qi::rule<Iterator, myAST::Argument(), Skipper> rArgument;
qi::rule<Iterator, myAST::ArgumentList(), Skipper> rArgumentList;
qi::rule<Iterator, myAST::Invocation(), Skipper> rInvocation;
qi::rule<Iterator, myAST::Block(), Skipper> rBlock;
// implicit lexemes
qi::rule<Iterator, myAST::Identifier()> rIdentifier;
qi::rule<Iterator, myAST::Expression()> rExpression;
};
}
int main() {
std::string const input = R"(
{
foo()
bar(a, b, 42)
qux(someThing_awful01 = 9)
}
)";
auto f = input.data(), l = f + input.size();
myAST::Block block;
bool ok = parse(f, l, myNS::myRules<>{}, block);
if (ok) {
std::cout << "Parse success\n";
for (auto& invocation : block) {
std::cout << invocation.identifier << "(";
for (auto& arg : invocation.args) std::cout << arg << ",";
std::cout << ")\n";
}
}
else
std::cout << "Parse failed\n";
if (f!=l)
std::cout << "Remaining unparsed input: '" << std::string(f,l) << "'\n";
}
打印输出
Parse success
foo()
bar(a,b,42,)
qux(someThing_awful01=9,)
Remaining unparsed input: '
'