C++ Boost Spirit精通教程

 更新时间:2022年11月11日 15:49:53   作者:无水先生  
Boost是为C++语言标准库提供扩展的一些C++程序库的总称。Boost库是一个可移植、提供源代码的C++库,作为标准库的后备,是C++标准化进程的开发引擎之一,是为C++语言标准库提供扩展的一些C++程序库的总称

rule规则

在 Boost.Spirit 中,解析器由规则组成。由于规则通常基于 Boost.Spirit 提供的解析器,因此没有明显的区别。例如, boost::spirit::ascii::digit 既可以是解析器,也可以是规则。通常,规则指的是更复杂的表达式,例如 qi::int_ % ','。

在迄今为止的所有示例中,规则都直接传递给 boost::spirit::qi::parse() 或 boost::spirit::qi::phrase_parse()。通过 boost::spirit::qi::rule,Boost.Spirit 提供了一个类来定义规则变量。例如,如果应该将规则存储在类的成员变量中,则需要 boost::spirit::qi::rule。

Example11.13.Defining rules withboost::spirit::qi::rule

#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();
  qi::rule<std::string::iterator, std::vector<int>(),
    ascii::space_type> values = qi::int_ % ',';
  std::vector<int> v;
  if (qi::phrase_parse(it, s.end(), values, ascii::space, v))
  {
    std::ostream_iterator<int> out{std::cout, ";"};
    std::copy(v.begin(), v.end(), out);
  }
}

示例 11.13 的工作方式与示例 11.12 类似。如果您输入多个以逗号分隔的整数,它们将用分号显示。与前面的示例相比,解析器没有直接传递给 boost::spirit::qi::phrase_parse(),而是在 boost::spirit::qi::rule 变量中定义。

boost::spirit::qi::rule 是一个类模板。唯一的强制参数是被解析字符串的迭代器类型。在示例中,还传递了另外两个可选模板参数。

第二个模板参数是 std::vector<int>(),它是返回 std::vector<int> 类型向量且不需要参数的函数的签名。该模板参数表示解析的属性类型为int向量。

第三个模板参数是 boost::spirit::qi::phrase_parse() 使用的 skipper 的类型。在示例中,使用了船长 boost::spirit::ascii::space。该船长的类型可通过 boost::spirit::ascii::space_type 获得,并作为模板参数传递给 boost::spirit::qi::rule。

如果您希望您的代码独立于平台并使用 C++11 开发环境,您应该更喜欢 boost::spirit::qi::rule 而不是关键字 auto。如果值是用 auto 定义的,则该示例在 GCC 和 Clang 中可以正常工作。然而,在 Visual C++ 2013 中,只有第一个数字被解析并写入标准输出。

Example11.14.Nesting Rules

#include <boost/spirit/include/qi.hpp>
#include <boost/variant.hpp>
#include <string>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace boost::spirit;
struct print : public boost::static_visitor<>
{
  template <typename T>
  void operator()(T t) const
  {
    std::cout << std::boolalpha << t << ';';
  }
};
int main()
{
  std::string s;
  std::getline(std::cin, s);
  auto it = s.begin();
  qi::rule<std::string::iterator, boost::variant<int, bool>(),
    ascii::space_type> value = qi::int_ | qi::bool_;
  qi::rule<std::string::iterator, std::vector<boost::variant<int, bool>>(),
    ascii::space_type> values = value % ',';
  std::vector<boost::variant<int, bool>> v;
  if (qi::phrase_parse(it, s.end(), values, ascii::space, v))
  {
    for (const auto &elem : v)
      boost::apply_visitor(print{}, elem);
  }
}

Example11.14.Nesting Rules

#include <boost/spirit/include/qi.hpp>
#include <boost/variant.hpp>
#include <string>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace boost::spirit;
struct print : public boost::static_visitor<>
{
  template <typename T>
  void operator()(T t) const
  {
    std::cout << std::boolalpha << t << ';';
  }
};
int main()
{
  std::string s;
  std::getline(std::cin, s);
  auto it = s.begin();
  qi::rule<std::string::iterator, boost::variant<int, bool>(),
    ascii::space_type> value = qi::int_ | qi::bool_;
  qi::rule<std::string::iterator, std::vector<boost::variant<int, bool>>(),
    ascii::space_type> values = value % ',';
  std::vector<boost::variant<int, bool>> v;
  if (qi::phrase_parse(it, s.end(), values, ascii::space, v))
  {
    for (const auto &elem : v)
      boost::apply_visitor(print{}, elem);
  }
}

example11.14 定义了两个规则,其中一个引用另一个:values 定义为 value % ',',value 设置为 qi::int_ | qi::bool_。 values 表示可以解析由逗号分隔的任意数量的值。 value 将值定义为整数或布尔值。总之,规则规定用逗号分隔的整数和布尔值可以按任何顺序输入。

