Limit the supported template class types with variadic templates

I'm trying to handle classes of image processing operations that are only compatible for certain combinations:

  • measurement set [1,2,3, ...]
  • set of types [int, float, double, ...]

One working method would be to define a generic templated class that handles the default case (which: does nothing):

template <int dimension, typename dataType>
class IPOperation
{
public:
    void execute()
    {
        std::cout << "do nothing" << std::endl;
    }
};

      

Then I would have to write a specialization of this class for all supported combinations of types, for example, for example:

template<>
class IPOperation<2,float>
{
public:
    void execute()
    {
        std::cout << "do something" << std::endl;
    }
};

      

But since the number of supported size / type combinations can get really large, this approach is overkill.

Inspired by political design, it would be nice to equip the template class with certain constraints, something like this:

template <int dimension, typename dataType>
class IPOperation : public supportedDimensions<2,3,4>, public supportedTypes<int, float, double>
{
    void execute()
    {
        std::cout << "execute()" << std::endl;
    }
};

      

My bad approach so far has been (and it seems pretty bad, I can't help myself):

#include <iostream>
#include <vector>

template <int... supportedDimensions>
class IPDimensions
{
private:
public:
    static std::vector<int> getSupportedDimensions()
    {
        return { supportedDimensions... };
    }

    static bool supportsDimension(int dim)
    {
        std::vector<int> dimensions = getSupportedDimensions();
        return std::find(dimensions.begin(), dimensions.end(), dim) != dimensions.end();
    }
};

template <int dim>
class IPOperation : public IPDimensions<2,3>
{
private:
public:
    void execute(void)
    {
        if(IPOperation::supportsDimension(dim))
        {
            std::cout << dim << "d is supported -> execute" << std::endl;
        }
        else
        {
            std::cout << dim << "d is not supported -> sit down and do nothing" << std::endl;
        }
    }
};

int main(int argc, const char * argv[])
{
    IPOperation<2>* okay = new IPOperation<2>();
    IPOperation<4>* notOkay = new IPOperation<4>();

    okay->execute();
    notOkay->execute();
}

      

When trying to apply something like this for types, I am completely lost. It would be nice to have some kind of mechanism for doing some checks on how you use policies as delegates for certain strategies. Maybe my approach is wrong and the whole thing could be done much easier with macros, enums or dashes and std :: enable_if to make functions visible only for certain scenarios, but since I spent some time reading some C ++ 11 so, I don't know anything else.

Thanks guys for some helpful advice!

+3


source to share


3 answers


You can use something like the following if you want to allow a bogus operation (but not a compilation error) for unsupported types:



#include <type_traits>
#include <iostream>

template <class...>
struct supportedTypes {
    template <class X>
    static constexpr bool check() { return false; };
};
template <class A, class... R>
struct supportedTypes<A, R...> {
    template <class X>
    static constexpr bool check() {
        return std::is_same<X, A>::value
        || supportedTypes<R...>::template check<X>(); }
};

int main() {
    std::cout << supportedTypes<int,double>::check<int>();
    std::cout << supportedTypes<int,double>::check<void>();
}

      

+1


source


I am using SFINAE for this type of problem, for example:

template<std::size_t dimensions, typename dataType, typename=void> class IPOperation;

template<std::size_t dimensions, typename dataType>
class IPOperation<dimensions, dataType,
                  std::enable_if<(0<dimensions && dimensions<4) && 
                                 std::is_floatingpoint<dataType>::value >::type>
{
   /* ... */
};

IPOperation<2,float> a; // okay
IPOperation<4,float> b; // compile-time error: wrong dimensionality
IPOperation<3,int>   c; // compile-time error: wrong dataType

      

or static_assert

:



template<std::size_t dimensions, typename dataType>
class IPOperation<dimensions, dataType>
{
   static_assert(0<dimensions && dimensions<4,"wrong dimension");
   static_assert(std::is_floatingpoint<dataType>::value,"incompatible data type");
   /* ... */
};

      

which gives slightly nicer error messages.

+1


source


You can use boost::mpl::set

to check if a given size / type is supported. Use static_assert

if you want to reject unsupported types at compile time, or use SFINAE if you really want the "do nothing by default" logic.

static_assert

#include <iostream>

#include <boost/mpl/set.hpp>
#include <boost/mpl/set_c.hpp>

using namespace boost::mpl;

template <std::size_t Dim, typename Type>
struct Op {

  static_assert(has_key<set_c<std::size_t, 2, 3, 4>,
                        integral_c<std::size_t, Dim>>::value,
                "Unsupported dimension!");
  static_assert(has_key<set<int, float, double>, Type>::value,
                "Unsupported type!");

  void Execute() {
    std::cout << "DoSomething" << std::endl;
  }

};

int main() {
  // Op<1, int> x;  // error: Unsupported dimension!
  // Op<2, std::string> x;  // error: Unsupported type!
  Op<2, int> x;
  x.Execute();
}

      

Printing

DoSomething

      

SFINAE

#include <iostream>

#include <boost/mpl/set.hpp>
#include <boost/mpl/set_c.hpp>

using namespace boost::mpl;

template <std::size_t Dim, typename Type, typename = void>
struct Op {

  void Execute() {
    std::cout << "DoNothing" << std::endl;
  }

};

template <std::size_t Dim, typename Type>
struct Op<Dim,
          Type,
          std::enable_if_t<has_key<set_c<std::size_t, 2, 3, 4>,
                                   integral_c<std::size_t, Dim>>::value &&
                           has_key<set<int, float, double>, Type>::value>> {

  void Execute() {
    std::cout << "DoSomething" << std::endl;
  }

};

int main() {
  Op<1, int> x;
  Op<2, int> y;
  x.Execute();
  y.Execute();
}

      

Printing

DoNothing
DoSomething

      

0


source







All Articles