使用重复将Boost Spirit Qi存储到std :: vector中会导致模糊的类模板实例化

时间:2014-11-24 21:55:03

标签: c++ boost-spirit

将重复语句的结果存储到std :: vector会导致编译错误:

/usr/include/boost/spirit/home/qi/detail/pass_container.hpp:172:12: error: ambiguous 

    class template instantiation for ‘struct boost::spirit::qi::detail::pass_through_container_base<std::vector<Vertex3d<float> >, Vertex3d<float>, Vertex3d<float>, mpl_::bool_<false>, void>’

/usr/include/boost/spirit/home/qi/detail/pass_container.hpp:103:12: error: candidates are: struct boost::spirit::qi::detail::pass_through_container_base<Container, ValueType, Attribute, Sequence, typename boost::enable_if<boost::fusion::traits::is_sequence<Attribute> >::type>
     struct pass_through_container_base<Container, ValueType, Attribute
            ^
/usr/include/boost/spirit/home/qi/detail/pass_container.hpp:136:12: error:                 struct boost::spirit::qi::detail::pass_through_container_base<Container, ValueType, Attribute, Sequence, typename boost::enable_if<boost::spirit::traits::is_container<T2> >::type>
     struct pass_through_container_base<
            ^
/usr/include/boost/spirit/home/qi/detail/pass_container.hpp:172:12: error: invalid use of incomplete type ‘struct boost::spirit::qi::detail::pass_through_container_base<std::vector<wc3lib::Vertex3d<float> >, wc3lib::Vertex3d<float>, wc3lib::Vertex3d<float>, mpl_::bool_<false>, void>’
     struct pass_through_container
            ^
/usr/include/boost/spirit/home/qi/detail/pass_container.hpp:50:12: error: declaration of ‘struct boost::spirit::qi::detail::pass_through_container_base<std::vector<wc3lib::Vertex3d<float> >, wc3lib::Vertex3d<float>, wc3lib::Vertex3d<float>, mpl_::bool_<false>, void>’
     struct pass_through_container_base

以下代码用于语法:

qi::rule<Iterator, long32(), Skipper> integer_literal;
qi::rule<Iterator, float32(), Skipper> real_literal;
qi::rule<Iterator, VertexReal3d(), Skipper> vertex_real_3d;
qi::rule<Iterator, Geoset::Vertices(), Skipper, qi::locals<long32> > vertices;


integer_literal %=
        lexeme[
            qi::int_parser<long32>()
        ]
    ;

real_literal %=
        lexeme[
            qi::real_parser<float32>()
        ]
    ;

vertex_real_3d =
        lit('{')
        >> real_literal[at_c<0>(_val) = _1]
        >> lit(',')
        >> real_literal[at_c<1>(_val) = _1]
        >> lit(',')
        >> real_literal[at_c<2>(_val) = _1]
        >> lit('}')
    ;

vertices =
        lit("Vertices")
        >> integer_literal[_a = _1]
        >> lit('{')
        >> repeat(_a)[
            vertex_real_3d
            >> lit(',')
        ][_val = _1] // Does not work?
        >> lit('}')
    ;

...

typedef Vertex3d<float32> VertexData;
typedef VertexData VertexReal3d;
typedef std::vector<VertexData> Vertices;

...

BOOST_FUSION_ADAPT_ADT(
    wc3lib::mdlx::VertexData,
    (wc3lib::float32, wc3lib::float32, obj[0], obj[0] = val)
    (wc3lib::float32, wc3lib::float32, obj[1], obj[1] = val)
    (wc3lib::float32, wc3lib::float32, obj[2], obj[2] = val)
)

...

template<typename T, typename std::size_t N>
class BasicVertex : public std::array<T, N>
{
    public:
        typedef std::array<T, N> Base;

        BasicVertex() : Base()
        {
        }

        BasicVertex(const BasicVertex<T, N> &other) : Base(other) {
    }


};


template<typename T = float32>
class Vertex3d : public BasicVertex<T, 3>
{
    public:
        typedef BasicVertex<T, 3> Base;

        Vertex3d() : Base()
        {
        }

        Vertex3d(T x, T y, T z)
        {
            (*this)[0] = x;
            (*this)[1] = y;
            (*this)[2] = z;
        }

        Vertex3d(const Base &other) : Base(other) {
        }
};

规则顶点应返回VertexData的std :: vector。因此,重复用于解析固定数量的顶点。金额将作为整数值放在列表之前的解析文件中,并存储在_a。

编译错误提示它不能在&#34; is_sequence&#34;之间有所不同。和&#34; is_container&#34;。我不是精神的专家,所以我无法回答它究竟意味着什么。

