1

I am using google test for testing my c++ lib that does pixel conversions and other things.

Now consider I have these models participating in the test.

using Models = testing::Types<ARGBNormalized, RGBNormalized, HSLNormalized,
                              HSVNormalized, YUVNormalized, YCCNormalized>;

When I need to test conversion I need two distinct models from these types.

So I create a struct like this.

template<typename Source, typename Dest>
struct ConversionTypes{
  using SourceModelType = Source;
  using DestModelType = Dest;
};

And then just add this.

using ConversionModels = testing::Types<ConversionTypes<ARGBNormalized,YCCNormalized>.....>;

If model's count were say n I would have to write n*(n-1) types in conversion models list. It does not matter if I had like 4 or 3 color models, but I am gonna have 10+ models in my program, and it is insane to write 90 combinations by-hand. So is there a way tp somehow generate these ConversionModels from Models I already wrote?

Hrant Nurijanyan
  • 789
  • 2
  • 9
  • 26

1 Answers1

1

With Boost.Mp11, all it takes is a little extra predicate. We can write the transformation as follows:

#include <type_traits>
#include <gtest/gtest.h>
#include <boost/mp11/algorithm.hpp>

template<typename Source, typename Dest>
struct ConversionTypes {
  using SourceModelType = Source;
  using DestModelType = Dest;
};

template<typename>   struct SameConversionTypes : std::false_type{};
template<typename T> struct SameConversionTypes<ConversionTypes<T, T>> : std::true_type{};

using Models = ::testing::Types<char, int, long, long long>;

using ConversionModels = 
        boost::mp11::mp_remove_if<
            boost::mp11::mp_product<ConversionTypes, Models, Models>,
            SameConversionTypes
        >;

mp_product applies ConversionTypes to the Cartesian product of the list Models with itself. The result is produced in the same sort of list as Models. So we obtain a ::testing::Types<ConversionTypes<...>...>. However, since you do not want to test the identity conversion, we use mp_remove_if to get rid of all elements of the form ConversionTypes<T, T> (which is what we defined the helper predicate SameConversionTypes for).


Having said that, while this works with the GoogleTest trunk, the above may fail for you. That is because google test only switched to using variadic templates for Types only in the last 18 months or so. Prior it generated a testing::Types template that took a very large number of arguments (to simulate variadic templates for older compilers). If you use a slightly older version of GoogleTest, then you can still rely on boost::mp11 to get by, with a little alteration.

using Models_ = boost::mp11::mp_list<char, int, long, long long>;
using Models  = boost::mp11::mp_rename<Models_, ::testing::Types>;

using ConversionModels = 
    boost::mp11::mp_rename<
        boost::mp11::mp_remove_if<
            boost::mp11::mp_product<ConversionTypes, Models_, Models_>,
            SameConversionTypes
        >,
        ::testing::Types
    >;

mp_list is a truly variadic type list. So we can define everything in terms of it, and then use mp_rename to replace the "outer shell". It turns mp_list<A, ..., Z> into ::testing::Types<A, ..., Z>.

Only caveat remaining is to be mindful of GTest's implementation limits. It may not support a type list that is 90 types long. You may need to regenerate the header locally to increase how many types it can support.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458