提升精神模板专业化失败

Boost Spirit template specialization failure

本文关键字:专业化 失败      更新时间:2023-10-16

下面是我尝试使用boost::spirit::qi编写的语法的非常紧凑的版本。
环境: VS2013, x86, Boost1.64
当 #including 头文件时,编译器抱怨该行

rBlock = "{" >> +(rInvocation) >> "}";

有一个很长的日志(我只复制了开头和结尾(:

多个部分专用化与模板参数列表匹配 ...

请参阅函数模板实例化的参考 '提升::精神::气::规则 &boost::spirit::qi::rule::operator =>(const Expr &(' 正在编译

我的错误在哪里?

头文件:

//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) >> "}";
}
};
}

我不确定问题在哪里触发,但这显然是属性转发规则中太多歧义的症状。

概念上讲,这可能是由具有相似/兼容布局的属性类型触发的。在语言理论中,您看到的是C++的主格类型系统与属性传播系统中结构类型的近似之间的不匹配。但足够的理论:)

我不认为attr_cast<>会在这里拯救你,因为它可能在引擎盖下使用相同的机制和启发式方法。

它引起了我的注意,使ArgumentList可选是......不是很有用(因为空列表已经准确地反映了参数的荒谬(。

所以我尝试简化规则:

rArgumentList = -(rArgument % ',');
rInvocation   = rIdentifier >> '(' >> rArgumentList >> ')';

声明的属性类型可以简单地ArgumentList而不是boost::optional::ArgumentList

事实证明,这可以消除传播到vector<Invocation>时的歧义,所以......你得救了。

如果这对你来说感觉是"偶然的",你应该!如果这没有"偶然"消除歧义,我会怎么做?我会创建一个语义操作来通过更简单的机制传播调用。fusion::push_back(_val, _1)或类似方法很有可能奏效。

另见 振奋精神:"语义行为是邪恶的"?

回顾和演示

在这里的清理审查中,我提出了一些修复/改进和一个转储解析的 AST 的测试运行。

  1. 将 AST 与分析器分开(您不希望在 AST 类型中使用qi。面对泛型模板库,您特别不希望using namespace指令(
  2. 不要在适应宏中使用auto。这不是一个功能。相反,由于表面上可以使用 C++11,因此请使用基于 C++11 (decltype( 的宏

    BOOST_FUSION_ADAPT_STRUCT(myAST::IdntifierEqArgument, ident,arg);
    BOOST_FUSION_ADAPT_STRUCT(myAST::Invocation, identifier,args);
    
  3. 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>;
    }
    

    将定义分开是件好事

  4. 关于解析器,

    • 我更喜欢qi::grammar约定。也
    • 您没有与船长声明任何规则。我从上下文中"猜测",在ExpressionIdentifier的规则之外,空格是微不足道的.
    • 表情吃每char_,所以也会吃')'甚至'3'。我仅在测试和调试后才注意到这一点:

      //#define BOOST_SPIRIT_DEBUG
      BOOST_SPIRIT_DEBUG_NODES((start)(rBlock)(rInvocation)(rIdentifier)(rArgumentList)(rArgument)(rExpression))
      

      我强烈建议使用这些设施

  5. 总而言之,解析器归结为

    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;
    };
    }
    
  6. 添加测试驱动程序

    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 successn";
    for (auto& invocation : block) {
    std::cout << invocation.identifier << "(";
    for (auto& arg : invocation.args) std::cout << arg << ",";
    std::cout << ")n";
    }
    }
    else
    std::cout << "Parse failedn";
    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 successn";
for (auto& invocation : block) {
std::cout << invocation.identifier << "(";
for (auto& arg : invocation.args) std::cout << arg << ",";
std::cout << ")n";
}
}
else
std::cout << "Parse failedn";
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: '
'