4

I have a function with a non-type template parameter of type int, like so:

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

I would like to unit test this function for all values of N from 0 to 32. I have a function int expected(int n) that takes the same N value and returns the expected value. Effectively, I want:

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

I don't want to write out all 33 test cases by hand, and I can't easily use a runtime loop because N is compile time.

How can I get the compiler to generate the test cases for me in a simple way, without BOOST_PP_REPEAT-style tricks or code generation, in C++11?

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
BeeOnRope
  • 60,350
  • 16
  • 207
  • 386
  • Can you clarify whether you want to continue or abort if a test fails? – M.M Aug 16 '19 at 03:03
  • I am using Catch2 and the test looks like `REQUIRE(foo<0>() == expected(0))` which aborts using some Catch2-specific magic, and depending on command line flags. I didn't really want to introduce the complication of the Catch2 macro here though. Does it answer your question @M.M? – BeeOnRope Aug 16 '19 at 03:05
  • Possible duplicate of [c++ generic compile-time for loop](https://stackoverflow.com/questions/55648387/c-generic-compile-time-for-loop) – francesco Aug 16 '19 at 06:07

4 Answers4

5

You can write a recursive function template with full specialization to perform the test. e.g.

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

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

and run it like

test<32>();
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
2

In c++14 you can do something like this

#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;
}
Amos
  • 3,238
  • 4
  • 19
  • 41
1

Here's a method:

template<int N>
void f();

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

Which can be called like so:

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

Edit:

Here's version that actually checks if the tests completed successfully:

template<int N>
int f();

int expected(int n);

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

Which is used like:

g(std::make_index_sequence<33>()); // true if all tests are sucessful, false otherwise
Krystian S
  • 1,586
  • 7
  • 23
  • 1
    The `(f(), ...);` part doesn't compile under C++11 or C++14, at least for me. Does it require C++17? – BeeOnRope Aug 16 '19 at 03:56
  • To match the template parameter, you could replace `index_sequence` with `integer_sequence`. –  Aug 16 '19 at 07:54
  • Not really significant but... the OP ask for a sequence "from 0 to 32"; so the template argument for `std::make_index_sequence` should be `33`, not `31` (if I'm not wrong) – max66 Aug 16 '19 at 12:33
  • @BeeOnRope - yes: template folding is available starting from C++17. – max66 Aug 16 '19 at 12:42
0

A possible C++14 solution, that "simulate" the C++17 template-folding and interrupt the f<N> != expected(N) at first failure

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))... }; 
 }

callable as follows

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

For a C++11 solution, you need a substitute for std::make_integer_sequence/std::integer_sequence that are available only from C++14.

max66
  • 65,235
  • 10
  • 71
  • 111