Giriş
C++'ta Template ve virtual metodlar yanyana pek gelemiyorlar. Template parametreleri metodun imzası oldukları anda kalıtımda imza bozuluyor. Java gibi dillerde ise bu problem olmuyor. Java'da şöyle bir sınıfımız olsun.
Çeşitli çözümler var. Benim anladığım en kolay çözüm virtual metodu template'tan kurtarmak. Bir çeşit dispatch'in kullanmak
Template Visitor
Template visitor da dispatch yöntemini kullanıyor. Ben de bir projede şuna benzer bir kod kullandım.
Template Store
T tipi nesneyi saklamak için kullanılır. Önce bir ata sınıf tanımlanır.Şöyle yaparız.
Şöyle yaparız. Parameter nesnesi Interface arayüzünü gerçekleştiren generic bir Interfacer nesnesi içerir. Böylece tanımlı bir sınıfa bir çeşit ata sınıf verme imkanı oluyor.
C++'ta Template ve virtual metodlar yanyana pek gelemiyorlar. Template parametreleri metodun imzası oldukları anda kalıtımda imza bozuluyor. Java gibi dillerde ise bu problem olmuyor. Java'da şöyle bir sınıfımız olsun.
abstract class A<T> {
abstract void setList(List<? extends T> list);
}
A sınıfından kalıtan B sınıfı, metodun imzasını template parametresine bağlayarak değiştirebilir.class B extends A<Foo> {
@Override void setList(List<? extends Foo> list) { ...}
}
C++'ta ise bunu yapamıyoruz. Data isimli bir sınıfımız olsun.template<size_t SIZE>
class Data {...};
Bu sınıfı döndüren Generator ve Generator'den kalıtan bir sınıfımız daha olsun. SIZE imzaya dahil edildiği için kalıtımda farklı bir SIZE kullanılması derleme hatasına sebep olur.class Generator {
virtual template<size_t SIZE> Data<SIZE> generate() = 0;
};
class PseudoRandomX : public Generator {
template<size_t SIZE> Data<SIZE> generate() override {...}
};
ÇözümÇeşitli çözümler var. Benim anladığım en kolay çözüm virtual metodu template'tan kurtarmak. Bir çeşit dispatch'in kullanmak
class Generator {
public:
template< size_t SIZE >
Data<SIZE> generate() {
Data<SIZE> x;
vgenerate(SIZE, x.pointer_to_internals());
return x;
}
protected:
virtual void vgenerate(size_t size, InternalDataPointer *p) = 0;
};
class PseudoRandomX : public Generator {
void vgenerate(size_t size, InternalDataPointer *p) override {...}
};
Diğer çözümleri anlamadım :)Template Visitor
Template visitor da dispatch yöntemini kullanıyor. Ben de bir projede şuna benzer bir kod kullandım.
class DefaultVisitor {
public:
template <typename T>
void setMsg (const T& value) {
visit (value);
}
protected:
virtual void visitImpl (const std::string & value) {...};
virtual void visitImpl (const int & value) {...};
Amacım aralarında ortak bir hiyerarşi bulunmayan nesneler için visitor yapabilmekti. Bu sınıftan kalıtan sınıflar sadece ilgilendikleri nesne için visitImpl metodlarını gerçekleştiriyorlardı.Template Store
T tipi nesneyi saklamak için kullanılır. Önce bir ata sınıf tanımlanır.Şöyle yaparız.
class ParameterBase
{
public:
virtual ~ParameterBase() {}
template<class T> const T& get() const; //to be implimented
template<class T, class U> void setValue(const U& rhs); //to be implimented
};
Daha sonra bu ata sınıf'tan kalıtan template tanımlanır. Şöyle yaparız.template <typename T>
class Parameter : public ParameterBase
{
public:
Parameter(const T& rhs) :value(rhs) {}
const T& get() const {return value;}
void setValue(const T& rhs) {value=rhs;}
private:
T value;
};
Bu sınıf için getter ve setter kodlanır. Şöyle yaparız.//Here's the trick: dynamic_cast rather than virtual
template<class T> const T& ParameterBase::get() const
{
return dynamic_cast<const Parameter<T>&>(*this).get();
}
template<class T, class U> void ParameterBase::setValue(const U& rhs)
{
return dynamic_cast<Parameter<T>&>(*this).setValue(rhs);
}
Böylece ortak bir ata sınıftan kalıtmayan nesneleri ortak bir ata sınıf ile sarmalayarak vector,list gibi yapılarda saklayabiliriz. Aslında boost::any gibi bir yapı ortaya çıkıyor. Şöyle yaparız.std::vector<ParameterBase*> vector;
Template Store ve Ortak Ata SınıfŞöyle yaparız. Parameter nesnesi Interface arayüzünü gerçekleştiren generic bir Interfacer nesnesi içerir. Böylece tanımlı bir sınıfa bir çeşit ata sınıf verme imkanı oluyor.
class Parameter
{
struct Interface
{
virtual void bar() = 0;
virtual ~Interface(){}
};
template <class T>
struct Interfacer : Interface
{
T t;
Interfacer(T t):t(t){}
void bar() { t.bar(); }
};
std::unique_ptr<Interface> interface;
public:
template <class T>
Parameter(const T & t): interface(new Interfacer<T>(t)){}
void bar() { interface->bar(); }
};
Hiç yorum yok:
Yorum Gönder