C 组合模板扩展
C++ combinatorial template expansion
我想从库中请求一系列数据,这要求我指定两种类型:
class a;
class b;
class c;
template<typename A, typename B> void get() {....}
void get_all() {
get<a,a>()
get<a,b>()
get<a,c>()
get<b,a>()
get<b,b>()
// etc...
}
我想称呼get&lt; a,b&gt;一组类(大约10个不同的变体(的每种组合的功能 - 含义大约100个不同的调用。我宁愿不用手编码所有这些get((呼叫。
是否有某种方法可以自动生成这些调用?我想我可以使用预处理器宏,但是我很好奇是否有通过使用模板代码来产生这样的组合列表。
编辑:次要复杂性:实际上,这全都在成员函数中。
template<typename A, typename B>
void Getter::get()
{
auto assn = fMemberObject->RetrieveAllDataOfType<association<A,B>();
.. do stuff with assn ...
}
void Getter::get_all() {
get<a,a>();
}
因此,代码需要与模板一起处理传递this
。另外,如果可能的话,C 11可能是我想推动语言的范围。
编辑2:原来我想避免重复的情况<a,a>
和<b,b>
...
在解决模板元编程问题时,它有助于考虑如何在正常编程中执行此操作。在这种情况下,我们想要类似的东西:
for a in type_list:
for b in type_list:
foo(a,b)
因此,让我们将其直接翻译成C 。我们需要一种表示类型和打字员的方法:
template <class T> struct tag { };
template <class... Ts> struct type_list { };
我们需要一种在类型列表上迭代并为每种类型做某事的方法。我们将其称为功能调用。在C 17(在C 14及更低中,您可以使用燕子/扩展器技巧来调用每个tag<Ts>
上的f
(:
template <class... Ts, class F>
void for_each(type_list<Ts...>, F f) {
(f(tag<Ts>{}), ...);
}
这实际上是……实际上。我们需要一个实际调用的函数,打字机和代码:
template <class X, class Y>
void foo(tag<X>, tag<Y> ) {
std::cout << __PRETTY_FUNCTION__ << 'n'; // or something more meaningful
}
int main() {
type_list<a, b, c> types;
for_each(types, [=](auto x){
for_each(types, [=](auto y){
foo(x, y);
});});
}
演示
C 11版本,演示通用lambdas的力量。
基于想要类型不不同的更新,这也很容易更新。我们可以将平等运算符添加到tag
:
template <class T, class U>
constexpr std::integral_constant<bool, std::is_same<T,U>::value>
operator==(tag<T>, tag<U> ) { return {}; }
template <class T, class U>
constexpr std::integral_constant<bool, !std::is_same<T,U>::value>
operator!=(tag<T>, tag<U> ) { return {}; }
请注意,我们没有返回bool
,我们正在返回一种编码类型系统中结果的类型。然后,我们可以自然使用它:
for_each(types, [=](auto x){
for_each(types, [=](auto y){
if constexpr (x != y) {
foo(x, y);
}
});});
foo(x,y)
只有在两种标签类型不同的情况下才会实例化。
更新以处理不使用相同类型参数调用get()
的新要求,并且get()
是类成员函数。我在这里打电话给manager
类。
您可以(AB(使用C 11变异模板来实现这一目标。可能有一种更简洁的方法可以做到这一点,但是它有效,并且(希望(可以理解:
struct manager
{
// Sample implementation of get(), showing the names of the types.
template <typename A, typename B>
void get() {
std::cout << "A=" << typeid(A).name() << " B=" << typeid(B).name() << 'n';
}
};
// Implementation, uses variadic templates with recursion.
namespace detail
{
// Helper to delimit template parameter packs.
template <typename...> struct pack;
// Terminating case, <=1 type argument(s) means we are done.
template <typename...>
struct call_get_all_beta
{
static void call(manager &) { }
};
// Invoke get<First, Second>() and recurse with <First, Tail...>.
template <typename First, typename Second, typename... Tail>
struct call_get_all_beta<First, Second, Tail...>
{
static void call(manager &m) {
m.get<First, Second>();
call_get_all_beta<First, Tail...>::call(m);
}
};
// Specialization to handle skipping over types that are the same.
template <typename First, typename... Tail>
struct call_get_all_beta<First, First, Tail...>
{
static void call(manager &m) {
call_get_all_beta<First, Tail...>::call(m);
}
};
template <typename...>
struct call_get_all_alpha;
// Terminating case, first pack is empty.
template <typename... B>
struct call_get_all_alpha<pack<>, pack<B...>>
{
static void call(manager &) { }
};
// Pass <FirstA, B...> on to call_get_all_beta, recurse with
// <pack<TailA...>, pack<B...>>.
template <typename FirstA, typename... TailA, typename... B>
struct call_get_all_alpha<pack<FirstA, TailA...>, pack<B...>>
{
static void call(manager &m) {
call_get_all_beta<FirstA, B...>::call(m);
call_get_all_alpha<pack<TailA...>, pack<B...>>::call(m);
}
};
}
// Helper to call the implementation detail.
template <typename... Types>
void get_all_permutations(manager &m)
{
detail::call_get_all_alpha<detail::pack<Types...>, detail::pack<Types...>>::call(m);
}
然后我们只做get_all_permutations<a, b, c>();
。(演示(
要解释其工作原理,detail::call_get_all_alpha
采用两个pack<...>
模板参数,两者最初都包含整个类型集。第二个保持不变,但每种类型都会在每次递归时都从第一个包装上剥离。第一种类型和完整的类型集(通过第二个包(传递给detail::call_get_all_beta
,它使用相同的"脱皮"递归技术将<A, B, C, D>
变成呼叫get<A, B>()
,get<A, C>()
和get<A, D>()
。
因此,在较高级别上,detail::call_get_all_alpha
负责迭代 first 模板参数到 get()
,而 detail::call_get_all_beta
负责迭代 second 模板参数。 template参数。
如果您可以使用Boost,则可以避免大量的基本打字机和应用程序样板,从而使代码更容易阅读。无需在这里重新发明轮子。
此示例使用boost.mpl,该示例具有广泛的编译器支持,但我敢肯定,boost.hana中的等效解决方案更漂亮。
首先,生成对的功能(根据此答案进行了改编(:
#include <utility>
#include <boost/mpl/fold.hpp>
#include <boost/mpl/lambda.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/push_back.hpp>
// For each element in TList, append to Accumulator
// the type pair<PairFirst, element>.
template <
typename TList, typename PairFirst, typename Accumulator = boost::mpl::vector<>>
struct generate_pairs_fixed_element :
boost::mpl::fold<
TList,
Accumulator,
boost::mpl::push_back<boost::mpl::_1, std::pair<PairFirst, boost::mpl::_2>>>
{};
// For each element in TList, concatenate to Accumulator
// the result of generate_pairs_fixed_element<TList, element>.
template <typename TList, typename Accumulator = boost::mpl::vector<>>
struct generate_pairs_combinations :
boost::mpl::fold<
TList,
Accumulator,
boost::mpl::lambda<
generate_pairs_fixed_element<TList, boost::mpl::_2, boost::mpl::_1>
>>
{};
现在只需将您的类型收集到列表中并生成组合:
class a {};
class b {};
class c {};
class d {};
typedef boost::mpl::vector<a, b, c, d> all_classes_t;
typedef generate_pairs_combinations<all_classes_t>::type all_classes_pairs_t;
并与他们一起做任何您想做的事:
#include <iostream>
#include <boost/mpl/for_each.hpp>
#include <boost/type.hpp>
template <typename A, typename B>
void get() {
std::cout << "Getting " << typeid(A).name()
<< " and " << typeid(B).name() << std::endl;
}
struct pair_visitor {
template <typename T, typename U>
void operator()(boost::type<std::pair<T, U>>) const {
get<T, U>();
}
};
int main() {
boost::mpl::for_each<all_classes_pairs_t, boost::type<boost::mpl::_>>(pair_visitor());
}
您可以使用以下内容:
class a;
class b;
class c;
template<typename A, typename B> void get() { /*....*/ }
namespace detail {
template <typename> struct tag{};
template <typename Tuple, std::size_t...Is>
void get_all_pairs(tag<Tuple>, std::index_sequence<Is...>)
{
constexpr auto size = std::tuple_size<Tuple>::value;
#if 1 // Folding expression with C++17
(get<std::tuple_element_t<Is / size, Tuple>,
std::tuple_element_t<Is % size, Tuple>>(), ...);
#else
const dummy[] = {0, (get<std::tuple_element_t<Is / size, Tuple>,
std::tuple_element_t<Is % size, Tuple>>(),
void(), 0)...};
static_cast<void>(dummy); // Avoid warning for unused variable.
#endif
}
template <typename ... Ts>
void get_all_pairs() {
get_all_pairs(tag<std::tuple<Ts...>>{},
std::make_index_sequence<sizeof...(Ts) * sizeof...(Ts)>{});
}
}
void get_all() { detail::get_all_pairs<a, b, c>(); }
- 如何在OMNET++中指定与命令行参数组合的输出文件名
- 可组合的lambda/std::函数与std::可选
- 是否可以通过C++扩展强制多个python进程共享同一内存
- static_assert在宏中,但也可以扩展到可以用作函数参数的东西
- 如何将两个不同矢量的同一位置的两个元素组合在一起
- 如何将这个C++哈希表转换为动态扩展和收缩,而不是使用硬设置的最大值
- 扩展光电二极管探测器以支持多个传感器
- 混合组合和继承的C++问题
- 我需要将多个函数组合为一个函数
- 构建可组合有向图(扫描仪生成器的汤普森构造算法)
- 通过组合不同的类型来创建唯一的id
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 模板元编程:如何将参数包组合成新的参数包
- C++中的VLA,扩展名为std=C++11
- OpenGL 和 GLM 矩阵无法正确扩展,总是按比例缩小
- 检查向量是否具有所有可能的字符组合
- 基于范围的 for 循环:迭代使用一个元素扩展的向量
- C 组合模板扩展
- 如何扩展/组合包含纯虚拟函数的接口类
- 如何在boost:: Python扩展模块中正确组合c++和Python代码