亲宝软件园·资讯

展开

C++ Boost Fusion创建异构容器详解

无水先生 人气:0

一、说明

标准库提供了许多容器,它们有一个共同点:它们是同类的。也就是说,标准库中的容器只能存储一种类型的元素。 std::vector<int> 类型的向量只能存储 int 值,而 std::vector<std::string> 类型的向量只能存储字符串。

Boost.Fusion 使创建异构容器成为可能。例如,您可以创建一个向量,其第一个元素是 int,第二个元素是字符串。此外,Boost.Fusion 提供了处理异构容器的算法。您可以将 Boost.Fusion 视为异构容器的标准库。

严格来说,从C++11开始,标准库就提供了一个异构容器,std::tuple。您可以对存储在元组中的值使用不同的类型。 Boost.Fusion 中的 boost:fusion::tuple 是类似的类型。虽然标准库没有提供更多,但元组只是 Boost.Fusion 的起点。

二、示例和代码

示例 50.1。处理融合元组

#include <boost/fusion/tuple.hpp>
#include <string>
#include <iostream>
using namespace boost::fusion;
int main()
{
  typedef tuple<int, std::string, bool, double> tuple_type;
  tuple_type t{10, "Boost", true, 3.14};
  std::cout << get<0>(t) << '\n';
  std::cout << get<1>(t) << '\n';
  std::cout << std::boolalpha << get<2>(t) << '\n';
  std::cout << get<3>(t) << '\n';
}

Example50.1

示例 50.1 定义了一个由 int、std::string、bool 和 double 组成的元组。该元组基于 boost:fusion::tuple。在示例 50.1 中,元组随后被实例化、初始化,并使用 boost::fusion::get() 检索各种元素并写入标准输出。函数 boost::fusion::get() 类似于 std::get(),它访问 std::tuple 中的元素。

融合元组与标准库中的元组没有区别。因此,Boost.Fusion 提供函数 boost::fusion::make_tuple() 就不足为奇了,它的工作方式类似于 std::make_tuple()。但是,Boost.Fusion 提供了超出标准库中所提供功能的附加功能。

示例 50.2。使用 boost::fusion::for_each() 迭代元组

#include <boost/fusion/tuple.hpp>
#include <boost/fusion/algorithm.hpp>
#include <string>
#include <iostream>
using namespace boost::fusion;
struct print
{
  template <typename T>
  void operator()(const T &t) const
  {
    std::cout << std::boolalpha << t << '\n';
  }
};
int main()
{
  typedef tuple<int, std::string, bool, double> tuple_type;
  tuple_type t{10, "Boost", true, 3.14};
  for_each(t, print{});
}

示例 50.2 介绍了算法 boost::fusion::for_each(),它迭代 Fusion 容器。此处使用该函数将元组 t 中的值写入标准输出。

boost::fusion::for_each() 旨在像 std::for_each() 一样工作。 std::for_each() 仅迭代同类容器,而 boost::fusion::for_each() 适用于异构容器。您将容器而不是迭代器传递给 boost::fusion::for_each()。如果您不想遍历容器中的所有元素,可以使用视图。

示例 50.3。使用 boost::fusion::filter_view 过滤 Fusion 容器

#include <boost/fusion/tuple.hpp>
#include <boost/fusion/view.hpp>
#include <boost/fusion/algorithm.hpp>
#include <boost/type_traits.hpp>
#include <boost/mpl/arg.hpp>
#include <string>
#include <iostream>
using namespace boost::fusion;
struct print
{
  template <typename T>
  void operator()(const T &t) const
  {
    std::cout << std::boolalpha << t << '\n';
  }
};
int main()
{
  typedef tuple<int, std::string, bool, double> tuple_type;
  tuple_type t{10, "Boost", true, 3.14};
  filter_view<tuple_type, boost::is_integral<boost::mpl::arg<1>>> v{t};
  for_each(v, print{});
}

Boost.Fusion 提供了视图,它像容器一样工作但不存储数据。使用视图,可以以不同方式访问容器中的数据。视图类似于来自 Boost.Range 的适配器。然而,虽然来自 Boost.Range 的适配器只能应用于一个容器,但来自 Boost.Fusion 的视图可以跨越来自多个容器的数据。

示例 50.3 使用类 boost::fusion::filter_view 来过滤元组 t。过滤器指示 boost::fusion::for_each() 仅写入基于整数类型的元素。

boost::fusion::filter_view 期望第一个模板参数是要过滤的容器类型。第二个模板参数必须是过滤元素的谓词。谓词必须根据元素的类型过滤元素。