2 个答案:

答案 0 :(得分:5)

这是我自制的结果。

Live On Coliru

我选择了改编VectorData类型。 (我注意到你可能没有太晚了)。

这使得语法变得复杂。我不确定 什么不起作用,因为在编译代码时它确实有用......

所以让我们在代码清理和示例中进行另一个练习:

  • 删除了语义操作
  • 删除了两条规则
  • 添加了调试信息
  • 在此处删除了所需的尾随','的奇特:

    '{' >> repeat(_a)[ vertex_real_3d >> (',' | &lit('}')) ] >> '}'
    
  • 删除了operator%=

  • 的多余用途
  • 删除了qi::lit()
  • 的多余用途
  • 不要在语法声明中公开qi::locals<>(这是一个实现细节)

现在代码的时钟频率为77行(比之前减少了25行):

<强> Live On Coliru

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

namespace qi = boost::spirit::qi;

using long32  = int32_t;
using float32 = float;

namespace Geoset {
    template <typename T>
        struct Vertex3d {
            T a,b,c;
        };

    typedef Vertex3d<float32>       VertexData;
    typedef VertexData              VertexReal3d;
    typedef std::vector<VertexData> Vertices;
}

BOOST_FUSION_ADAPT_STRUCT(Geoset::VertexData, (float32, a)(float32, b)(float32, c))

template <typename Iterator, typename Skipper = qi::space_type>
struct grammar : qi::grammar<Iterator, Geoset::Vertices(), Skipper> {

    grammar() : grammar::base_type(start) {
        using namespace qi;

        vertex_real_3d = 
            '{' >> real_literal >> ','
                >> real_literal >> ','
                >> real_literal >> '}'
            ;

        vertices %=
                "Vertices"
                >> omit [ integer_literal[_a = _1] ]
                >> '{' >> repeat(_a)[ vertex_real_3d >> (',' | &lit('}')) ] >> '}'
            ;

        start = vertices;

        BOOST_SPIRIT_DEBUG_NODES((start)(vertices)(vertex_real_3d))
    }
  private:
    qi::int_parser<long32> integer_literal;
    qi::real_parser<float32> real_literal;
    qi::rule<Iterator, Geoset::VertexReal3d(), Skipper> vertex_real_3d;
    qi::rule<Iterator, Geoset::Vertices(), Skipper, qi::locals<long32> > vertices;
    qi::rule<Iterator, Geoset::Vertices(), Skipper> start;
};

int main() {
    std::string const input = "Vertices 4 { \n"
        " { 1,  2,  3  }, \n"
        " { 4,  5,  6  }, \n"
        " { 7,  8,  9  }, \n"
        " { 10, 11, 12 } \n"
    "}";

    auto f(begin(input)), l(end(input));
    grammar<std::string::const_iterator> g;

    Geoset::Vertices vertices;
    bool ok = qi::phrase_parse(f,l,g,qi::space,vertices);

    if (ok) {
        std::cout << "Parsed: " << vertices.size() << "\n";
        for (auto& v : vertices)
            std::cout << boost::fusion::as_vector(v) << "\n";
    } else
        std::cout << "Parse failed\n";

    if (f!=l)
        std::cout << "Remaining input: '" << std::string(f,l) << "'\n";
}

答案 1 :(得分:2)

好的,通过编辑,问题变得可行。

确实有一个(公共)基类std::array<> 同时融合适应(幸运的是!)精神无法决定哪个属性赋值路径将被采取。

所以通过

修复它
  • 告诉灵魂“这些不是你正在寻找的机器人”(当它想到它看到一个容器时):

    namespace boost { namespace spirit { namespace traits {
        template <> 
        struct is_container<wc3lib::mdlx::VertexData> : mpl::false_ 
           { };
    } } }
    

    <强> Live On Coliru

  • 不是(公开)派生自std :: array

我想你可能想先问问自己,从std::array<>继承的重点是什么。现在明确定义Vertex类型的方式使它们成为非POD,这意味着性能将受到影响。

考虑一下

template <typename T, typename std::size_t N> class BasicVertex : public std::array<T,N> {
public:
    typedef std::array<T, N> Base;
};

template <typename T = float> class Vertex3d : public BasicVertex<T, 3> {
public:
    typedef BasicVertex<T, 3> Base;
};

template <typename T, typename std::size_t N> class BasicVertex {
public:
    std::array<T, N> data_;
};

template <typename T = float> class Vertex3d : public BasicVertex<T, 3> {
};

表现出色。实际上,我可能会写

template <typename T = float> struct Vertex3d {
    T x, y, z;
};

¹相信我,你希望一个图书馆来诊断这种情况