【问题标题】:Repeatedly calling a function with a non-type template parameter重复调用具有非类型模板参数的函数
【发布时间】:2019-08-16 02:55:10
【问题描述】:

我有一个带有int 类型的非类型模板参数的函数,如下所示:

template <int N>
int foo() { /*...*/ }

我想针对从 0 到 32 的所有 N 值对这个函数进行单元测试。我有一个函数 int expected(int n),它采用相同的 N 值并返回预期值。实际上,我想要:

if (foo<0>() != expected(0)) { /* fail... */ }
if (foo<1>() != expected(1)) { /* fail... */ }
if (foo<2>() != expected(2)) { /* fail... */ }
// 30 more lines

我不想手动写出所有 33 个测试用例,而且我不能轻易使用运行时循环,因为 N 是编译时。

如何让编译器在 C++11 中以简单的方式为我生成测试用例,无需 BOOST_PP_REPEAT 风格的技巧或代码生成?

【问题讨论】:

  • 您能否说明如果测试失败是要继续还是中止?
  • 我正在使用 Catch2,测试看起来像 REQUIRE(foo&lt;0&gt;() == expected(0)),它使用一些 Catch2 特定的魔法中止,具体取决于命令行标志。不过,我真的不想在这里介绍 Catch2 宏的复杂性。它是否回答了您的问题@M.M?

标签: c++ c++11 templates


【解决方案1】:

您可以使用full specialization 编写递归函数模板来执行测试。例如

template <int N>
void test() {
    test<N-1>();
    if (foo<N>() != expected(N)) { /* fail... */ }
}

template <>
void test<-1>() {
    // do nothing
}

然后运行它

test<32>();

【讨论】:

    【解决方案2】:

    在 c++14 中你可以做这样的事情

    #include <type_traits>
    
    template <int beg, int end> struct static_for {
        template <typename Fn> void operator()(Fn const& fn) const {
            if (beg < end) {
                fn(std::integral_constant<int, beg>());
                static_for<beg + 1, end>()(fn);
            }
        }
    };
    
    template <int n> struct static_for<n, n> {
        template <typename Fn> void operator()(Fn const& fn) const {}
    };
    
    template <int N> int foo() { /*...*/
        return N;
    }
    
    int main() {
        static_for<0, 32>()([&](auto i) {
            if (foo<i>() != i) { /* fail... */
            }
        });
        return 0;
    }
    

    【讨论】:

      【解决方案3】:

      这是一个方法:

      template<int N>
      void f();
      
      template<int... N>
      void g(std::index_sequence<N...>)
      {
        (f<N>(), ...);
      }
      

      可以这样称呼:

      g(std::make_index_sequence<33>());
      

      编辑:

      这是实际检查测试是否成功完成的版本:

      template<int N>
      int f();
      
      int expected(int n);
      
      template<int... N>
      bool g(std::index_sequence<N...>)
      {
        return ((f<N>() == expected(N)) && ...);
      }
      

      使用如下:

      g(std::make_index_sequence<33>()); // true if all tests are sucessful, false otherwise
      

      【讨论】:

      • (f&lt;N&gt;(), ...); 部分不能在 C++11 或 C++14 下编译,至少对我而言。它需要 C++17 吗?
      • 为了匹配模板参数,您可以将index_sequence替换为integer_sequence&lt;int,...&gt;
      • 不是很重要,但是...... OP 要求一个序列“从 0 到 32”;所以std::make_index_sequence 的模板参数应该是33,而不是31(如果我没记错的话)
      • @BeeOnRope - 是的:模板折叠从 C++17 开始可用。
      【解决方案4】:

      一种可能的 C++14 解决方案,“模拟”C++17 模板折叠并在第一次失败时中断 f&lt;N&gt; != expected(N)

      template <int N>
      void f ();
      
      template <int ... Is>
      void g (std::integer_sequence<int, Is...>)
       {
         using unused = int[];
      
         bool ret { false };
      
         (void)unused { 0, (ret ? 0 : (ret = (f<Is>() != expected(Is)), 0))... }; 
       }
      

      如下调用

      g(std::make_integer_sequence<33>());
      

      对于 C++11 解决方案,您需要替代 std::make_integer_sequence/std::integer_sequence,这仅适用于 C++14。

      【讨论】:

        猜你喜欢
        • 2018-05-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-01-25
        相关资源
        最近更新 更多