D'nin sözleşmeli programlama olanaklarında bir eksiklik var: 'out' bloğu içindeyken 'in' bloğu içindeki değişkenlere erişilemiyor. (1)
Aslında bunun temel nedeni, farklı blokların birbirlerini görememeleridir:
{
int i = 42;
}
{
int j = i; // derleme hatası: 'i' tanımlı değil!
}
Bu durum in ve out blokları için de geçerlidir; ama ne yazık ki o durumda bir eksiklik olarak görülebiliyor. Çünkü bazen belirli bir durumun değişmediğini denetlemek isteyebiliriz. Örneğin bazı sınıf üyeleri eski değerlerini korumalıdırlar. D haber grubunda bununla ilgili bir konu açıldı; oradan uyarlıyorum.
Elimizde şöyle bir program olsun:
struct Nokta
{
int satır;
int sütun;
}
struct DörtgenBölge
{
Nokta solÜst;
int en;
int boy;
this(Nokta solÜst, int en, int boy)
{
this.solÜst = solÜst;
this.en = en;
this.boy = boy;
}
/**
* Bölgenin yerini değiştirir
*
* Params:
* yukarı = yukarı öteleme miktarı; eksi değer aşağı öteler
* sağa = sağa öteleme miktarı; eksi değer aşağı öteler
*/
void ötele(int yukarı, int sağa)
{
solÜst.satır += yukarı;
solÜst.sütun += sağa;
}
}
void main()
{
auto bölge = DörtgenBölge(Nokta(-1, -2), 30, 40);
bölge.ötele(5, 6);
}
Oradaki 'ötele' işlevi sırasında dörtgenin eninin veya boyunun değişmemesini isteriz. Yani amaç, ötele'nin bunları değiştirmediğini denetlemek. (Not: Bu sınıf farklı şekillerde de tasarlanabilir. Saçma geliyorsa göz yumun lütfen. :))
Akla ilk gelen ve çalışması gereken çözüm, in ve out bloklarıdır:
void ötele(int yukarı, int sağa)
in
{
// Eski değerleri hatırlayalım ...
immutable int eskiEn = en;
immutable int eskiBoy = boy;
}
out
{
// ... ve karşılaştıralım
assert(en == eskiEn);
assert(boy == eskiBoy);
}
body
{
solÜst.satır += yukarı;
solÜst.sütun += sağa;
}
İşte, out bloğu in bloğunun içini göremediği için derleme hatası alırız:
'
Error: undefined identifier eskiEn
Error: undefined identifier eskiBoy
'
(Not: Daha önceden konuştuğumuz gibi, değişmeyeceğini bildiğim yerel değişkenleri de 'immutable' olarak işaretledim.)
Bunun en güzel çözümü, out bloklarının bir istisna olarak in bloklarını görebilmeleri olurmuş; ama dmd'yi bu şekilde değiştirmek çok dertli olduğu için şimdilik ertelemişler.
Bir başka öneri, dile 'old' diye bir anahtar sözcük eklemek ve eski değişkenlerin otomatik olarak saklanmalarını sağlamakmış. Bu durumda in bloğuna da gerek kalmadan:
void ötele(int yukarı, int sağa)
out
{
assert(en == old.end);
assert(boy == old.boy);
}
body
{
solÜst.satır += yukarı;
solÜst.sütun += sağa;
}
Ama öyle bir olanak da henüz yok. :/
Andrei Alexandrescu'nun önerdiği çözüm, bu yetersizliği kabul etmek ve böyle durumlarda da 'scope''tan yararlanmak: (2)
void ötele(int yukarı, int sağa)
{
debug {
immutable int eskiEn = en;
immutable int eskiBoy = boy;
scope (exit) {
assert(en == eskiEn);
assert(boy == eskiBoy);
}
}
++en; // İstenmeyen bir değişiklik; bunu yakalayacağız
solÜst.satır += yukarı;
solÜst.sütun += sağa;
}
Bütün denetimi de bir 'debug' bloğu içine alıyor... Böylece yalnızca özellikle '-debug' seçeneği kullanıldığında etkilidirler. (Garip bir şekilde, '-debug' ile '-release' birbirlerinin tersi değildir.)
O konuda yeni bir şey daha öğrendim: 'else', 'debug' blokları ile de kullanılabiliyormuş:
debug {
// ... -debug seçeneği kullanılmışsa ...
} else {
// ... kullanılmamışsa ...
}
'else''i aslında Koşullu Derleme dersinde 'version' ile kullanmışız, ve version ile debug kardeş olanaklar... (3)
Yukarıdaki konular şu sayfalarda geçiyor:
(1) Sözleşmeli Programlama
http://ddili.org/ders/d/sozlesmeli.html
(2) Hata Atma ve Yakalama
http://ddili.org/ders/d/hatalar.html
(3) Koşullu Derleme
http://ddili.org/ders/d/kosullu_derleme.html
Ali
--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]