该库之所以称为 Boost.Fusion,是因为它结合了两个世界:C++ 程序在运行时处理值,在编译时处理类型。对于开发人员来说,运行时的值通常更为重要。来自标准库的大多数工具在运行时处理值。为了在编译时处理类型,使用了模板元编程。值在运行时根据其他值进行处理,而类型在编译时根据其他类型进行处理。 Boost.Fusion 允许您根据类型处理值。

传递给 boost::fusion::filter_view 的第二个模板参数是一个谓词,它将应用于元组中的每个类型。谓词需要一个类型作为参数,如果该类型应该是视图的一部分,则返回 true。如果返回 false,则过滤掉该类型。

示例 50.3 使用来自 Boost.TypeTraits 的类 boost::is_integral。 boost::is_integral 是一个检查类型是否为整数的模板。因为必须将模板参数传递给 boost::fusion::filter_view,所以使用来自 Boost.MPL 的占位符 boost::mpl::arg<1> 来创建 lambda 函数。 boost::mpl::arg<1> 类似于来自 Boost.Phoenix 的 boost::phoenix::place_holders::arg1。在示例 50.3 中,视图 v 将仅包含元组中的 int 和 bool 元素,因此,该示例会将 10 和 true 写入标准输出。

示例 50.4。使用迭代器访问 Fusion 容器中的元素

#include <boost/fusion/tuple.hpp>
#include <boost/fusion/iterator.hpp>
#include <boost/mpl/int.hpp>
#include <string>
#include <iostream>
using namespace boost::fusion;
int main()
{
  typedef tuple<int, std::string, bool, double> tuple_type;
  tuple_type t{10, "Boost", true, 3.14};
  auto it = begin(t);
  std::cout << *it << '\n';
  auto it2 = advance<boost::mpl::int_<2>>(it);
  std::cout << std::boolalpha << *it2 << '\n';
}

在看过 boost::fusion::tuple 和 boost::fusion::for_each() 之后,在示例 50.4 中找到迭代器应该不足为奇。 Boost.Fusion 提供了几个独立的函数,例如 boost::fusion::begin() 和 boost::fusion::advance(),它们的工作方式类似于标准库中的同名函数。

迭代器要递增的步数作为模板参数传递给 boost::fusion::advance()。该示例再次使用来自 Boost.MPL 的 boost::mpl::int_。

boost::fusion::advance() 返回一个与传递给函数的迭代器类型不同的迭代器。这就是示例 50.4 使用第二个迭代器 it2 的原因。您不能将 boost::fusion::advance() 的返回值分配给第一个迭代器 it。示例 50.4 将 10 和 true 写入标准输出。

除了示例中介绍的函数之外,Boost.Fusion 还提供了与迭代器一起使用的其他函数。其中包括:boost::fusion::end()、boost::fusion::distance()、boost::fusion::next() 和 boost::fusion::prior()。

示例 50.5。具有 boost::fusion::vector 的异构向量

#include <boost/fusion/container.hpp>
#include <boost/fusion/sequence.hpp>
#include <boost/mpl/int.hpp>
#include <string>
#include <iostream>
using namespace boost::fusion;
int main()
{
  typedef vector<int, std::string, bool, double> vector_type;
  vector_type v{10, "Boost", true, 3.14};
  std::cout << at<boost::mpl::int_<0>>(v) << '\n';
  auto v2 = push_back(v, 'X');
  std::cout << size(v) << '\n';
  std::cout << size(v2) << '\n';
  std::cout << back(v2) << '\n';
}

到目前为止,我们只看到了一个异构容器,boost::fusion::tuple。示例 50.5 引入了另一个容器,boost::fusion::vector。

boost::fusion::vector 是一个向量:通过索引访问元素。访问不是使用运算符 operator[] 实现的。相反,它是使用独立函数 boost::fusion::at() 实现的。索引作为用 boost::mpl::int_ 包装的模板参数传递。

此示例将一个 char 类型的新元素添加到向量中。这是通过独立函数 boost::fusion::push_back() 完成的。两个参数被传递给 boost::fusion::push_back():要添加元素的向量和要添加的值。

boost::fusion::push_back() 返回一个新向量。矢量 v 没有改变。新向量是添加了元素的原始向量的副本。

此示例使用 boost::fusion::size() 获取向量 v 和 v2 中的元素数量,并将这两个值写入标准输出。该程序显示 4 和 5。然后调用 boost::fusion::back() 获取 v2 中的最后一个元素并将其写入标准输出,在本例中值为 X。

