September 09, 2009

throw'u D'nin kütüphanesine bakarken görmüştüm. Anladığım kadarıyla C++'da da çok kullanılıyor.
Nerede kullanılır nasıl kullanılır gibi bilgiler edinebilir miyim ?

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

September 10, 2009

throw; "at, fırlat" anlamındadır ve bir fonksiyonun "benim bu işi yapmam olanaksız" diyerek havlu atmasıdır. ;)

Hata atma [exception] kavramının değerini görmek için öncelikle bu olanağın olmadığı dillerde nasıl yapıldığına bakmak gerek. Bir C programı:

#include <stdio.h>

#define BOOL int
#define TRUE 1
#define FALSE 0

BOOL yumurtanin_kabugu_var()
{
   // Asagida hata olusmasi icin bilerek hatali
   return FALSE;
}

int yumurta_kir()
{
   int hata = 0;

   if (!yumurtanin_kabugu_var()) {
       hata = 1;
       goto cikis;
   }

   // asil isi gerceklestir

cikis:
   // Burada, gerekiyorsa temizlik islemleri yapilir

   return hata;
}

int tezgahi_bosalt()
{
   int hata = 0;

   // Burada yumurta_kir()'a benzer hata denetimi...

   return hata;
}

int malzemeleri_topla()
{
   int hata = 0;

   // Burada yumurta_kir()'a benzer hata denetimi...

   return hata;
}

int yemek_pisir()
{
   int hata = 0;

   hata = tezgahi_bosalt();
   if (hata) {
       goto cikis;
   }

   hata = malzemeleri_topla();
   if (hata) {
       goto cikis;
   }

   hata = yumurta_kir();
   if (hata) {
       goto cikis;
   }

   // vs.

cikis:
   // Burada, gerekiyorsa temizlik islemleri yapilir

   return hata;
}

int main()
{
   int hata = 0;

   hata = yemek_pisir();

   if (hata) {
       /*
         Burada normalde hata kodunun hangi mesaja karsilik geldigine de
         bakilabilir; ben sabit bir mesaj yazdiriyorum
       */
       fprintf(stderr, "HATA: Yemek pisiremedim; cunku yumurtanin kabugu yok!\n");

       return 1; }

   return 0;
}

Her çağrının mutlaka hata kodunu denetlemesi gerektiğini görüyor musun? Her fonksiyon çağrısının iki işi var: 1) fonksiyonu çağır, 2) hatayı denetle... ve hatayı elden ele üst katmanlara aktar.

Yukarıdaki örnekte hata kodu şu sıra ile elden ele geçiriliyor:

'yumurta_kir() -> yemek_pisir() -> main()'

Tabii aynı durum tezgahi_bosalt() ve malzemeleri_topla() fonksiyonlarında da oluşur, ve aslında uygulamada elden ele geçire fonksiyonların sayısı üç değil, çok daha fazla olur.

Burada dikkat edilmesi gereken şu: Hata, yalnızca onu bildiren ve onu bilmek isteyen fonksiyonu ilgilendirir. İşte hata atma düzeneği bunu gerçekleştirir. 'throw' ile atılan hata, ara katmanların işlerini otomatik olarak yarım bıraktırır; ve hata ile ilgilenen fonksiyona otomatik olarak ulaşır.

İşte aynı programın hata atan D eşdeğeri:

import std.cstream;

int main()
{
   try {
       yemek_pişir();

   } catch {
       derr.writefln("HATA: Yemek pişiremedim; çünkü yumurtanın kabuğu yok!");
       return 1;
   }

   return 0;
}

class Hata
{}

void yemek_pişir()
{
   tezgahi_boşalt();
   malzemeleri_topla();
   yumurta_kır();
   // vs.
}

void yumurta_kır()
{
   if (!yumurtanın_kabuğu_var()) {
       throw new Hata;
   }

   // asıl işi gerçekleştir
}

void tezgahi_boşalt()
{
   // gerekirse hata atabilir
}

void malzemeleri_topla()
{
   // gerekirse hata atabilir
}

bool yumurtanın_kabuğu_var()
{
   return false;
}

Şimdi hatayı farkeden fonksiyon ("throw eden") ile hatayla ilgilenen fonksiyon ("catch eden") arasında aracı olmadığı için kod son derece temiz. Herkes, işini yapmaya çalışıyor...

Not: Aslında Hata sınıfı daha akıllı olabilir ve hatanın kendisiyle ilgili bilgi de taşır. Örneğin türü bile 'KabuksuzYumurtaHatası' olabilir...

Ali

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]