为了存储任意数量的值,提供了一个 std::vector 类型的容器。因为值的类型是 int 或 bool,所以需要一个可以存储 int 或 bool 值的类。根据属性类型和运算符的概述,必须使用来自 Boost.Variant 的类 boost::variant。

如果您启动示例并输入以逗号分隔的整数和布尔值,则这些值将写入以分号分隔的标准输出流。这是在 Boost.Variant 提供的函数 boost::apply_visitor() 的帮助下完成的。这个函数需要一个访问者——在这个例子中是类 print 的一个对象。

请注意,布尔值必须输入为 true 和 false。

语法

如果要解析复杂的格式,需要定义多个相互引用的规则,可以用 boost::spirit::qi::grammar 进行分组。

Example11.15.Grouping rules in a grammar

#include <boost/spirit/include/qi.hpp>
#include <boost/variant.hpp>
#include <string>
#include <vector>
#include <iostream>
using namespace boost::spirit;
template <typename Iterator, typename Skipper>
struct my_grammar : qi::grammar<Iterator,
  std::vector<boost::variant<int, bool>>(), Skipper>
{
  my_grammar() : my_grammar::base_type{values}
  {
    value = qi::int_ | qi::bool_;
    values = value % ',';
  }
  qi::rule<Iterator, boost::variant<int, bool>(), Skipper> value;
  qi::rule<Iterator, std::vector<boost::variant<int, bool>>(), Skipper>
    values;
};
struct print : public boost::static_visitor<>
{
  template <typename T>
  void operator()(T t) const
  {
    std::cout << std::boolalpha << t << ';';
  }
};
int main()
{
  std::string s;
  std::getline(std::cin, s);
  auto it = s.begin();
  my_grammar<std::string::iterator, ascii::space_type> g;
  std::vector<boost::variant<int, bool>> v;
  if (qi::phrase_parse(it, s.end(), g, ascii::space, v))
  {
    for (const auto &elem : v)
      boost::apply_visitor(print{}, elem);
  }
}

示例 11.15 的工作方式与示例 11.14 类似:您可以按任何顺序输入整数和布尔值,并以逗号分隔。它们将以相同的顺序写入标准输出流,但用分号分隔。该示例使用与前一个相同的规则——值和值。然而,这一次规则被分组在一个语法中。语法在一个名为 my_grammar 的类中定义,该类派生自 boost::spirit::qi::grammar。

my_grammar 和 boost::spirit::qi::grammar 都是类模板。 boost::spirit::qi::grammar 期望的模板参数与 boost::spirit::qi::rule 期望的模板参数相同。要解析的字符串的迭代器类型必须传递给 boost::spirit::qi::grammar。您还可以传递定义属性类型和船长类型的函数的签名。

在 my_grammar 中, boost::spirit::qi::rule 用于定义规则值和值。规则被定义为成员变量并在构造函数中初始化。

请注意,最外层的规则必须与 base_type 一起传递给基类的构造函数。这样,Boost.Spirit 就知道哪个规则是语法的入口点。

一旦定义了语法,就可以像解析器一样使用它。在示例 11.15 中,my_grammar 在 main() 中实例化以创建 g。然后将 g 传递给 boost::spirit::qi::phrase_parse()。

Example11.16.Storing parsed values in structures

#include <boost/spirit/include/qi.hpp>
#include <boost/variant.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <string>
#include <vector>
#include <iostream>
using namespace boost::spirit;
typedef boost::variant<int, bool> int_or_bool;
struct int_or_bool_values
{
  int_or_bool first;
  std::vector<int_or_bool> others;
};
BOOST_FUSION_ADAPT_STRUCT(
  int_or_bool_values,
  (int_or_bool, first)
  (std::vector<int_or_bool>, others)
)
template <typename Iterator, typename Skipper>
struct my_grammar : qi::grammar<Iterator, int_or_bool_values(), Skipper>
{
  my_grammar() : my_grammar::base_type{values}
  {
    value = qi::int_ | qi::bool_;
    values = value >> ',' >> value % ',';
  }
  qi::rule<Iterator, int_or_bool(), Skipper> value;
  qi::rule<Iterator, int_or_bool_values(), Skipper> values;
};
struct print : public boost::static_visitor<>
{
  template <typename T>
  void operator()(T t) const
  {
    std::cout << std::boolalpha << t << ';';
  }
};
int main()
{
  std::string s;
  std::getline(std::cin, s);
  auto it = s.begin();
  my_grammar<std::string::iterator, ascii::space_type> g;
  int_or_bool_values v;
  if (qi::phrase_parse(it, s.end(), g, ascii::space, v))
  {
    print p;
    boost::apply_visitor(p, v.first);
    for (const auto &elem : v.others)
      boost::apply_visitor(p, elem);
  }
}