如果您更仔细地查看示例 50.5,您会注意到 boost::fusion::tuple 和 boost::fusion::vector 之间没有区别;他们是一样的。因此,示例 50.5 也可以与 boost::fusion::tuple 一起使用。

Boost.Fusion 提供了额外的异构容器,包括:boost::fusion::deque、boost::fusion::list 和 boost::fusion::set。示例 50.6 引入了 boost::fusion::map,它是键/值对的容器。

示例 50.6。带有 boost::fusion::map 的异构映射

#include <boost/fusion/container.hpp>
#include <boost/fusion/sequence.hpp>
#include <boost/fusion/algorithm.hpp>
#include <string>
#include <iostream>
using namespace boost::fusion;
int main()
{
  auto m = make_map<int, std::string, bool, double>("Boost", 10, 3.14, true);
  if (has_key<std::string>(m))
    std::cout << at_key<std::string>(m) << '\n';
  auto m2 = erase_key<std::string>(m);
  auto m3 = push_back(m2, make_pair<float>('X'));
  std::cout << std::boolalpha << has_key<std::string>(m3) << '\n';
}

Example50.6

示例 50.6 使用 boost::fusion::map() 创建了一个异构映射。地图的类型是 boost::fusion::map,由于关键字 auto,它没有在示例中写出。

boost::fusion::map 类型的映射像 std::map 一样存储键/值对。但是,Fusion 映射中的键是类型。键/值对由一个类型和一个映射到该类型的值组成。该值可能是与键不同的类型。在示例 50.6 中,字符串“Boost”映射到键 int。

创建映射后,调用 boost::fusion::has_key() 以检查键 std::string 是否存在。然后,调用 boost::fusion::at_key() 以获取映射到该键的值。因为数字 10 映射到 std::string,所以它被写入标准输出。

然后使用 boost::fusion::erase_key() 擦除键/值对。这不会改变地图 m。 boost::fusion::erase_key() 返回一个新映射,该映射缺少已擦除的键/值对。

对 boost::fusion::push_back() 的调用将一个新的键/值对添加到映射中。键是浮点数,值是“X”。调用 boost::fusion::make_pair() 来创建新的键/值对。此函数类似于 std::make_pair()。

最后,再次调用 boost::fusion::has_key() 以检查映射是否具有键 std::string。因为它已被删除,所以返回 false。

请注意,在调用 boost::fusion::at_key() 之前,您不需要调用 boost::fusion::has_key() 来检查密钥是否存在。如果传递给 boost::fusion::at_key() 的键在映射中不存在,则会出现编译器错误。

示例 50.7。结构融合适配器

#include <boost/fusion/adapted.hpp>
#include <boost/fusion/sequence.hpp>
#include <boost/mpl/int.hpp>
#include <iostream>
struct strct
{
  int i;
  double d;
};
BOOST_FUSION_ADAPT_STRUCT(strct,
  (int, i)
  (double, d)
)
using namespace boost::fusion;
int main()
{
  strct s = {10, 3.14};
  std::cout << at<boost::mpl::int_<0>>(s) << '\n';
  std::cout << back(s) << '\n';
}

Boost.Fusion 提供了几个宏,让您可以将结构用作 Fusion 容器。这是可能的,因为结构可以充当异构容器。由于宏 BOOST_FUSION_ADAPT_STRUCT,示例 50.7 定义了一个可以用作 Fusion 容器的结构。这使得可以使用具有 boost::fusion::at() 或 boost::fusion::back() 等函数的结构。

示例 50.8。对 std::pair 的融合支持

#include <boost/fusion/adapted.hpp>
#include <boost/fusion/sequence.hpp>
#include <boost/mpl/int.hpp>
#include <utility>
#include <iostream>
using namespace boost::fusion;
int main()
{
  auto p = std::make_pair(10, 3.14);
  std::cout << at<boost::mpl::int_<0>>(p) << '\n';
  std::cout << back(p) << '\n';
}

Boost.Fusion 无需使用宏即可支持 std::pair 和 boost::tuple 等结构。您只需包含头文件 boost/fusion/adapted.hpp(参见示例 50.8)。

#include <boost/math/constants/constants.hpp>
#include <iostream>
struct animal
{
    std::string name;
    int legs;
    bool has_tail;
};
struct important_numbers
{
    const float pi = boost::math::constants::pi<float>();
    const double e = boost::math::constants::e<double>();
};
template <class T>
void debug(const T &t)
{
    // TODO: Write member variables of t to standard output.
}
int main()
{
    animal a{ "cat", 4, true };
    debug(a);
    important_numbers in;
    debug(in);
}

加载全部内容

相关教程
猜你喜欢
用户评论