与外部库集成

优质
小牛编辑
126浏览
2023-12-01

Hana提供了与一些现有库的开箱即用集成。 具体来说,这意味着您可以使用Hana算法中的这些库中的一些容器,只需包含适当的标头,使之成为Hana和外部组件之间的桥梁。 这可以非常有用的移植现有代码从例如。 Fusion / MPLHana

// In the old code, this used to receive a Fusion sequence.
// Now, it can be either a Hana sequence or a Fusion sequence.
template <typename Sequence>
void f(Sequence const& seq) {
    hana::for_each(seq, [](auto const& element) {
        std::cout << element << std::endl;
    });
}

注意: 仅提供将其他库的数据类型用于Hana的适配器; 不提供适配器(将Hana容器用于其他库)。

但是,使用外部适配器有几个陷阱。 例如,使用Hana一段时间后,您可能习惯于使用正常的比较运算符比较Hana元组,或者使用Hanaintegral_constants进行算术。 但不保证这些操作符也被定义为外部适配器(并且通常它们不会)。 因此,你必须坚持Hana提供的实现这些运算符的函数。 例如:

auto r = std::ratio<3, 4>{} + std::ratio<4, 5>{}; // error, the operator is not defined!

相反,您应该使用以下:

#include <boost/hana/ext/std/ratio.hpp>
#include <boost/hana/plus.hpp>
#include <ratio>
namespace hana = boost::hana;
auto r = hana::plus(std::ratio<3, 4>{}, std::ratio<4, 5>{});

但有时候,情况会更糟。 一些外部组件定义运算符,但它们不一定具有与来自Hana的语义相同的语义。 例如,比较两个不同长度的std::tuple会在使用operator ==时产生错误:

std::make_tuple(1, 2, 3) == std::make_tuple(1, 2); // compiler error

另一方面,比较不同长度的Hana元组将只返回一个错误的IntegralConstant

hana::make_tuple(1, 2, 3) == hana::make_tuple(1, 2); // hana::false_c

这是因为std::tuple定义了自己的操作符,它们的语义与Hana的操作符不同。 解决方案是坚持使用Hana的命名函数,而不是使用运算符,当你知道你将需要使用其他库:

hana::equal(std::make_tuple(1, 2, 3), std::make_tuple(1, 2)); // hana::false_c

当使用外部适配器时,还应小心不要忘记包括正确的头文件。 例如,假设我想使用一个Boost.MPL向量与Hana。 我包括适当的头文件:

#include <boost/hana/ext/boost/mpl/vector.hpp> // bridge header
using Vector = mpl::vector<int, char, float>;
static_assert(hana::front(Vector{}) == hana::type_c<int>, "");

注意: 这些头文件的确切布局在关于头文件组织的部分中有说明。

然而,现在,假设我使用mpl::size查询向量的大小,然后将其与某个值进行比较。 我也可以使用hana::length,一切都会很好,但为了例子的缘故与我一起:

using Size = mpl::size<Vector>::type;
static_assert(hana::equal(Size{}, hana::int_c<3>), ""); // breaks!

这个断点的原因是mpl::size返回一个MPL IntegralConstantHana无法知道这些,除非你包括正确的头文件。 因此,您应该执行以下操作:

#include <boost/hana/ext/boost/mpl/integral_c.hpp>
using Size = mpl::size<Vector>::type;
static_assert(hana::equal(Size{}, hana::int_c<3>), "");

在使用外部库时,你必须小心对待对你所操作的对象。 最后一个陷阱是关于外部库中的实现限制。 许多较旧的库对于可以使用它们创建的异构容器的最大大小是有限制的。 例如,不能创建包含FUSION_MAX_LIST_SIZE个以上元素的Fusion列表。 显然,这些限制是由Hana继承的,例如,试图计算包含5个元素的fusion::list的排列(结果列表将包含120个元素)将以可怕的方式失败:

auto list = fusion::make_list(1, 2, 3, 4, 5);
auto oh_jeez = hana::permutations(list); // probably won't make it

除了在本节中解释的陷阱,使用外部适配器应该像使用正常的Hana容器一样简单。 当然,只要有可能,你应该试着坚持Hana的容器,因为他们通常更友好的工作,并经常更优化。