当前位置: 首页 > 工具软件 > Spirit > 使用案例 >

【BOOST C++字串专题11】Boost.Spirit(二)

严宸
2023-12-01

四、行动

        到目前为止,本章中的示例只检测到两件事:解析器是否成功以及解析器在哪里结束。但是,解析器通常以某种方式处理数据,正如您将在下一个示例中看到的那样。

Example 11.9. Linking actions with parsers

#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>

using namespace boost::spirit;

int main()
{
  std::string s;
  std::getline(std::cin, s);
  auto it = s.begin();
  bool match = qi::phrase_parse(it, s.end(),
    qi::int_[([](int i){ std::cout << i << '\n'; })], ascii::space);
  std::cout << std::boolalpha << match << '\n';
  if (it != s.end())
    std::cout << std::string{it, s.end()} << '\n';
}

        示例 11.9 使用 boost::spirit::qi::int_ 解析一个整数,然后将该整数写入标准输出。这就是为什么一个动作与 boost::spirit::qi::int_ 相关联。动作是应用解析器时调用的函数或函数对象。链接是使用运算符 operator[] 完成的,它被 boost::spirit::qi::int_ 和其他解析器重载。示例 11.9 使用 lambda 函数作为操作,该操作需要一个 int 类型的唯一参数并将其写入标准输出。

        如果您启动示例 11.9 并输入一个数字,则会显示该数字。

        传递给动作的参数类型取决于解析器。例如, boost::spirit::qi::int_ 转发一个 int 值,而 boost::spirit::qi::float_ 传递一个 float 值。

Example 11.10. Boost.Spirit with Boost.Phoenix

#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/qi.hpp>
#include <boost/phoenix/phoenix.hpp>
#include <string>
#include <iostream>

using namespace boost::spirit;
using boost::phoenix::ref;

int main()
{
  std::string s;
  std::getline(std::cin, s);
  auto it = s.begin();
  int i;
  bool match = qi::phrase_parse(it, s.end(), qi::int_[ref(i) = qi::_1],
    ascii::space);
  std::cout << std::boolalpha << match << '\n';
  if (match)
    std::cout << i << '\n';
  if (it != s.end())
    std::cout << std::string{it, s.end()} << '\n';
}

         示例 11.10 使用 Boost.Phoenix 将使用 boost::spirit::qi::int_ 解析的 int 值存储在 i 中。如果 boost::spirit::qi::phrase_parse() 返回 true,则将 i 写入标准输出。

        这个例子在包含来自 Boost.Spirit 的头文件之前定义了宏 BOOST_SPIRIT_USE_PHOENIX_V3。此宏选择 Boost.Phoenix 的第三个和当前版本。这很重要,因为 Boost.Phoenix 是从 Boost.Spirit 派生的,而 Boost.Spirit 包含 Boost.Phoenix 的第二个版本。如果未定义 BOOST_SPIRIT_USE_PHOENIX_V3,则 Boost.Phoenix 的第二个版本将包含在 Boost.Spirit 头文件中,第三个版本将包含在 boost/phoenix/phoenix.hpp 中。不同的版本会导致编译器错误。

        请注意如何详细定义 lambda 函数。 boost::phoenix::ref() 创建对变量 i 的引用。但是,占位符 _1 不是来自 Boost.Phoenix,而是来自 Boost.Spirit。这很重要,因为 boost::spirit::qi::_1 提供了对具有通常预期类型的​​解析值的访问——示例 11.10 中的 int。如果 lambda 函数使用 boost::phoenix::placeholders::arg1,编译器会报错,因为 boost::phoenix::placeholders::arg1 不代表 int;它将基于 Boost.Spirit 中的另一种类型,并且需要提取 int 值。

五、属性

        操作是处理解析值的一种选择。另一种选择是将对象传递给 boost::spirit::qi::parse() 或 boost::spirit::qi::phrase_parse() 用于存储解析值。这些对象称为属性。它们的类型必须与解析器的类型相匹配。

        您已经在上一节中使用过属性。传递给动作的参数也是属性。每个解析器都有一个属性。例如,解析器 boost::spirit::qi::int_ 具有 int 类型的属性。在以下示例中,属性不会作为参数传递给函数。相反,解析后的值存储在属性中,可以在 boost::spirit::qi::parse() 或 boost::spirit::qi::phrase_parse() 返回后处理。

Example 11.11. Storing an int value in an attribute

#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>

using namespace boost::spirit;

int main()
{
  std::string s;
  std::getline(std::cin, s);
  auto it = s.begin();
  int i;
  if (qi::phrase_parse(it, s.end(), qi::int_, ascii::space, i))
    std::cout << i << '\n';
}

        示例 11.11 使用解析器 boost::spirit::qi::int_。解析后的 int 值存储在变量 i 中。 i 作为另一个参数传递给 boost::spirit::qi::phrase_parse(),因此成为解析器 boost::spirit::qi::int_ 的一个属性。 如果您启动示例 11.11 并输入一个数字,该数字将被写入标准输出流。

Example 11.12. Storing several int values in an attribute

#include <boost/spirit/include/qi.hpp>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
#include <iostream>

using namespace boost::spirit;

int main()
{
  std::string s;
  std::getline(std::cin, s);
  auto it = s.begin();
  std::vector<int> v;
  if (qi::phrase_parse(it, s.end(), qi::int_ % ',', ascii::space, v))
  {
    std::ostream_iterator<int> out{std::cout, ";"};
    std::copy(v.begin(), v.end(), out);
  }
}

        示例 11.12 使用了一个用 qi::int_ % ',' 定义的解析器。解析器接受由逗号分隔的任意数量的整数。像往常一样忽略空格。

        因为解析器可以返回多个 int 值,所以属性的类型必须支持存储多个 int 值。该示例传递一个向量。如果您开始该示例并输入多个以逗号分隔的整数,则这些整数将写入以分号分隔的标准输出流。

        除了向量,您还可以传递其他类型的容器,例如 std::list。Boost.Spirit 文档描述了哪些属性类型必须与哪些运算符一起使用。

 类似资料: