Çarşamba, 26 Mart 2014 11:02

C++ dilinde sayısal uyumlu (binary compatible ) problemleri

Yazan 
Öğeyi Oyla
(0 oy)

Sayısal Uyumlu  ve Kaynak Uyumlu Dinamik Kütüphaneler

Eğer bir program bir kütüphane ( dll,so uzantılı ) dosyasına bağlanmışsa ve bu kütüphane dosyası kodlarda yapılan değişiklik sebebiyle güncellenmişse aynı programın çalışmaya devam edebilmesi için güncellenen kütüphanenin önceki versiyonla sayısal olarak uyumlu (binary compatible) olması gerekir.

Aynı şekilde eğer bir kütüphane için derlenmiş kodlarla oluşan program güncellenen kütüphane dosyası ile birlikte çalışabilmek için sadece yeniden derlenmesi gerekiyorsa bu durumda kütüphane dosyası için kaynak kod uyumlu ( source compatible) tanımı yapılır.

Sayısal olarak uyumlu olan kütüphane dosyaları daha çok tercih edilen bir yöntemdir. Böylelikle kütüphane dosyalarındaki hata ve iyileştirmeler başarı ile yapılırken bu kütüphaneleri kullanan dosyalarda bir değişiklik yapılması gerekmez. Aksi taktirde programın güncellemelerden bağımsız bir şekilde sürekli çalışmasını sağlamak amacıyla programcı kütüphaneyi programına statik olarak eklemeyi düşünebilir. Fakat bu yöntem hem kaynak tüketimini arttırır, dosya boyutu artar ve kütüphane dosyalarında güvenlik problemlerinin çözülmesini engeller.

Kütüphane Programlama Teknikleri

Kütüphane programlarken en çok problem olan durum sınıfa eklenen bir üye neticesinde dosya boyutunun ve şablonunun değişmesi ile kütüphane dosyasının sayısal uyumluluğunun bozulmasıdır.

d-işaretçisi ( d-Pointer) kullanımı
 

d-işaretçisi  bir tasarım örüntü tekniği olarak özellikle kütüphane dosyalarının sayısal uyumlu kalmasını sağlamak için kullanılan bir yöntemdir.

Şimdi aşağıdaki gibi bir kütüphane dosyası için kaynak kodumuz olsun ve versiyonu WidgetLib 1.0 olsun.

 

class Widget{
private :
Rect m_geometry;
String m_stylesheet; // WidgetLig 1.1 için yeni eklendi
}
class Label:public Widget{
public:
String text() const { return m_text; }
private:
String m_text;

}

Şimdi bu kaynak dosyamıza yeni bir üye özellik ekleyrek sınıfımızı değiştirelim.

class Widget{
private :
Rect m_geometry;
}
class Label:public Widget{
public:
String text() const { return m_text; }
private:
String m_text;

}


Kütüphanemizin yeni versiyonu WidgetLib 1.1 olsun. Şimdi önemli noktaya gelelim eğer WidgetLib 1.0 kullanan ve buna göre derlenmiş bir programmız varsa kütüphane dosyamız WidgetLib 1.1 e güncellendiğinde bu program çöker.
Sebebi ise kütüphane versiyonları arsındaki sayısal uyumsuzluktur. Yeni eklenen özellik sebebiyle sınıfın boyutu artmıştır.

Şimdi d-gösterici (d-pointer) kullanarak problemimizi çözelim.

//widget.h dosyası
class WidgetPrivate; 
class Widget{
private :
Rect geometry() const;
private:
WidgetPrivate *d_ptr;
}
//widget_p.h dosyası. 
/*
Dosya adındaki "_p" eklentisi gelenekseldir.
*/
struct WidgetPrivate{
Rect geomentry;
String stylesheet;
}
//widget.cpp dosyası aşağıdaki gibi olacak.
#include "widget_p.h"
Widget::Widget():d_ptr(new WidgetPrivate){
// özel üye değişkenleri burada oluşturulabilir.
}
Rect Widget::geometry() const{
return d_ptr->geometry;
}
//label.h
class label:public Widget{
String text();
private:
//Her sınıf kendi d-işaretçisini muhafaza eder.
LabelPrivate *d_ptr;
};
//label.cpp
struct LabelPrivate{
String text;
};
Label::Label():d_ptr(new LabelPrivate){
}
String Label::text()
{
return d_ptr->text;
}

Şimdi yukarıdaki kodlar vasıtasıyla derlenen kütüphanede widget sınıfı için tanımladığımız (d_ptr) d-işaretçisinin kapladığı yer hiçbir zaman değişmediği için ve güncellemelerimizide d_ptr işaretçisinin göstermiş olduğu widget_p sınıfı içerisinde yapacağımız için kodlarda yapacağımız güncellemeler neticecsinde alacağımız kütüphane sonuçları sayısal olarak uyumlu (binary compatible) olur.

Dolayısıyla d_işaretçisi kullanılarak yapılan kütüphane güncellemelerinde ilk kütüphane versiyonu için yapılmış olan program diğer kütüphane güncellemelerinde de çalışacaktır.

d_işaretçisinin diğer yararları

  • Gerçekleştirim detayları gizlenir ve kütüphane dosyası sadece bir başlık ve ikili dosya ile kapalı kod haline getirilebilir.
  • Başlık dosyası detaylar gizli kaldığı için bir API referansı olarak kullanılabilir.
  • Gerçekleştirme kodları kaynak dosyasına taşındığı için derleyici programı daha hızlı derler.
q_işaretçisi ve kullanımı
Şimdiye kadar gördüğümüz örneklerde d-işaretçisi genellikle C programlama stilindeki yapı (struct) şeklindeydi. Halbuki bu hem bir yapı hem de bir sınıf olabilir. Ve bu sınıf veya yapılar da yardımcı fonksiyonlar dediğimiz methodları barındırabilir. İşte bu yardımcı fonksiyonlar d_işaretçisinin sınıfında bulunan bir metoda ulaşması gerekebilir.  İşte bu d_işaretçisinin gösterdiği özel sınıfda d_işaretçisinin tanımlandığı sınıfı gösteren bir işaretçi tanımlanır. Bu işaretçi vasıtasıya sınıfdaki public değerli metodlara ulaşılabilir. Bu işaretçiyi q-işaretçisi ( q-pointer ) olarak adlandırıyoruz.
//widget.h dosyası
class WidgetPrivate; 
class Widget{
private :
Rect geometry() const;
private:
WidgetPrivate *d_ptr;
}
//widget_p.h dosyası. 
/*
Dosya adındaki "_p" eklentisi gelenekseldir.
*/
struct WidgetPrivate{
// Yapılandırıcı q-işaretçisini ayarlıyor.
WidgetPrivate(Widget *q):q_ptr(q){}
Widget *q_ptr; // q-işaretçisi API sınıfımızı gösterir.
Rect geomentry;
String stylesheet;
}
//widget.cpp dosyası aşağıdaki gibi olacak.
#include "widget_p.h"
// 'this' işaretçisnin d_ptr atama yapılırken nasıl kullanıldığına dikkat edin.
Widget::Widget():d_ptr(new WidgetPrivate(this)){
// özel üye değişkenleri burada oluşturulabilir.
}
Rect Widget::geometry() const{
return d_ptr->geometry;
}
//label.h
class label:public Widget{
String text();
private:
//Her sınıf kendi d-işaretçisini muhafaza eder.
LabelPrivate *d_ptr;
};
//label.cpp
struct LabelPrivate{
LabelPrivate(Label *q) : q_ptr(q) { }
Label *q_ptr;
String text;
};
Label::Label():d_ptr(new LabelPrivate){
}
String Label::text()
{
return d_ptr->text;
}

Q_D, Q_Q, Q_DECLARE_PRIVATE ve Q_DECLARE_PUBLIC makroları

Şimdi qt q-işaretçisi ve d-işaretçisi kullanımını kolaylaştırmak için bazı makrolar tanımlar.

void MyClass::setFoo( int i )
{
    Q_D(MyClass);
    d->m_foo = i;
}

int MyClass::foo() const
{
   Q_D(const MyClass);
   return d->m_foo;
}
Okunma 4634 defa Son Düzenlenme Çarşamba, 26 Mart 2014 18:23
Ufuk Yıldırım

Yazılım Geliştirme Uzmanı

Web site: www.ufuk.biz

Yorum Ekle

Gerekli olan (*) işaretli alanlara gerekli bilgileri girdiğinizden emin olun. HTML kod izni yoktur.