检测Spirit语义操作中的参数类型

seh*_*ehe 14 c++ compiler-errors boost-spirit

一般情况:我无法弄清楚为什么我的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);
Run Code Online (Sandbox Code Playgroud)

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

(松散地改编自精神总表上的帖子)

seh*_*ehe 10

我可以解决这个特殊情况下的问题(事实上我们讨论了列表中的选项),但实际上,这种"神秘"错误会更频繁地使用Boost Spirit,并且很容易掌握一般类问题.

您的第一个资源应该是优秀的精神文档,它详细说明了给定解析器原语,运算符或指令的合成属性.请参见参考部分 ,以精神齐文档.

在某些情况下,我已经将焦点从"试图将信息从编译器错误列表中删除"转移到"主动查询Spirit传递的类型".我使用的技术是Polymorphic Callable Type(参见Spirit/Fusion docs).

这是一个使用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;
    }
};
Run Code Online (Sandbox Code Playgroud)

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

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

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);
}
Run Code Online (Sandbox Code Playgroud)

(注意: 这显示了该方法的局限性 - 该技术假定您具有'其他'工作语法,并且您知道如何传递满足表达式的输入足以触发语义操作.在大多数情况下,这将是正确的.你是在攻击你的灵魂解析器,但是)

我们来试试吧.例如,让我们看看介质复杂度与表达式之间的区别是什么,以及在qi::raw[]指令中包含的内容:

int main()
{
    detect_attr_type(       -(int_ >> *int_)    );
    detect_attr_type( raw [ -(int_ >> *int_) ] );
}
Run Code Online (Sandbox Code Playgroud)

输出:

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*>
Run Code Online (Sandbox Code Playgroud)

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

示例使用:检测传递给Semantic Actions的类型

我们可以使用相同的一元函数object(what_is_the_attr)来检测这些,但是,语义动作可以使用任意数量的参数,因此我们需要进行概括.如果它不是可变参数模板(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;
    }
};
Run Code Online (Sandbox Code Playgroud)

对上述测试用例重复显示,如果可能,Spirit实际上会尝试使用三个参数调用语义操作(如文档所述):

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
Run Code Online (Sandbox Code Playgroud)

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

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) ]);
}
Run Code Online (Sandbox Code Playgroud)

印刷,为此(对不起)非常人为的例子:

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
Run Code Online (Sandbox Code Playgroud)

适用于OP

的的合成属性derived的规则是一样的int_>>int_>>int_>>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() ] );
Run Code Online (Sandbox Code Playgroud)

会打印

what_is_the_attr: boost::fusion::vector3<mybase, int, int>
what_is_the_attr: boost::fusion::vector4<int, int, int, int>
Run Code Online (Sandbox Code Playgroud)

有你的问题.我们在原始主题中讨论了基于此诊断的一些变通方法(并在此处查看其他答案).但这篇文章应该有助于回答一般案例问题.

完整代码清单

在集成形式中,使用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;
}
Run Code Online (Sandbox Code Playgroud)


je4*_*e4d 6

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

你可以base_ >> int_ >> int_通过定义一个可以接受任何参数的仿函数来打印出boost通过解析创建的值的类型,并告诉你它们是什么(下面的代码改编自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;
    }
};
Run Code Online (Sandbox Code Playgroud)

然后使用它,在初始化程序的语义操作中使用上面的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);
Run Code Online (Sandbox Code Playgroud)

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

然后运行它应该产生一个编码类型,可以用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 &)
Run Code Online (Sandbox Code Playgroud)

第一行,boost::fusion::vector3<mybase, int, int>至少告诉你boost正在尝试从3个类型的对象创建返回类型mybase,int并且int.