For循环遍历模板参数/类型
For loop over template arguments/types
我想为几种可能的类的几种组合编写基准代码。如果我自己编写每个组合,它就会变得不可维护。因此,我正在寻找一种通过模板自动组合每种类型的方法,类似于以下伪代码:
for (typename HashFuction : Sha256, Sha512, Sa512_256, Sha3_256, Sha3_512) {
for (typename KeyingWrapper : TwoPassKeyedHash, OnePassKeyedHash, PlainHash) {
for (typename InstantiatedGetLeaf: GetLeaf<8>, GetLeaf<1024>) {
for (typename algorithm : algA, algB, algC) {
runAndTime<HashFunction,KeyingWrapper,
InstantiatedGetLeaf,algorithm>(someArgs);
}
}
}
}
其中Sha256
,…,TwoPassKeyedHash
,…为类型。
我正在寻找的代码应该在功能上等同于以下内容:
runAndTime<Sha256,TwoPassKeyedHash,GetLeaf<8>,algA>(someArgs);
runAndTime<Sha256,TwoPassKeyedHash,GetLeaf<8>,algB>(someArgs);
runAndTime<Sha256,TwoPassKeyedHash,GetLeaf<8>,algC>(someArgs);
runAndTime<Sha256,TwoPassKeyedHash,GetLeaf<1024>,algA>(someArgs);
runAndTime<Sha256,TwoPassKeyedHash,GetLeaf<1024>,algB>(someArgs);
runAndTime<Sha256,TwoPassKeyedHash,GetLeaf<1024>,algC>(someArgs);
runAndTime<Sha256,OnePassKeyedHash,GetLeaf<8>,algA>(someArgs);
runAndTime<Sha256,OnePassKeyedHash,GetLeaf<8>,algB>(someArgs);
runAndTime<Sha256,OnePassKeyedHash,GetLeaf<8>,algC>(someArgs);
// And 99 further lines…
在peregring - like的帮助下,我来到了这里
#include <iostream>
template<typename Aux_type>
void test_helper()
{}
template<typename Aux_type, typename Head, typename... Tail>
void test_helper() {
std::cout << Head::i;
test_helper<Aux_type, Tail...>();
}
template<typename... Args>
void test()
{
test_helper<void, Args...>();
}
struct A{
static const int i=1;
};
struct B{
static const int i=2;
};
int main() {
test<A, B>();
return 0;
}
,但我还不知道如何迭代递归来获得嵌套循环。如有任何帮助,不胜感激。
(编辑:代码重组和包含Peregring-lk的答案)
有时对你的目标有一个概念是有帮助的:
- 你需要几个参数类型
- 和每个参数类型,几个可能的"值"
并且想要对每个值的组合(每次一个参数类型)应用一些东西。
看起来可以这样表示:
combine<
Set<Sha256, Sha512, Sa512_256, Sha3_256, Sha3_512>,
Set<TwoPassKeyedHash, OnePassKeyedHash, PlainHash>,
Set<GetLeaf<8>, GetLeaf<1024>>,
Set<algA, algB, algC>
>(runAndTime);
如果runAndTime
是:
struct SomeFunctor {
template <typename H, typename W, typename L, typename A>
void operator()(cons<H>{}, cons<W>{}, cons<L>{}, cons<A>{});
};
和cons
只是将类型作为常规参数传递的一种方法(更容易)。
Let's go ?
首先,传递类型(便宜)的方法:
template <typename T>
struct cons { using type = T; };
template <typename... T>
struct Set {};
一个显式的bind
(里面没有魔法):
template <typename F, typename E>
struct Forwarder {
Forwarder(F f): inner(f) {}
template <typename... Args>
void operator()(Args... args) { inner(cons<E>{}, args...); }
F inner;
}; // struct Forwarder
现在我们深入研究手头的真正任务:
- 我们需要迭代类型集合
- 在一个集合中,我们需要迭代它的元素(类型也是)
调用两个级别的调度:
template <typename FirstSet, typename... Sets, typename F>
void combine(F func);
template <typename Head, typename... Tail, typename... Sets, typename F>
void apply_set(F func, Set<Head, Tail...>, Sets... others);
template <typename... Sets, typename F>
void apply_set(F func, Set<>, Sets... others);
template <typename E, typename NextSet, typename... Sets, typename F>
void apply_item(F func, cons<E>, NextSet, Sets...);
template <typename E, typename F>
void apply_item(F func, cons<E> e);
其中combine
是外层(公开的)函数,apply_set
用于遍历集合,apply_item
用于遍历集合内的类型。
实现很简单:
template <typename Head, typename... Tail, typename... Sets, typename F>
void apply_set(F func, Set<Head, Tail...>, Sets... others) {
apply_item(func, cons<Head>{}, others...);
apply_set(func, Set<Tail...>{}, others...);
} // apply_set
template <typename... Sets, typename F>
void apply_set(F, Set<>, Sets...) {}
template <typename E, typename NextSet, typename... Sets, typename F>
void apply_item(F func, cons<E>, NextSet ns, Sets... tail) {
Forwarder<F, E> forwarder(func);
apply_set(forwarder, ns, tail...);
}
template <typename E, typename F>
void apply_item(F func, cons<E> e) {
func(e);
} // apply_item
template <typename FirstSet, typename... Sets, typename F>
void combine(F func) {
apply_set(func, FirstSet{}, Sets{}...);
} // combine
对于apply_set
和apply_item
,我们有一个递归情况和一个基本情况,尽管在apply_item
回调apply_set
时这里有某种共递归。
和一个简单的例子:
struct Dummy0 {}; struct Dummy1 {}; struct Dummy2 {};
struct Hello0 {}; struct Hello1 {};
struct Tested {
Tested(int i): value(i) {}
void operator()(cons<Dummy0>, cons<Hello0>) { std::cout << "Hello0 Dummy0!n"; }
void operator()(cons<Dummy0>, cons<Hello1>) { std::cout << "Hello1 Dummy0!n"; }
void operator()(cons<Dummy1>, cons<Hello0>) { std::cout << "Hello0 Dummy1!n"; }
void operator()(cons<Dummy1>, cons<Hello1>) { std::cout << "Hello1 Dummy1!n"; }
void operator()(cons<Dummy2>, cons<Hello0>) { std::cout << "Hello0 Dummy2!n"; }
void operator()(cons<Dummy2>, cons<Hello1>) { std::cout << "Hello1 Dummy2!n"; }
int value;
};
int main() {
Tested tested(42);
combine<Set<Dummy0, Dummy1, Dummy2>, Set<Hello0, Hello1>>(tested);
}
您可以在Coliru打印上现场观看:
Hello0 Dummy0!
Hello1 Dummy0!
Hello0 Dummy1!
Hello1 Dummy1!
Hello0 Dummy2!
Hello1 Dummy2!
享受:)
注意:假定该函子的复制成本很低,否则可以使用引用,无论是在传递还是在Forwarder
中存储它。
编辑:删除Set
周围的cons
(它出现的任何地方),这是不必要的
Functions不允许部分专门化,除非专门化已经完成。每个新的不同的函数签名声明一个新的重载,除非它们的签名完全相同。
试试下面的代码:
#include <iostream>
template<typename Aux_type>
void test_helper()
{}
template<typename Aux_type, typename Head, typename... Tail>
void test_helper() {
std::cout << Head::i;
test_helper<Aux_type, Tail...>();
}
template<typename... Args>
void test()
{
test_helper<void, Args...>();
}
struct A{
static const int i=1;
};
struct B{
static const int i=2;
};
int main() {
test<A, B>();
return 0;
}
,它确实编译(并打印12
)。
无论如何,我不理解你的伪代码示例。
我认为c++不是合适的工具,当它涉及到方便和灵活的代码生成…只需用您选择的脚本语言(如python)编写一个简单的实用程序:
generate_test_code.py:
#!/usr/bin/python
for HashFuction in {"Sha256", "Sha512", "Sa512_256", "Sha3_256", "Sha3_512"}:
for KeyingWrapper in {"TwoPassKeyedHash", "OnePassKeyedHash", "PlainHash"}:
for InstantiatedGetLeaf in {"GetLeaf<8>", "GetLeaf<1024>"}:
for Algorithm in {"algA", "algB", "algC"}:
print("runAndTime<{},{},{},{}>(someArgs);".format(HashFuction,KeyingWrapper,InstantiatedGetLeaf,Algorithm))
…然后在你的Makefile中:
generated_test_code.cpp: generate_test_code.py
python generate_test_code.py > generated_test_code.cpp
…在你的c++代码中,只要#include "generated_test_code.cpp"在你想要的地方
- 扩展C++生成的代码的模板参数类型名称
- 如何在 c++ 中定义接受不同参数类型的函数向量?
- 在 C++ 中运行时调用模板时,是否可以切换模板的参数类型?
- 将函数参数类型声明为 auto
- 将函数的参数 - 签名从使用 'std::function<T>' 转换为模板参数类型
- 在 C++17 中调用具有不同参数类型的构造函数
- 具有先前参数类型匹配的参数包
- 我想知道为什么"std::unique_ptr<int> foo(新 int)"是合法的,因为"std::<int>unique_ptr"要求输入参数类型应该是"int"?
- 将可变参数类型列表的扩展打包为复杂类型的初始值设定项列表 - 合法吗?
- MSVC 错误:4 个重载中的任何一个都无法转换所有参数类型
- 使用constexpr + auto作为返回和参数类型的奇怪类型推导
- 如何从第一个参数推断第二个参数类型?
- C++模板函数中,指定回调函子/lambda 的参数类型,同时仍允许内联?
- 如何用不同的参数类型和数字回调函数
- C++stoi:这两个重载都无法转换所有参数类型
- 为什么std::{container}::template不能推导其参数类型
- 为模板参数类型中的新对象分配内存
- 为指向成员模板参数的指针推导额外模板参数类型的紧凑方式
- 使用std::conditional中的模板来确定函数参数类型
- C++中的短参数类型