【问题标题】:Detecting the parameter types in a Spirit semantic action检测 Spirit 语义动作中的参数类型
【发布时间】:2012-03-13 08:02:52
【问题描述】:

一般情况:我无法弄清楚为什么我的 Spirit 语法/语义操作无法编译。

有时,编译器会抱怨赋值或类型不兼容,我不知道出了什么问题。问题主要出现在两个方面:

  • 预测规则/表达式的综合属性类型
    • 因此,预测哪些类型的属性可以合法地定义为规则的公开属性(依赖于构建转换、融合适配器或 Spirit 自定义点)
  • 匹配我的语义操作的参数类型,以便
    • 编译器将能够编译函数调用
    • 调用不会在进程中调用不必要的隐式转换

编译器错误并不完全易于处理,要么是文档错误,要么是我误解了它。

有没有办法确切地找出 Spirit 传递给我的语义动作的内容?

示例代码:

struct mybase             { int a,b; };
struct myderived : mybase { int c,d; };

BOOST_FUSION_ADAPT_STRUCT(mybase,    (int,a)(int,b));
BOOST_FUSION_ADAPT_STRUCT(myderived, (int,a)(int,b)(int,c)(int,d));

auto base_expr = int_ >> int_; // avoids assigning to struct attribute

rule<decltype(f), mybase()   , space_type> base_       = int_ >> int_;
rule<decltype(f), myderived(), space_type> derived_    = base_ >> int_ >> int_;

myderived data;
bool ok = phrase_parse(f,l,derived_,space,data);

此代码无法编译,存在大量无法理解的错误。

大致改编自 spirit-general 列表中的帖子

【问题讨论】:

  • +1 正是我作为初学者每天都在努力解决的问题......

标签: c++ compiler-errors boost-spirit


【解决方案1】:

我可以解决这个特殊情况的问题(实际上是we discussed options on the list),但实际上,这种 Boost Spirit 会更频繁地出现“神秘”错误,这会很好 处理一般问题。

您的第一个资源应该是优秀精神文档,它 详细说明给定解析器的合成属性是什么 原语、运算符或指令。见the Reference sectionSpirit Qi Docs

在某些情况下,我已将重点从“试图撬开 信息从编译器错误列表'到'主动查询 Spirit 它通过的类型'。我使用的技术是多态可调用类型 (参见 Spirit/Fusion 文档)。

这是一个使用 GCC 特定的 API 来漂亮 [原文] 打印它检测到的类型:

函子what_is_the_attr

#include <cxxabi.h>
#include <stdlib.h>
#include <string>
#include <iostream>

template <typename T> std::string nameofType(const T& v) {
    int     status;
    char   *realname = abi::__cxa_demangle(typeid(v).name(), 0, 0, &status);
    std::string name(realname? realname : "????");
    free(realname);
    return name;
}

struct what_is_the_attr {
    template <typename> struct result { typedef bool type; };

    template <typename T> bool operator()(T& attr) const {
        std::cerr << "what_is_the_attr: " << nameofType(attr) << std::endl;
        return true;
    }
};

示例使用:检测合成属性类型

您可以使用它来准确检测一个合成属性类型是什么类型 解析器表达式实际上最终是:

template <typename Exp>
    void detect_attr_type(const Exp& exp)
{
    using namespace boost::spirit::qi;

    const char input[] = "1 2 3 4";
    auto f(std::begin(input)), l(std::end(input)-1);

    bool dummy = phrase_parse(
            f, l, 
            exp [ what_is_the_attr() ],
            space);
}

