For循环遍历模板参数/类型 [英] For loop over template arguments/types

查看:44
本文介绍了For循环遍历模板参数/类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想为几种可能的类的几种组合编写基准代码.如果我自己写每个组合,那将变得难以维持.因此,我正在寻找一种通过模板自动组合每种类型的方法,类似于以下伪代码:

I want to write benchmark code for several combinations of several possible classes. If I write each combination myself it becomes an unmaintainable mess. Thus I'm looking for a way to automatically combine each type via templates, something akin to the following pseudo code:

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,...是类型.

Where Sha256,… ,TwoPassKeyedHash,… are types.

我正在寻找的代码在功能上应等效于以下内容:

The code I'm looking for is supposed to be functionally equivalent to the following:

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-lk的帮助下,我已经做到了

With Peregring-lk's help I have come as far as

#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;
}

但是我还没有看到如何迭代该递归以获取嵌套循环.任何帮助将不胜感激.

but I don't yet see how I could iterate that recursion to get nested loops. Any help would be appreciated.

(代码重组和Peregring-lk的回答.)

( Code restructuring and inclusion of Peregring-lk's answer.)

推荐答案

有时候了解一下您的目标会有所帮助:

Sometimes it helps to have an idea of what you are aiming for:

  • 您需要几种参数类型
  • 对于每种参数类型,几个可能的值"

并且想要对值的每个单个组合应用某些内容(一次,每种参数类型一个).

And want to apply something on every single combination of values (one per parameter type at a time).

这看起来像是可以表达的:

This looks like it could be expressed:

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是以下内容的实例:

if runAndTime is an instance of:

struct SomeFunctor {
   template <typename H, typename W, typename L, typename A>
   void operator()(cons<H>{}, cons<W>{}, cons<L>{}, cons<A>{});
};

cons只是一种将类型作为常规参数传递的方式(更容易).

and cons is just a way to pass a type as a regular parameter (much easier).

走吧?

首先,通过某种方式(便宜地)传递类型:

First, some way to pass around types (cheaply):

template <typename T>
struct cons { using type = T; };

template <typename... T>
struct Set {};

明确的bind(里面没有魔法):

An explicit bind (with no magic inside):

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

现在,我们深入研究手头上的实际任务:

And now we delve into the real task at hand:

  • 我们需要迭代类型集
  • 在一个集合中,我们需要迭代其元素(类型也是如此)

这需要两个调度级别:

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用于对集合中的类型进行迭代.

Where combine is the outer (exposed) function, apply_set is used to iterate on the sets and apply_item is used to iterate on the types within a set.

实现很简单:

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_setapply_item中的每一个,我们都有一个递归的情况和一个基本的情况,尽管这是某种形式的共递,因为apply_item调用回apply_set.

For each of apply_set and apply_item we have a recursive case and a base case, though it's some kind of co-recursion here as apply_item calls back to 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中时都可以使用引用.

Note: it was presumed that the functor was cheap to copy, otherwise a reference can be used, both when passing and when storing it in Forwarder.

删除Set周围的cons(出现的所有位置),这是不必要的.

removed the cons around Set (everywhere it appeared), it's unnecessary.

这篇关于For循环遍历模板参数/类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