用于循环模板参数/类型

Per*_*ids 5 c++ templates metaprogramming

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

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);
         }
       }
    }
 }
Run Code Online (Sandbox Code Playgroud)

哪里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…
Run Code Online (Sandbox Code Playgroud)

在Peregring-lk的帮助下,我已经走到了尽头

#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;
}
Run Code Online (Sandbox Code Playgroud)

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

(编辑:代码重组和包含Peregring-lk的答案.)

Mat*_* M. 7

有时,了解您的目标是有帮助的:

  • 你需要几种参数类型
  • 并为每个参数类型,几个可能的"值"

并希望在每个值的组合上应用一些东西(每次一个参数类型一个).

这似乎可以表达:

combine<
    Set<Sha256, Sha512, Sa512_256, Sha3_256, Sha3_512>,
    Set<TwoPassKeyedHash, OnePassKeyedHash, PlainHash>,
    Set<GetLeaf<8>, GetLeaf<1024>>,
    Set<algA, algB, algC>
>(runAndTime);
Run Code Online (Sandbox Code Playgroud)

if runAndTime是以下的实例:

struct SomeFunctor {
   template <typename H, typename W, typename L, typename A>
   void operator()(cons<H>{}, cons<W>{}, cons<L>{}, cons<A>{});
};
Run Code Online (Sandbox Code Playgroud)

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

我们走吧 ?


首先,一些传递类型的方法(便宜):

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

template <typename... T>
struct Set {};
Run Code Online (Sandbox Code Playgroud)

一个明确的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
Run Code Online (Sandbox Code Playgroud)

现在我们深入研究手头的真正任务:

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

这需要两个级别的派遣:

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);
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

对于每一个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);
}
Run Code Online (Sandbox Code Playgroud)

可以在Coliru现场见证打印:

Hello0 Dummy0!
Hello1 Dummy0!
Hello0 Dummy1!
Hello1 Dummy1!
Hello0 Dummy2!
Hello1 Dummy2!
Run Code Online (Sandbox Code Playgroud)

请享用 :)

注意:假设仿函数复制起来很便宜,否则可以使用引用,无论是在传递还是存储时Forwarder.

编辑:删除cons周围Set(它出现的任何地方),这是不必要的.

  • @ Peregring-lk:是的,`cons`只是一种传递类型而不实际构建对象的方法; 因为所述对象可能没有默认构造函数,并且因为不清楚是否可以传递引用(或者基准调用是否每次都创建一个新引用).顺便说一句,我对解决方案进行了一些手术:我把所有`cons <Set <... >>`转换成了'Set <...>`因为`cons`在那里是多余的.我也更新了Coliru上的实例,它产生了相同的结果. (2认同)