vendredi 26 août 2016

C++ Template Specialization Enforcement

I am trying to write a small library where I need to enforce a specific template specialization. Assume I have an abstract class Abstract<S,T> with two template arguments. I also have another (abstract) class which is parameterized by Abstract<S,T> as in Other<U, V, Abstract<S,T>>. I would like to define virtual void Other<U, V, Abstract<S,T>>::operator()(U, V, Abstract<S,T>&) = 0; so that yet another Another<S,T,U> : public Other<S,T,U> (concrete) class can override the operator() for references to the abstract object. This way, I can create new types which can use the inheritance mechanism for any Concrete<S,T> : public Abstract<S,T> object it receives in its operator().

I am not a great software architect. Below is a small code excerpt of how I think the above mechanism could be enforced. I have already read about the issue in different forums, and have realized that this might be solved using type_traits of C++11 (maybe a SFINAE-type of solution, I don't know).

I would appreciate if you could give me an idea of how to enforce such a mechanism. Or, even better, another approach (i.e., a different design pattern solution) to my problem, if needed.

#include <iostream>
#include <type_traits>

template <typename S, typename T>
class Abstract {
public:
  Abstract() { std::cout << "Abstract: ctor called." << std::endl; }
  virtual void operator()(void) {
    std::cout << "Abstract::operator() called." << std::endl;
  }
};

template <typename S, typename T>
class Concrete : public Abstract <S, T> {
public:
  Concrete() { std::cout << "Concrete: ctor called." << std::endl; }
  void operator()(void) override {
    std::cout << "Concrete::operator() called." << std::endl;
  }
};

template <typename S, typename T>
class A {
public:
  A() { std::cout << "A: ctor called." << std::endl; }
};

template < typename S, typename T, typename U >
class B {
  // hopefully a catch-all static_assert to warn the user about the template
  // specialization.
};

template < typename S, typename T, typename U, typename V >
class B < S, T, Abstract<U, V> > {
public:
  B() { std::cout << "B: ctor called." << std::endl; }
  virtual void operator()(S s, T t, Abstract<U,V>& y) {
    std::cout << "B::operator(S, T, U&) called." << std::endl;
    y();
  };
};

template < typename S, typename T, typename U >
class C : public B < S, T, U > {
public:
  C() { std::cout << "C: ctor called." << std::endl; }
  void operator()(S s, T t, U& y) override {
    std::cout << "C::operator(S, T, U&) called." << std::endl;
    y();
  };
};

int main (int argc, char* argv[]) {
  // B     <int, int, A<double, double>>         b1 { };
  B     <int, int, Abstract<double, double>>  b2 { };
  // B     <int, int, Concrete<double, double>>  b3 { };
  C     <int, int, Abstract<double, double>>  c1 { };

  // A         <double, double>  obj1    { };
  Concrete  <double, double>  obj2    { };

  // b1 ( 5, 6, obj1 );
  b2 ( 5, 6, obj2 );
  // b3 ( 5, 6, obj2 );
  c1 ( 5, 6, obj2 );
  return 0;
}

Above solution provides a mechanism to enforce what I want; however, the compiler can not give informative error messages to the end user. I would like to have such a nice error mechanism. I would also appreciate, if you pointed out to possible flaws/problems related to the above solution.

Aucun commentaire:

Enregistrer un commentaire