示例 11.16 基于前面的示例,但需要至少两个值。规则值定义为 value >> ',' >> value % ','。

values 中的第一个组件是 value,第二个是 value % ','。第一个组件解析的值必须存储在 boost::variant 类型的对象中。第二个组件解析的值必须存储在容器中。使用 int_or_bool_values,该示例提供了一个结构来存储由规则值的两个组件解析的值。

要将 int_or_bool_values 与 Boost.Spirit 一起使用,必须使用宏 BOOST_FUSION_ADAPT_STRUCT。该宏由 Boost.Fusion 提供。该宏可以将 int_or_bool_values 视为具有 int_or_bool 和 std::vector<int_or_bool> 类型的两个值的元组。因为这个元组具有正确数量的正确类型的值,所以可以使用签名 int_or_bool_values() 定义值。 values 将在 first 中存储第一个解析值,在其他中存储所有其他解析值。

int_or_bool_values 类型的对象作为属性传递给 boost::spirit::qi::phrase_parse()。如果您启动示例并输入至少两个以逗号分隔的整数或布尔值,则它们都存储在属性中并写入标准输出流。

解析器已从上一个示例中使用的内容进行了更改。如果值是用 value % ',' 定义的,则 int_or_bool_values 将只有一个成员变量,并且所有解析的值都可以存储在一个向量中,如前面的示例所示。因此,int_or_bool_values 就像一个只有一个值的元组——Boost.Spirit 不支持。只有一个成员变量的结构将导致编译器错误。该问题有多种解决方法。

练习

创建一个可以加减整数的解析器。解析器应该能够处理像 1+2-5+8 这样的输入,并将结果(这里是 6)写入标准输出。

扩展您的解析器:它现在应该也支持浮点数。此外,应该可以使用分数。新的解析器应该能够处理像 1.2+6/5-0.9 这样的输入,并且应该将结果(这里是 1.5)写入标准输出。

到此这篇关于C++ Boost Spirit精通教程的文章就介绍到这了,更多相关C++ Boost Spirit内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++ 详解数据结构中的搜索二叉树

    C++ 详解数据结构中的搜索二叉树

    搜索二叉树是一种具有良好排序和查找性能的二叉树数据结构,包括多种操作,本篇只介绍插入,排序(遍历),和删除操作,重点是删除操作比较复杂
    2022-04-04
  • C语言之预处理命令的深入讲解

    C语言之预处理命令的深入讲解

    这篇文章主要给大家介绍了关于C语言之预处理命令的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • QT实现贪吃蛇游戏代码详解

    QT实现贪吃蛇游戏代码详解

    本文主要为大家详细介绍了在QT中实现贪吃蛇游戏的详细教程,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-11-11
  • C/C++实现高并发http服务器的代码示例

    C/C++实现高并发http服务器的代码示例

    这篇文章简单给大家介绍了C/C++实现高并发http服务器的代码示例,文章通过代码和图文介绍的非常详细,感兴趣的同学可以参考阅读
    2023-07-07
  • C++ this指针和空指针的具体使用

    C++ this指针和空指针的具体使用

    这篇文章主要介绍了C++ this指针和空指针的具体使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • C语言实现经典小游戏井字棋的示例代码

    C语言实现经典小游戏井字棋的示例代码

    这个三子棋游戏是在学习C语言的过程中自己编写的一个小游戏,现在将自己的思路(主要以流程图形式和代码中的注释表达)和具体代码以及运行结果分享出来以供大家学习参考,希望对大家有所帮助
    2022-11-11
  • C++ explicit关键字讲解

    C++ explicit关键字讲解

    这篇文章主要介绍了C++ explicit关键字讲解,++提供了explicit关键字,相对于implicit而言,他默认关闭了隐式类型转换方法。至于两者有什么区别,看下面文章内容的介绍吧
    2021-12-12
  • Qt可视化大屏布局的实现

    Qt可视化大屏布局的实现

    数据可视化大屏在项目中的使用很常见,本文主要介绍了Qt可视化大屏布局的实现,具有一定的参考价值,感兴趣的可以了解一下
    2024-02-02
  • C语言中你不知道的隐式类型转换规则详解

    C语言中你不知道的隐式类型转换规则详解

    在C语言中,类型转换的方式一般可分为隐式类型转换和显示类型转换(也称为强制类型转换),其中隐式类型转换由编译器自动进行,不需要程序员干预,本文给大家详细介绍了C语言中隐式类型转换规则,需要的朋友可以参考下
    2024-01-01
  • 关于C/C++内存管理示例详解

    关于C/C++内存管理示例详解

    这篇文章主要给大家介绍了C/C++内存管理的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-05-05

最新评论