4

When writing unit tests I often want to invoke a function with a combination of parameters. For example, I have a function which is declared as

void tester_func(int p1, double p2, std::string const& p3);

and some selected parameters

std::vector<int> vec_p1 = { 1, 2, 666 };
std::vector<double> vec_p2 = { 3.14159, 0.0001 };
std::vector<std::string> vec_p3 = { "Method_Smart", "Method_Silly" };

What I currently do is simply

for(auto const& p1 : vec_p1)
    for(auto const& p2 : vec_p2)
        for(auto const& p3 : vec_p3)
            tester_func(p1, p2, p3);

However, Sean Parent suggests to avoid explicit loops and use std:: algorithms instead. How could one follow this advice in the above mentioned case? Any idioms? What is the cleanest way to write a variadic template that does this? What is the best way without C++11 features?

7
  • The answer may depend on which unit testing framework you're using. Commented Jan 17, 2014 at 13:14
  • Boost UnitTest. The tester_func automatically logs all error. It is only important that the function is invoked for all parameter combinations. Commented Jan 17, 2014 at 13:19
  • In the video you have linked to, he explicitly states that sometimes( saves on typing) using a range for loop is ok as long as the loop body is a single statement/ assignment. I think what you are doing here is alright. Commented Jan 17, 2014 at 14:06
  • 2
    It seems the C++ standard library is missing an algorithm that generates/iterates over the cartesian product of multiple containers. An implementation of such an algorithm can be found here at stackoverlow, e.g. stackoverflow.com/a/13841673/2128694 Commented Jan 17, 2014 at 14:11
  • 1
    Check out the product template from the AWESOME cppitertools package: github.com/ryanhaining/cppitertools Commented Jan 17, 2014 at 18:41

1 Answer 1

1

A reference to the very good solution is given in comments by @Oberon.

But I think there are many different solutions to this problem. Here is my solution:

#include <tuple>
#include <type_traits>

template <class TestFunction, class... Containers, class... Types>
typename std::enable_if<sizeof...(Containers) == sizeof...(Types)>::type
TestNextLevel
    (
        TestFunction testFunction,
        const std::tuple<Containers...>& containersTuple,
        const Types&... parameters
    )
{
    testFunction(parameters...);
}

template <class TestFunction, class... Containers, class... Types>
typename std::enable_if<(sizeof...(Containers) > sizeof...(Types))>::type
TestNextLevel
    (
        TestFunction testFunction,
        const std::tuple<Containers...>& containersTuple,
        const Types&... parameters
    )
{
    for (const auto& element : std::get<sizeof...(Types)>(containersTuple))
    {
        TestNextLevel(testFunction, containersTuple, parameters..., element);
    }
}

template <class TestFunction, class... Containers>
void TestAllCases
    (
        TestFunction testFunction,
        const Containers&... containers
    )
{
    TestNextLevel
        (
            testFunction,
            std::tuple<const Containers&...>(containers...)
        );
}

Example of using:

TestAllCases(tester_func, vec_p1, vec_p2, vec_p3);
Sign up to request clarification or add additional context in comments.

1 Comment

<sarcasm> Clearly, this is easier to understand than the original code </sarcasm> :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.