(注意: 这表明该方法的局限性 - 该技术假设您 有一个“否则”的工作语法,你知道如何传递一个输入 满足表达足以触发语义动作。在大多数情况下, 但是,当您破解 Spirit 解析器时,这将是真的

让我们测试一下。例如。让我们看看有什么区别和表达 中等复杂度,同样包裹在 qi::raw[] 指令中:

int main()
{
    detect_attr_type(       -(int_ >> *int_)    );
    detect_attr_type( raw [ -(int_ >> *int_) ] );
}

输出:

what_is_the_attr: boost::optional<boost::fusion::vector2<int, std::vector<int, std::allocator<int> > > >
what_is_the_attr: boost::iterator_range<char const*>

在底部,我们将把它应用于 OP 中的问题。

使用示例:检测传入语义动作的类型

我们可以使用相同的一元函数对象 (what_is_the_attr) 来检测 然而,这些语义动作可以接受任意数量的参数,所以我们需要 概括。如果不是 variadic,这将是一项乏味的工作 模板 (woot! for c++0x):

struct what_are_the_arguments {
    template <typename...> struct result { typedef bool type; };

    template <typename... T> bool operator()(const T&... attr) const {
        std::vector<std::string> names { nameofType(attr)... };
        std::cerr << "what_are_the_arguments:\n\t";
        std::copy(names.begin(), names.end(), std::ostream_iterator<std::string>(std::cerr, "\n\t"));
        std::cerr << '\n';
        return true;
    }
};

对上述测试用例的重复表明 Spirit 实际上试图调用 如果可能的话,带有三个参数的语义动作(如documented):

what_are_the_arguments:
    boost::optional<boost::fusion::vector2<int, std::vector<int, std::allocator<int> > > >
    boost::spirit::unused_type
    bool

what_are_the_arguments:
    boost::iterator_range<char const*>
    boost::spirit::unused_type
    bool

但好消息是,您现在可以将其应用于任何语义操作:

template <typename ExpWSA> void test(const ExpWSA& exp)
{
    const char input[] = "1 2 3 4";
    auto f(std::begin(input)), l(std::end(input)-1);

    qi::phrase_parse(f, l, exp, qi::space);
}

int main()
{
    test(-(-double_ >> *int_) [ phx::bind(what_are_the_arguments(), _1, _2, _0, phx::ref(std::cout), 42) ]);
}

打印,对于这个(对不起)非常人为的例子:

what_are_the_arguments:
    boost::optional<double>
    std::vector<int, std::allocator<int> >
    boost::fusion::vector2<boost::optional<double>, std::vector<int, std::allocator<int> > >
    std::ostream
    int

应用于 OP

derived 规则的综合属性int_&gt;&gt;int_&gt;&gt;int_&gt;&gt;int_ 相同:

auto base_expr = int_ >> int_; // avoids assigning to struct attribute

rule<const char*, mybase(), space_type> base_       = base_expr;

test(base_     >> int_ >> int_ [ what_is_the_attr() ] );
test(base_expr >> int_ >> int_ [ what_is_the_attr() ] );

将打印

what_is_the_attr: boost::fusion::vector3<mybase, int, int>
what_is_the_attr: boost::fusion::vector4<int, int, int, int>

你的问题。我们在original thread 中讨论了一些基于此诊断的解决方法(并在此处查看其他答案)。但这篇文章应该有助于回答一般案例问题。

完整代码清单

集成形式,使用 gcc 4.6.1 --std=c++0x 和 boost 1_48 编译:

#include <cxxabi.h>
#include <iostream>
#include <iterator>
#include <stdlib.h>
#include <string>
#include <vector>

template <typename T> std::string nameofType(const T& v)
{
    int     status;
    char   *realname = abi::__cxa_demangle(typeid(v).name(), 0, 0, &status);
    std::string name(realname? realname : "????");
    free(realname);

    return name;
}

struct what_is_the_attr {
    template <typename> struct result { typedef bool type; };

    template <typename T> bool operator()(T& attr) const {
        std::cerr << "what_is_the_attr: " << nameofType(attr) << std::endl;
        return true;
    }
};

struct what_are_the_arguments {
    template <typename...> struct result { typedef bool type; };

    template <typename... T> bool operator()(const T&... attr) const {
        std::vector<std::string> names { nameofType(attr)... };
        std::cerr << "what_are_the_arguments:\n\t";
        std::copy(names.begin(), names.end(), std::ostream_iterator<std::string>(std::cerr, "\n\t"));
        std::cerr << '\n';
        return true;
    }
};

#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>

struct mybase             { int a,b; };
struct myderived : mybase { int c,d; };

BOOST_FUSION_ADAPT_STRUCT(mybase,    (int,a)(int,b));
BOOST_FUSION_ADAPT_STRUCT(myderived, (int,a)(int,b)(int,c)(int,d));

template <typename ExpWSA>
void test(const ExpWSA& exp)
{
    using namespace boost::spirit::qi;

    const char input[] = "1 2 3 4";
    auto f(std::begin(input)), l(std::end(input)-1);

    bool dummy = phrase_parse(f, l, exp, space);
}

int main()
{
    using namespace boost::spirit::qi;

    // Diagnostics for the OP case
    auto base_expr = int_ >> int_;                                   // avoids assigning to struct attribute
    rule<const char*, mybase(), space_type> base_       = base_expr;

    // Derived rule, different formulations
    test((base_     >> int_ >> int_) [ what_is_the_attr() ] );
    test((base_expr >> int_ >> int_) [ what_is_the_attr() ] );

    // Applied to attribute types
    test(raw [ -(int_ >> *int_) ]  [ what_is_the_attr() ] );
    test(-(int_ >> *int_)          [ what_is_the_attr() ] );

    // applied to semantic actions - contrived example
    namespace phx = boost::phoenix;
    test(-(-double_ >> *int_) [ phx::bind(what_are_the_arguments(), _1, _2, _0, phx::ref(std::cout), 42) ]);

    return 0;
}

【讨论】:

    【解决方案2】:

    为了清楚起见 - 这里的错误是base_ &gt;&gt; int_ &gt;&gt; int_ 被用作创建myderived 的规则的表达式,并且由于base_ 固定为mybase 类型,我们必须创建一个@ 987654326@ 来自一个 mybase 和两个 ints,但没有什么可以告诉 Spirit 如何做到这一点。

    您可以通过定义一个接受任何参数的仿函数来打印出 boost 通过解析 base_ &gt;&gt; int_ &gt;&gt; int_ 创建的值的类型,并告诉您它们是什么(以下代码改编自一些代码 sehe put在 SO 聊天中):

    struct what_is_the_attr
    {
        template <typename> struct result { typedef bool type; };
    
        template <typename T>
        static void print_the_type()
        {
            std::cout << "    ";
            std::cout << typeid(T).name();
            if(std::is_const<typename std::remove_reference<T>::type>::value)
                std::cout << " const";
            if(std::is_rvalue_reference<T>::value)
                std::cout << " &&";
            else if(std::is_lvalue_reference<T>::value)
                std::cout << " &";
        }
    
        template <typename Th, typename Th2, typename... Tt>
        static void print_the_type()
        {
            print_the_type<Th>();
            std::cout << ",\n";
            print_the_type<Th2, Tt...>();
        }
    
        template <typename... Ts>
        void operator()(Ts&&...) const
        {
            std::cout << "what_is_the_attr(\n";
            print_the_type<Ts...>();
            std::cout << ")" << std::endl;
        }
    };
    

    然后要使用它,在初始化器的语义操作中使用上面的actor来为你的错误规则:

    std::string input = "1 2 3 4";
    auto f(std::begin(input)), l(std::end(input));
    
    rule<decltype(f), mybase()   , space_type> base_    = int_ >> int_;
    rule<decltype(f), myderived(), space_type> derived_ = (base_ >> int_ >> int_)[what_is_the_attr()];
    
    myderived data;
    bool ok = phrase_parse(f,l,derived_,space,data);
    

    请注意,您不能对 %= 使用自动属性传播(除非您从规则的声明类型中删除公开的属性类型)。

    运行它应该会产生一个编码类型,可以用c++filt -t解码:Live On Coliru

    $ g++ 9404189.cpp -std=c++0x
    $ ./a.out |c++filt -t
    what_is_the_attr(
        boost::fusion::vector3<mybase, int, int> &,
        boost::spirit::context<boost::fusion::cons<boost::spirit::unused_type&, boost::fusion::nil>, boost::fusion::vector0<void> > &,
        bool &)
    

    第一行 boost::fusion::vector3&lt;mybase, int, int&gt; 至少告诉您 boost 正在尝试从 mybaseintint 类型的 3 个对象创建您的返回类型。

    【讨论】:

    • +1 用于改进类型名称打印的类型特征魔法。酷!
    猜你喜欢
    • 2013-02-06
    • 2015-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多