在boost精神中将属性传递给子规则

时间:2012-09-20 20:44:22

标签: c++ boost boost-spirit

我有两个具有相同属性的规则。

是否可以将matrix_规则的属性传递给matrixBlock_子规则? 我想保持repeat指令不要创建表单向量<的属性。取代。相反,它应该继续写入matrix_(numBlocks的次数)的属性。 我试图将该属性作为继承属性传递给子规则并进行编译(见下文)。但我在我的矢量中得到了几个“鬼”条目,这些条目不是来自phoenix :: push_back。 这对我来说似乎不是最佳方式。我们可以在matrixBlock_中使用自动属性传播而不是语义动作吗?

typedef vector<columnT> Matrix;
matrix_ = repeat(numBlocks)[ matrixBlock_(_val) ];
matrixBlock_ = *column[phoenix::push_back(_r1, _1)];

qi::rule<Iterator, Matrix(), ascii::space_type> matrix_;
qi::rule<Iterator, void(Matrix&), ascii::space_type> matrixBlock_;

更新

澄清问题:

如果我编写没有语义动作的规则,则matrix_的合成属性将是

vector< vector< columnT > >

-

typedef vector<columnT> Matrix;
matrix_ = repeat(numBlocks)[ matrixBlock_ ];
matrixBlock_ = *column;

qi::rule<Iterator, Matrix(), ascii::space_type> matrix_;
qi::rule<Iterator, Matrix(), ascii::space_type> matrixBlock_;

我希望它具有与matrixBlock_相同的属性类型,即1维数组。


我的实际解决方案是只使用一个规则。 (看起来很简单:-))

typedef vector<columnT> Matrix;
matrix_ = repeat(numBlocks)[ *column_[ phoenix::push_back(_val, _1) ] ];
//matrixBlock_ = *column;

qi::rule<Iterator, Matrix(), ascii::space_type> matrix_;
//qi::rule<Iterator, Matrix(), ascii::space_type> matrixBlock_;

更新

我能够在vs2010中使用此代码重现幻像条目并提升1.46.1

http://liveworkspace.org/code/505091dc4631a379763567168a728e0c

  

输出为:42,45,-9,3,2,1,12,34,56,0,0,0

我的错误是使用旧的Boost版本。没有1.5的牙龈。

现在我的语法有两个工作版本。是否可以在不使用push_back语义操作的情况下重新设计语法?

1 个答案:

答案 0 :(得分:4)

更新

回答您编辑过的问题:是的,您可以在没有语义操作的情况下执行此操作,只需执行以下操作:

template<typename It>
struct Parser : qi::grammar<It, Matrix(), qi::space_type>
{
    Parser() : Parser::base_type(matrix_)
    {
        matrixBlock_ = qi::lit(";") >> *qi::int_;
        matrix_      = qi::repeat(3)[ matrixBlock_ ];
    }
    qi::rule<It, Matrix(), qi::space_type> matrixBlock_, matrix_;
};

请注意,可能要验证行数/列数。 See my extended sample ,它使用额外的语义操作来检查(注意从*int_+int_的细微更改以避免空行):

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>

namespace qi    = boost::spirit::qi;
namespace karma = boost::spirit::karma;
namespace phx   = boost::phoenix;

typedef std::vector<int> Matrix;

template<typename It>
struct Parser : qi::grammar<It, Matrix(), qi::space_type>
{
    Parser() : Parser::base_type(matrix_)
    {
        using namespace qi;
        matrixBlock_ = lit(";") >> +int_ >> eps( 0 == (phx::size(_val) % 3));
        matrix_      = repeat(3)[ matrixBlock_ ];
    }
    qi::rule<It, Matrix(), qi::space_type> matrixBlock_, matrix_;
};

int main()
{
    std::string test = ";42 45 -9; 3 2 1; 12 34 56";

    std::string::const_iterator f(test.begin()), l(test.end());

    Parser<std::string::const_iterator> parser;
    Matrix m;

    if (qi::phrase_parse(f,l,parser,qi::space, m))
        std::cout << "Wokay\n";
    else
        std::cerr << "Uhoh\n";

    std::cout << karma::format(karma::auto_ % ", ", m) << "\n";
}

旧答案

是的,您可以使用Spirit的自定义点将用户定义的类型视为容器。我建议的文档条目在这里:

这是一个简单的例子,展示了如何使用它:live:

  关于'幻影条目'的

旁注,一般来说:

     

请注意,有一些与回溯语法和容器属性相关的常见问题解答。问题是,出于性能原因,解析器不会在回溯时撤消(“回滚”)对其底层容器的更改。您可以使用qi::hold强制执行此行为,但将语法重新设计为

可能是值得的      
      
  • 避免回溯或
  •   
  • 稍后提交属性(使用语义操作)
  •   

完整代码示例:

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>

namespace qi = boost::spirit::qi;
namespace karma = boost::spirit::karma;

struct Matrix
{
   std::vector<int> data;
};

namespace boost { namespace spirit { namespace traits {
   template <>
      struct is_container<Matrix>
      {
      };

   template <typename Attrib>
      struct push_back_container<Matrix, Attrib>
      {
         static bool call(Matrix& c, Attrib const& val)
         {
            c.data.push_back(val);
            return true;
         }
      };

   template <>
      struct container_value<Matrix>
      {
         typedef int type;
      };
} } }

template<typename It>
struct Parser : qi::grammar<It, Matrix(), qi::space_type>
{
   Parser() : Parser::base_type(start)
   {
      start = *qi::int_;
   }
   qi::rule<It, Matrix(), qi::space_type> start;
};

int main()
{
   std::string test = "42 45 -9";

   std::string::const_iterator f(test.begin()),
      l(test.end());

   Parser<std::string::const_iterator> parser;
   Matrix m;

    if (qi::phrase_parse(f,l,parser,qi::space, m))
      std::cout << "Wokay\n";
   else
      std::cerr << "Uhoh\n";

   std::cout << karma::format(karma::auto_ % ", ", m.data) << "\n";
}

输出:

Wokay
42, 45, -9

更新

更多背景知识:

当然,对于这样一个简单的例子,它只包含一个标准的受支持容器类型,使用fusion adaptation 相当容易:(http://liveworkspace.org/code/56aea8619867451a21cd49fddb1e93bd

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

namespace qi = boost::spirit::qi;
namespace karma = boost::spirit::karma;

struct Matrix { std::vector<int> data; }; 
BOOST_FUSION_ADAPT_STRUCT(Matrix, (std::vector<int>, data));

int main()
{
    std::string test = "42 45 -9";
    std::string::const_iterator f(test.begin()), l(test.end());

    Matrix m;
    if (qi::phrase_parse(f,l, qi::eps >> *qi::int_, qi::space, m))
        std::cout << karma::format(karma::auto_ % ", ", m.data) << "\n";
}

请注意,由于包含仅包含一个数据元素的结构的错误(AFAICT),qi::eps是必需的。参见例如discussion here(以及其他一些提及)