对类型列表进行递归迭代并连接到结果类型列表中
Recursive iteration over type lists and concatenation into a result type list
考虑一个具有各种 classes/structs 的场景,其中一些具有复杂的数据成员,这些成员本身可以包含更多。为了设置/初始化,在实例化之前需要所有依赖项的列表。
因为类型在实例化之前是已知的,我的方法是定义一个类型列表,其中每个 class/struct 包含 involved/relevant 类型,如下所示:
template<typename...> struct type_list {};
struct a {
using dependencies = type_list<>;
};
struct b {
using dependencies = type_list<>;
};
struct c {
using dependencies = type_list<b>;
b b_;
};
struct d {
using dependencies = type_list<a>;
a a_;
};
struct e {
using dependencies = type_list<c, a>;
c c_;
a a_;
x x_; // excluded
};
struct f {
using dependencies = type_list<a,b>;
a a_;
b b_;
y y_; // excluded
};
比如我想预初始化d, e, f
.
接下来的步骤是:
- 遍历
d,e,f
的 dependencies
并将每个项目连接到结果列表
- 递归遍历每个
dependencies[n]::dependencies
的每个元素并将每个项目连接到结果列表,并对每个类型执行相同操作直到类型列表为空
结果可能包含重复项。这些在后面的步骤中被减少和排序。我打算使用 constexpr 散列映射使用 __FUNCSIG__ / __PRETTY_FUNCTION__
的散列(不是其中的一部分)来执行此操作。
如何使用 C++20 元编程实现这一点(迭代、访问类型列表元素、连接到结果列表)?
我只看元编程部分。一如既往,解决方案是使用 Boost.Mp11。在这种情况下,它是更复杂的算法之一:mp_iterate
.
这将一个函数应用于一个值直到失败 - 这就是我们实现递归的方式。我们需要几个步骤。
首先,元函数获取单个类型的依赖关系
template <typename T> using dependencies_of = typename T::dependencies;
然后,我们需要一种方法来获取类型列表的所有依赖项。重要的是,这需要在某些时候失败(对于 mp_iterate
的停止条件),因此我们在空列表上强制失败:
template <typename L>
using list_dependencies_of = std::enable_if_t<
not mp_empty<L>::value,
mp_flatten<mp_transform<dependencies_of, L>>>;
然后我们可以使用这些片段进行迭代:
template <typename L>
using recursive_dependencies_of = mp_unique<mp_apply<mp_append,
mp_iterate<
L,
mp_identity_t,
list_dependencies_of
>>>;
mp_append
连接了 mp_iterate
给你的列表列表,然后 mp_unique
在上面,因为你不想重复。
这需要一个列表,所以可以像 recursive_dependencies_of<mp_list<a>>
(就是 mp_list<a>
)或 recursive_dependencies_of<mp_list<d, e, f>>
(就是 mp_list<d, e, f, a, c, b>
)那样使用。
Demo.
考虑一个具有各种 classes/structs 的场景,其中一些具有复杂的数据成员,这些成员本身可以包含更多。为了设置/初始化,在实例化之前需要所有依赖项的列表。
因为类型在实例化之前是已知的,我的方法是定义一个类型列表,其中每个 class/struct 包含 involved/relevant 类型,如下所示:
template<typename...> struct type_list {};
struct a {
using dependencies = type_list<>;
};
struct b {
using dependencies = type_list<>;
};
struct c {
using dependencies = type_list<b>;
b b_;
};
struct d {
using dependencies = type_list<a>;
a a_;
};
struct e {
using dependencies = type_list<c, a>;
c c_;
a a_;
x x_; // excluded
};
struct f {
using dependencies = type_list<a,b>;
a a_;
b b_;
y y_; // excluded
};
比如我想预初始化d, e, f
.
接下来的步骤是:
- 遍历
d,e,f
的dependencies
并将每个项目连接到结果列表 - 递归遍历每个
dependencies[n]::dependencies
的每个元素并将每个项目连接到结果列表,并对每个类型执行相同操作直到类型列表为空
结果可能包含重复项。这些在后面的步骤中被减少和排序。我打算使用 constexpr 散列映射使用 __FUNCSIG__ / __PRETTY_FUNCTION__
的散列(不是其中的一部分)来执行此操作。
如何使用 C++20 元编程实现这一点(迭代、访问类型列表元素、连接到结果列表)?
我只看元编程部分。一如既往,解决方案是使用 Boost.Mp11。在这种情况下,它是更复杂的算法之一:mp_iterate
.
这将一个函数应用于一个值直到失败 - 这就是我们实现递归的方式。我们需要几个步骤。
首先,元函数获取单个类型的依赖关系
template <typename T> using dependencies_of = typename T::dependencies;
然后,我们需要一种方法来获取类型列表的所有依赖项。重要的是,这需要在某些时候失败(对于 mp_iterate
的停止条件),因此我们在空列表上强制失败:
template <typename L>
using list_dependencies_of = std::enable_if_t<
not mp_empty<L>::value,
mp_flatten<mp_transform<dependencies_of, L>>>;
然后我们可以使用这些片段进行迭代:
template <typename L>
using recursive_dependencies_of = mp_unique<mp_apply<mp_append,
mp_iterate<
L,
mp_identity_t,
list_dependencies_of
>>>;
mp_append
连接了 mp_iterate
给你的列表列表,然后 mp_unique
在上面,因为你不想重复。
这需要一个列表,所以可以像 recursive_dependencies_of<mp_list<a>>
(就是 mp_list<a>
)或 recursive_dependencies_of<mp_list<d, e, f>>
(就是 mp_list<d, e, f, a, c, b>
)那样使用。
Demo.