<?xml version="1.0" encoding="UTF-8"?> <rss
version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
><channel><title>Diyezon &#187; Delphi</title> <atom:link href="http://www.diyezon.com/tag/delphi/feed/" rel="self" type="application/rss+xml" /><link>http://www.diyezon.com</link> <description>programlama sanatı...</description> <lastBuildDate>Tue, 24 Jan 2012 00:13:26 +0000</lastBuildDate> <language>en</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <generator>http://wordpress.org/?v=3.3.1</generator> <item><title>7 Adımda Delphi Uygulamalarını Daha da Hızlandırın</title><link>http://www.diyezon.com/7-adimda-delphi-uygulamalarini-daha-da-hizlandirin/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=7-adimda-delphi-uygulamalarini-daha-da-hizlandirin</link> <comments>http://www.diyezon.com/7-adimda-delphi-uygulamalarini-daha-da-hizlandirin/#comments</comments> <pubDate>Tue, 24 Feb 2009 03:58:47 +0000</pubDate> <dc:creator>Fatih Tolga Ata</dc:creator> <category><![CDATA[Delphi]]></category> <category><![CDATA[optimizasyon]]></category> <category><![CDATA[performans]]></category><guid
isPermaLink="false">http://www.diyezon.com/?p=256</guid> <description><![CDATA[Delphi, etkili ve hızlı sonuç alabileceğiniz nadir geliştirme ortamlarından birisi. Delphi&#8217;nin hızlı program geliştirme özelliği, bazen bizlerin müsrif davranmasına yol açabiliyor. Aslında birazdan vereceğim adımlardan bazıları, çoğu programlama dillerinde dikkat etmemiz gereken kurallardan. Uygulamayı dağıtmadan önce hız için dikkat etmemiz gerekenleri 7 adımda toplayıp sizlere sunuyorum. Otomatik Form Oluşturmayın: Normalde, projeye eklediğiniz her bir form [...]]]></description> <content:encoded><![CDATA[<p
style="text-align: justify;">Delphi, etkili ve hızlı sonuç alabileceğiniz nadir geliştirme ortamlarından birisi. Delphi&#8217;nin hızlı program geliştirme özelliği, bazen bizlerin müsrif davranmasına yol açabiliyor. Aslında birazdan vereceğim adımlardan bazıları, çoğu programlama dillerinde dikkat etmemiz gereken kurallardan. Uygulamayı dağıtmadan önce hız için dikkat etmemiz gerekenleri 7 adımda toplayıp sizlere sunuyorum.</p><p><span
id="more-256"></span></p><ol
style="text-align: justify;"><li><strong>Otomatik Form Oluşturmayın: </strong>Normalde, projeye eklediğiniz her bir form uygulama tarafından otomatik olarak oluşturulmak üzere ayarlıdır. Bunu dpr dosyasının kaynak kodunda bulunan Application.CreateForm ile görebilrisiniz. Her ne kadar bu size formlara erişirken büyük kolaylık sağlasa da uygulamanın ilk çalışma zamanını önemli derece de yavaşlatmaktadır. Bu yüzden formları otomatik olarak oluşturmayın. Sadece ihtiyacınız olduğunda dinamik olarak oluşturun. Çoğu zaman sadece ana formu otomatik olarak oluşturmanız kafi glecektir.</li><li><strong>Mümkün Olduğunca Windows API&#8217;lerini Tercih Edin:</strong> Yapacağınız bir işlem için eğer Windows API&#8217;lerinde bu işi göre bir fonksiyon var ise, sakın ayrı bir fonksiyon yazmaya kalkmayın. Windows fonksiyonlarını kullanmak bizim için en hızlı yöntem olacaktır. Bu yüzden bir fonksiyon yazmadan önce Windows API&#8217;si içinde bu işi yapan başka bir fonksiyonun olup olmadığından emin olun.</li><li><strong>Küçük Fonskiyonlar İçin &#8220;inline&#8221; Direktifini Kullanın:</strong> Yeni Delphi sürümleri &#8220;inline&#8221; direktifini desteklemektedir. &#8220;inline&#8221; kullanmak exe boyutunu artıracaktır. Fakat hız da artacaktır. Eğer küçük fonskiyonlarınız varsa veya büyük olsa da çok fazla yerde kullanılmayan fonksiyonlarınız varsa, fonksiyon veya prosedür tanımlamasından sonra &#8220;inline&#8221; direktifini kullanın.</li><li><strong>Table yerine Query Tarzı Bileşenler Kullanın:</strong> TTable gibi bileşenleri mümkün olduğunca kullanmaktan çekinin. Bunun yerine TQuery veya TSqlQuery tarzı bileşenleri tercih edin.</li><li><strong>FastMM Kullanın:</strong> FastMM projesi, normalde Delphi 2005 ile Delphi&#8217;ye dahil oldu. Eğer daha düşük versiyon kullanıyorsanız kesinlikle FastMM kullanın. D2005 ve yukarısı için de yeni güncellemeler oldukça faydası olacaktır. Daha fazla bilgi için FastMM&#8217;i google&#8217;da arattırın.</li><li><strong>Kodlarınızı Yazarken Disiplinli Olun:</strong> Kodlarınızı yazarken değişken, sınıf, fonksiyon isimlendirmelerinde belli bir standarta uyun. Bıraktığınız kod yorumlarını daha sonradan bakacak mışsınız veya başka birisi projeyi devam ettirecekmişcesine yazın. Bu ve bunun gibi kendinize kurallar belirleyerek disiplinli bir şekilde kodlarınızı yazın. Bu size, daha sonradan programınızı bitirdikten sonra kolay bir optimizasyon imkanı sağlayacak. Tekrar kodlara dönüp baktığınızda disiplinli ve düzenli yazılmış bir kod göreceksiniz ve yavaş çalışan veya hatalı çalışan kısımları daha çabuk farkedip, kodunuzu optimize edebileceksiniz.</li><li><strong>Basm İle Optimizasyona Gidin:</strong> Basm yani Delphi içinde assembler kullanarak bazı önemli yerleri optimize edebilirsiniz. Özellikle hızın çok önemli olduğu yerleri bu şekilde optimize edebilirsiniz. Bununla ilgili olarak  daha önceden <a
href="http://www.diyezon.com/index.php/2007/06/20/delphi-ve-c-builder-ile-assembler/" target="_blank">burada</a> bir şeyler karalamıştık.</li></ol><p
style="text-align: justify;">Tabi bunlar dışında hızı etkileyen başka faktörlerin de olduğunu unutmayın. Fakat ilk etapta bu adımlara dikkat etmeniz, programlarınızdaki çalışma hızını önemli derece artıracaktır. Buradaki adımların çoğunun tek dez avantajı, exe boyutunun biraz artabilmesi. Fakat bu devirde kimse exe boyutunu çok önemsemiyor.</p><p
style="text-align: justify;">Benim aklıma 7 adım geldi fakat sizlerin de önerileri olursa lütfen yorum yazarak katkıda bulununuz.</p><p
style="text-align: justify;"><strong>Güncelleme: 07.03.2009:</strong> Bu maddelere ek olarak <a
href="#comment-871">buradaki yorumda</a> bulunan 3 maddeye daha göz atmanızı tavsiye ediyorum. Saadettin beye teşekkürler.</p><p
style="text-align: center;"><strong>Fatih Tolga Ata © 2009</strong></p> ]]></content:encoded> <wfw:commentRss>http://www.diyezon.com/7-adimda-delphi-uygulamalarini-daha-da-hizlandirin/feed/</wfw:commentRss> <slash:comments>11</slash:comments> </item> <item><title>Delphi ile Thread(Kanal) Kullanımı &#8211; Bölüm 2</title><link>http://www.diyezon.com/delphi-ile-threadkanal-kullanimi-bolum-2/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=delphi-ile-threadkanal-kullanimi-bolum-2</link> <comments>http://www.diyezon.com/delphi-ile-threadkanal-kullanimi-bolum-2/#comments</comments> <pubDate>Sat, 06 Oct 2007 17:44:45 +0000</pubDate> <dc:creator>Fatih Tolga Ata</dc:creator> <category><![CDATA[Delphi]]></category> <category><![CDATA[kanallar]]></category> <category><![CDATA[kritik bölgeler]]></category> <category><![CDATA[muteks]]></category><guid
isPermaLink="false">http://www.diyezon.com/?p=61</guid> <description><![CDATA[Bu bölümde kanalları nasıl eş zamanlı olarak çalıştırabileceğinizi göreceksiniz. Bunun için kritik bölgeler ve muteksler ile tanışacaksınız. Ayrıca kanal uyumlu olmayan VCL&#8217;in, kanallar ile nasıl kullanılabileceğini de göreceksiniz. Bununla beraber kanal kullanımındaki yaşanan bazı problemlere de değinmeye çalışacağız. Özellikle veritabanlarını kanallar ile kullanmada uyulması gereken püf noktalarına da değinmeye çalışacağız. Aslında bu bölüm gerçekten uzun [...]]]></description> <content:encoded><![CDATA[<p
align="justify">Bu bölümde kanalları nasıl eş zamanlı olarak çalıştırabileceğinizi göreceksiniz. Bunun için <strong>kritik bölgeler</strong> ve <strong>muteksler</strong> ile tanışacaksınız. Ayrıca kanal uyumlu olmayan VCL&#8217;in, kanallar ile nasıl kullanılabileceğini de göreceksiniz. Bununla beraber kanal kullanımındaki yaşanan bazı problemlere de değinmeye çalışacağız. Özellikle veritabanlarını kanallar ile kullanmada uyulması gereken püf noktalarına da değinmeye çalışacağız.</p><p
align="justify">Aslında bu bölüm gerçekten uzun oldu. Normalde VCL kullanımını üçüncü bir bölüme taşımayı düşünüyordum. Ama bu konuda istekler olunca birleştrip yayınlamayı düşüdüm. İçeride sizi iki sayfalık bir makale bekliyor. Bu yüzden sayfa sonunda bitti zannedip kapatmayın!</p><p
align="justify">Hazırsanız başlayalım.</p><p><span
id="more-61"></span></p><h1>Kanallar&#8217;ı Eş Zamanlı Olarak &Ccedil;alıştırmak</h1><p
align="justify">Kanallar ile uğraşırken karşılaşılan en büyük sorunlardan bir tanesi de şüphesiz kanalları ortak bir şekilde sorunsuz olarak çalıştırmaktır. Eminim kanallar ile uğraşan birisi bütün kanallar tarafından ortak olarak kullanılan bir kaynağa erişmede problemlerle karşı karşıya gelmiştir ve kanalları bir köşeye itmesine sebep olmuştur.</p><p
align="justify">Mesela şu veritabanı problemi size tanıdık gelecektir. Diyelim ki, iki adet kanalımız var ve bunlardan birincisi veritabanındaki bir kaydı açıp değişiklik yapmaya başladı. Sonra ikinci kanal aynı kaydın belli bölümlerinde düzenlemeler yaptıktan sonra veriyi kaydetti. Ardından birinci kanal, aynı verilerdeki değişikliğini bitirdi ve kaydetti. Birinci kanal, ikinci kanal&#8217;ın değişikliklerinden habersiz olduğundan ikinci kanalın yaptığı düzenlemeler de uçtu gitti.</p><p
align="justify">Burada okuduğunuz problem, normalde kanallar ve veritabanı ile uğraşan bir çok programcının başına gelmiş bir problemdir. Bununla birlikte sadece veritabanında değil bir çok noktada özellikle bir dosyayı, bir portu, bir DLL&#8217;i veya başka bir şeyi, kanalların <strong>ortak </strong>kullanmasında hep benzer sıkıntılarla karşılaşılmıştır. Bu gibi sıkıntıların ve problemlerin çözümü kanalları eş zamanlı kullanım için <strong>kritik bölgeler</strong> tanımlamak ya da <strong>muteksleri</strong> kullanmaktır. Bu iki yöntem de makalemizin konuları arasındadır.</p><h1>Kritik Bölgeler(Critical Sections)</h1><p
align="justify">Kritik Bölgeler -<em>ya da başka yerlerde görebileceğiniz gibi kritik kesimler, bölümler, vs..</em>- yukarıda anlatığımız problemdeki, birinci kanalın kaynağı kullanması esnasında ikinci kanalın bu kaynağa erişmesini <u>englelemek</u> için kullanılır. Birinci kanal işini yapıyorken, ikinci kanal&#8217;a çalışması için bir zaman dili ayrılmaz. Böylece birinci kanal işini bitirene kadar ikinci kanal çalıştırılmaz.</p><p
align="justify"> Bunun için örnek bir uygulamada yukarıdaki anlattığımız senaryolara benzer bir problem oluşturalım ve çözümünü aramaya çalışalım.</p><p
align="justify">Diyelim ki, iki adet kanalımız var ve bu kanallar bir döngünün her adımında bir dizi işlem gerçekleştiriyor. Ve bu gerçekleştirdikleri işlemler için ortak kullandıkları global olarak tanımlanmış bir değişken olsun. Meseleyi anlatabilmek için iki kanalında yaptığı işlemlerin sonucunun aynı olmasını sağlamak istiyorum. Böylece problem daha iyi anlaşılabilecek.</p><p
align="justify">Birinci kanal bir döngünün her adımında global değişkenimize teker teker harfler ekliyor ve sonuçta değişkenimizde bir yazı oluşuyor. Aynı işlemi ikinci kanalımız da yapıyor ve sonuç olarak birinci kanal ile aynı yazıyı üretiyorlar. Yanlız ikinci kanal harfleri teker teker eklemek yerine <u>ikişer ikişer</u> ekliyor. Ekleme işlemi çok hızlı olduğundan problemin oluşması için aralara <em>Sleep </em>rutinini ekleyerek işlemi birazcık yavaşlatıyoruz. Şimdi bu mantığımızı koda dökmeye başlayalım.</p></p><p
align="justify"> Yeni bir VCL uygulaması açalım ve bir adet button ekleyelim. Aşağıdaki gibi unitin var bloğunda global bir değişken tanımlayalım:</p><pre>
<pre class="brush: delphi">var
  Form1: TForm1;
  SonucYazi: string; //Global değişkenimiz</pre></pre><p
align="justify">Ardından buttonun OnClick olayını aşağıdaki gibi değiştirelim.</p><pre>
<pre class="brush: delphi">procedure TForm1.Button1Click(Sender: TObject);
var
  Kanal1ID, Kanal2ID: DWORD;
begin
  CreateThread(nil, 0, @Kanal1, nil, 0, Kanal1ID);
  CreateThread(nil, 0, @Kanal2, nil, 0, Kanal2ID);
end;</pre></pre><p
align="justify">Bu iki adet kanalımıza ait kanal fonksiyonlarını da aşağıdaki gibi oluşturalım:</p></p><pre>
<pre class="brush: delphi">procedure Kanal1;
procedure Kanal2;

...

implementation

...

procedure Kanal1;
var
  i: Integer;
begin
  for i := 0 to 10 do
  begin
    SonucYazi := SonucYazi + &#039;d&#039;;
    Sleep(10);
    SonucYazi := SonucYazi + &#039;i&#039;;
    SonucYazi := SonucYazi + &#039;y&#039;;
    SonucYazi := SonucYazi + &#039;e&#039;;
    Sleep(10);
    SonucYazi := SonucYazi + &#039;z&#039;;
    SonucYazi := SonucYazi + &#039;o&#039;;
    SonucYazi := SonucYazi + &#039;n &#039;;
    Sleep(10);
  end;
end;

procedure Kanal2;
var
  i: Integer;
begin
  for i := 0 to 10 do
  begin
    SonucYazi := SonucYazi + &#039;di&#039;;
    Sleep(10);
    SonucYazi := SonucYazi + &#039;ye&#039;;
    SonucYazi := SonucYazi + &#039;zo&#039;;
    Sleep(10);
    SonucYazi := SonucYazi + &#039;n &#039;;
  end;
end;</pre></pre><p
align="justify">Gördüğünüz gibi iki kanal fonksiyonu da aynı sonucu üretiyor. Ama sonucu üretme yolları farklı. Ayrıca iki kanal da peş peşe oluşturulup çalıştırılıyor. Forma bir adet daha button ekleyelim ve bize sonucu göstersin. Bunun için ikinci butonumuzun OnClick olayını aşağıdaki gibi düzenleyelim:</p><pre>
<pre class="brush: delphi">procedure TForm1.Button2Click(Sender: TObject);
begin
  ShowMessage(SonucYazi);
end;</pre></pre><p
align="justify">Bu programı çalıştırdıktan sonra ve kanalları birinci düğme ile çalıştırdıktan sonra sonuç yazının &quot;diyezon diyezon diyezon &#8230;&quot; gibi bir şey olmasını isteriz. Yani bir for döngüsünün bir adımı <u>tamamlanmadan</u> <em>SonucYazi </em>isimli global değişkene <u>müdahale</u> olmasını istemeyiz. Halbuki programı çalıştırdığınızda sonucun hiç de böyle olmadığını göreceğiz. <em>Kanal1</em> işini bitirip &quot;diyezon &quot; yazmadan <em>Kanal2</em> işe karışmış ve aralarda kendi harflerini eklemiştir. Hakeza bu, <em>Kanal2</em> için de geçerlidir. Yani <em>Kanal2</em>, döngünün bir adımını bitirip istenilen sonucu veremeden <em>Kanal1</em> global değişkenimize müdahale etmiştir.</p><p
align="justify">Burada verdiğimiz örnek biraz soyut kaçabilir. &Ccedil;ünkü yaptığımız işlemden sonuç olarak elimize bir şey geçmiyor. Ama karşılaşacağınız sorunlar hep bu tarzda sorunlardır. Karşılaştığınız sorunları verdiğimiz bu örnek ile kıyaslama yapabilirsiniz. Mesela biz bu örneğimizde işlem adımlarını yavaşlatmak için <em>Sleep</em> rutinini kullandık. Normal hayatta kanallar ile uğraşırken zaten yaptığımız işlemlerin adımları <em>Sleep</em> rutinini aratmamaktadır. Mesela bir veritabanı tablosunda <em>Edit</em> ve <em>Post</em> gibi metodları kullandığımızda belli bir miktar işlemin tamamlanmasını bekleriz. Bir de eğer sizde sonuç düzgün çıkıyorsa yine söylüyorum işlemciniz, bu makaleyi yazdığım sıralardaki işlemcimden hızlı demektir. Bu durumda <em>Sleep</em> rutinine verdiğimiz değerleri çok az miktarlarda artırmayı deneyin.</p><p
align="justify">Sadede gelelim ve problemi anladı isek şöyle bir soruyu kafamıza getirelim: Bir kanal belli bir işini bitirmeden önce diğer kanalların müdahalesini nasıl <u>engelleyebiliriz</u>?</p><p
align="justify">Cevabı zaten bu bölümün başında verdik. Bunun için belli yerleri <u>kritik bölge</u> ilan edeceğiz. Bir kanalda kritik bölge ilan ettiğimiz yerdeki kodlar çalışmaya başladığı sırada, diğer kanallardaki <strong><u>aynı</u></strong> kritik bölgedeki kodlar işletilmez ve bekler. Yani <strong>aynı kritik bölgeye sahip kanallardan aynı anda sadece bir bölgedeki kodlar çalıştırılabilir</strong>. Böylece diğer kanalların istenilmeyen müdahalesi engellenmiş olur.</p><p
align="justify">Bir Kritik bölge tanımlamak için bilmemiz gereken üç dört adet Windows apisi vardır.</p><p
align="justify"><font
face="Courier New"><strong>InitializeCriticalSection</strong></font>: Bu fonksiyon kritik bölge&#8217;yi temsil eden recordun ilk değerlerini atamakla sorumludur. Yani kritik bölgeyi kullanıma hazırlamaktadır. Genelde formun <em>OnCreate</em> olayında kullanılır.</p><p
align="justify"><font
face="Courier New"><strong>DeleteCriticalSection</strong></font>: Yukarıdaki fonksiyonun tam tersi olarak, bu fonksiyon da kritik bölgeyi ortadan kaldırır. Genelde formun <em>OnDestroy</em> olayına yerleştirilir.</p><p
align="justify"><font
face="Courier New"><strong>EnterCriticalSection</strong></font>: Bu fonksiyonu çalıştırdığınız satırdan itibaren kritik bölgenin başlangıcını belirtirsiniz. Yani Delphi&#8217;deki &quot;<strong><font
face="Courier New">begin</font></strong>&quot; kelimesi gibi düşünebilirsiniz.</p><p
align="justify"><font
face="Courier New"><strong>LeaveCriticalSection</strong></font>: Bu fonksiyon ise, çalıştırıldığı satırla kritik bölgeyi sınırlandırır. Yine Delphi&#8217;deki &quot;<font
face="Courier New"><strong>end</strong></font>&quot; ifadesine benzetebiliriz.</p><p
align="justify">Kritik bölgeyi temsil eden record ise <em>TRTLCriticalSection</em> tipindedir. Normalde Windows SDK yardım dosyalarında <em>LPCRITICAL_SECTION</em> şeklinde tanımlanmıştır. Ama Delphi ekibi bir çok windows yapısını tanımlarken bunlara &quot;<strong>T</strong>&quot; ile başlayan daha iyi okunur alias isimler vermişler. Her neyse&#8230;</p><p
align="justify"> Kritik bölgenin tanımlanması gayet basittir. Bir kritik bölgeyi işletim sistemine tanıtmak için <em>InitializeCriticalSection </em>fonksiyonunu kullanıyoruz. Kritik bölge ile işimiz bittiğinde de <em>DeleteCriticalSection </em>fonksiyonunu çağırmayı unutmuyoruz. Ardından kanal fonksiyonlarımızda kritik bölgeleri belirleyip <u>başına</u> <em>EnterCriticalSection </em>ve <u>sonuna</u> <em>LeaveCriticalSection </em>sonksiyonlarını koyuyoruz.</p><p
align="justify">Örneğimize dönelim ve kritik bölgeleri eklemeye başlayalım. Bizim müdahale istemediğimiz bölge, kanal fonksiyonlarındaki döngülerin her bir <u>adımıdır</u>. Yani teker teker veya ikişer ikişer ekleme yapılan kısımlarda bir adım işini bitirmeden başka bir kanalın global değişkenimize müdahalesini engellemek istiyoruz. Kısacası kritik bölgemiz bu örnekte, döngünün her bir adımı olmalıdır.</p><p
align="justify">İlk başta global olarak bir değişken daha tanımlayalım. Bu değişken bizim kritik bölgemizi temsil eden record olsun.</p><pre>
<pre class="brush: delphi">var
  Form1: TForm1;
  SonucYazi: string;
  BirinciKritikBolge: TRTLCriticalSection; //kritik bölgemizi temsil ediyor.</pre></pre><p
align="justify">Ardından formun <em>OnCreate </em>ve <em>OnDestroy </em>olaylarını aşağıdaki gibi değiştirelim.</p><pre>
<pre class="brush: delphi">procedure TForm1.FormCreate(Sender: TObject);
begin
  InitializeCriticalSection(BirinciKritikBolge);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  DeleteCriticalSection(BirinciKritikBolge);
end;</pre></pre><p
align="justify">Eğer başka kritik bölgeler de tanımlamış iseniz bunları da <em>InitializeCriticalSection </em>ve <em>DeleteCriticalSection </em>fonksiyonlarını kullanarak oluşturmalı ve silme işlemlerini gerçekleştirmelisiniz. Bizim örneğimizde bir adet kritik bölge yeterlidir.</p><p
align="justify">Şimidi kanal fonksiyonlarında bahsettiğimiz yerleri kritik bölge olarak ilan edelim.</p><pre>
<pre class="brush: delphi">procedure Kanal1;
var
  i: Integer;
begin
  for i := 0 to 10 do
  begin
    EnterCriticalSection(BirinciKritikBolge); //BirinciKritikBolge başlangıcı
    SonucYazi := SonucYazi + &#039;d&#039;;
    Sleep(10);
    SonucYazi := SonucYazi + &#039;i&#039;;
    SonucYazi := SonucYazi + &#039;y&#039;;
    SonucYazi := SonucYazi + &#039;e&#039;;
    Sleep(10);
    SonucYazi := SonucYazi + &#039;z&#039;;
    SonucYazi := SonucYazi + &#039;o&#039;;
    SonucYazi := SonucYazi + &#039;n &#039;;
    Sleep(10);
    LeaveCriticalSection(BirinciKritikBolge); //BirinciKritikBolge bitişi
  end;
end;

procedure Kanal2;
var
  i: Integer;
begin
  for i := 0 to 10 do
  begin
    EnterCriticalSection(BirinciKritikBolge); //BirinciKritikBolge başlangıcı
    SonucYazi := SonucYazi + &#039;di&#039;;
    Sleep(10);
    SonucYazi := SonucYazi + &#039;ye&#039;;
    SonucYazi := SonucYazi + &#039;zo&#039;;
    Sleep(10);
    SonucYazi := SonucYazi + &#039;n &#039;;
    LeaveCriticalSection(BirinciKritikBolge); //BirinciKritikBolge bitişi
  end;
end;</pre></pre><p
align="justify">Yapacağımız başka bir işlem kalmadı. Programı çalıştırıp deneybiliriz. Sonucun &quot;diyezon diyezon diyezon &#8230;&quot; gibi düzgün olduğunu göreceğiz. &Ccedil;ünkü Kritik bölge tanımlayarak sadece bir kanalın kritik bölgesini çalıştırmış olduk. Böylece bir kanal global <em>SonucYazi </em>değişkeni ile uğraşırken diğer kanal buna müdahale edemeyecektir.</p><p
align="justify">Burada bir adet kritik bölge tanımladık. Sizler ihtiyaca göre projelerinizde bir çok kritik bölge tanımlayabilirsiniz. Ayrıca kritik bölgelerin birbirinden <u>bağımsız </u>hareket ettiğini de aklınızdan çıkarmayın. Bazen karıştırılan nokta bu kısım olabiliyor. Mesela bu örneğimizde bir adet kritik bölge mevcut ama iki farklı yapılan iş var.</p><p
align="justify">Gördüğünüz gibi kanalları eş zamanlı olarak çalıştırmak o kadar da zor değilmiş. Tek yaptığımız bell bölgeleri kritik bölge olarak ilan etmek. Şimdi yeni bir kavramla daha tanışalım.</p><h1>Muteksler(Mutex)</h1><p
align="justify">İngilizce&#8217; de, <strong>mut</strong>ual ve <strong>ex</strong>clusive kelimelerinin birleşiminden meydana gelmiştir. Dilimizde ise müşterek ve seçkin kelimeleri bu kelimeleri karşılamaktadır. Programlama terminolojisinde ise, sadece tek işlemdeki değil <strong>birden fazla</strong> işlemdeki kanalları eş zamanlı olarak kullanılmasını sağlayan bir tekniktir.</p><div
align="justify">Muteksler, çalışma mantığı olarak kritik bölgelere benzerler fakat fazladan bir özellik olarak sadece aynı işlem yani sadece aynı uygulamada değil <strong>başka </strong>uygulamadaki kanallar ile eş zamanlı olarak çalışmayı da sağlarlar.</div><p
align="justify"><strong>Bir mutekse aynı anda sadece bir tek kanal sahip olabilir. </strong>Literatürde, bu mutekse, bir kanal sahip olduğunda, muteksin kanal tarafından &quot;<strong>tetiklendiği</strong>&quot; ya da &quot;<strong>tutulduğu</strong>&quot; söylenir. Aynı şekilde eğer kanal bu mutekse sahip deiğilse muteks &quot;<strong>serbestir</strong>&quot; ya da &quot;<strong>tetiklenmemiştir</strong>&quot; tabirleri kullanılır.</p><p
align="justify">Şu an kafanızda mutekslerin ne olduğuna dair somut bir düşünce oluşmamış olabilir. Ama bölümün sonuna kadar okuduğunuzda neyin ne olduğuna dair bir fikir oluşacağına eminim.</p><p
align="justify">Bir muteks, <em>CreateMutex </em>fonksiyonu ile oluşturulur. Bu fonksiyonun tanımlanması ve açıklaması aşağıdadır.</p><pre>
<pre class="brush: delphi">function CreateMutex(lpMutexAttributes: PSecurityAttributes;
  bInitialOwner: BOOL;
  lpName: PChar): THandle;</pre></pre><p
align="justify"><font
face="Courier New"><strong>lpMutexAttributes</strong></font>: Muteksin güvenliği ile ilgli parametredir. Bu parametreye <strong>nil </strong>girerek varsayılan güvenlik özelliklerini ayarlamış oluyoruz.</p><p
align="justify"><font
face="Courier New"><strong>bInitialOwner</strong></font>: Kanalın bu mutekse sahip olup olmadığını ya da yukarıda bahsettiğimiz gibi muteksin tetiklenip tetiklenmeyeceğini belirler. True ya da False giriyoruz. Genelde muteksi programın ana kanalında oluşturduğumuzdan, ana kanalın mutekse sahip olmasını istemeyiz. Bu yüzden genelde bu parametreye False gireriz.</p><p
align="justify"><font
face="Courier New"><strong>lpName</strong></font>: Eğer farklı işlemler bu mutekse erişecekse bu muteksimize bir isim vermemiz gerekmektedir. Buraya <strong>nil</strong> de girebilirisiniz. Bu durumda isimsiz bir muteksimiz olur.</p><p
align="justify">Bu fonksiyon eğer muteksi oluşturabilirse çıktı olarak muteksin handle numarasını dönderir. Eğer muteks oluşmamış ise <strong>0</strong> değerini çıktı olarak verir.</p><p
align="justify">Muteksleri, kritik bölgeler gibi düşünebilirsiniz. Kritik bölge oluşturmak için kullandığımız <em>InitializeCriticalSection</em> fonksiyonu yerine <strong>CreateMutex</strong> fonksiyonunu, kritik bölgenin başlangıcını belirten <em>EnterCriticalSection</em> yerine <strong>WaitForSingleObject</strong> fonksiyonunu, kritik bölgenin bitişini bildiren <em>LeaveCriticalSection</em> fonksiyonu yerine de <strong>ReleaseMutex</strong> fonksiyonunu kullanıyoruz. <strong>CloseHandle </strong>ile de CreateMutex ile oluşan muteksin handle&#8217;ını kapatmayı da unutmuyoruz.</p></p><p
align="justify">İlk önce bir muteks oluşturalım.</p><pre>
<pre class="brush: delphi">birmuteks := CreateMutex(nil, False, &#039;ornekmuteks&#039;);</pre></pre><p
align="justify">Ardından kritik bölgeyi oluşturduğumuz yerlerdeki fonksiyonları muteksin fonksiyonları ile yer değiştiriyoruz. Şimdilik sadece <em>Kanal1</em> için örnek verelim.</p><pre>
<pre class="brush: delphi">procedure Kanal1;
var
  i: Integer;
begin
  for i := 0 to 10 do
  begin
    WaitForSingleObject(birmuteks, INFINITE); //Muteksin tetiklenmesini bekle
    SonucYazi := SonucYazi + &#039;d&#039;;
    Sleep(10);
    SonucYazi := SonucYazi + &#039;i&#039;;
    SonucYazi := SonucYazi + &#039;y&#039;;
    SonucYazi := SonucYazi + &#039;e&#039;;
    Sleep(10);
    SonucYazi := SonucYazi + &#039;z&#039;;
    SonucYazi := SonucYazi + &#039;o&#039;;
    SonucYazi := SonucYazi + &#039;n &#039;;
    Sleep(10);
    ReleaseMutex(birmuteks); //Muteksi serbest bırak.
  end;
end;</pre></pre><p
align="justify">Şimdi muteksin ne olduğuna dair kafanızda bir şeyler şekillenmeye başlmamıştır. &Ccedil;ünkü kodlara baktığımızda <em>WaitForSingleObject </em>fonksiyonu  bir şeyleri bekliyor ve <em>ReleaseMutex </em>ise bir şeyi serbest bırakıyor. <em>WaitForSingleObject</em>, &quot;<strong>tek bir nesne için bekle</strong>&quot; gibi bir manası var. Esas yaptığı görev, birinci parametresinde verilen handle numarasına göre bir muteksi <u>beklemektir</u>. İkinci parametresinde ise ne kadar bekleyeceğini belirliyoruz. Bu örneğimizde <em>INFINITE </em>(sonsuz) girmekle, bir muteks tetiklenene kadar bekleyeceğini belirttik. Bu parametreye, en fazla ne kadar bekleneceğini milisaniye şeklinde girebilirsiniz.</p><p
align="justify">Muteksi anlamada genelde verilen örnek ve bence en iyi örnek bayrak yarışıdır. Bayrak yarışlarında <u>bir şeritte ancak bir</u> koşucu koşabilir. <u>Bayrağı alan koşmaya başlar</u>. Bir şeritte, bayrağa sahip <u>olmayan</u>, koşabilmek için bayrağı <u>beklemesi</u> gerekmektedir. Böyelece her bir şeritte sadece bir yarışçı koşabilir.</p><p
align="justify">Aynen bunun gibi, kanallar çalışabilmek için mutekse sahip olmaları gerekmektedir. <u>Mutekse sahip olan koşmaya yani çalışmaya başlar</u>. Mutekse sahip olmayan, muteksin kendisine gelmesini <u>bekler</u>. Bir hatırlatma olarak, önceki paragraflarda sahip olmanın tetiklemek olduğundan bahsetmiştik.</p><p
align="justify">İşte <em>WaitForSingleObject </em>ile muteksin bu kanalımıza gelmesini beklemiş oluyoruz. Ve bunu örneğimizde, <em>INFINTE </em>yani sonsuz süre kadar bekliyoruz. Ve muteksle işimiz bittiğinde diğer kanalların kullanabilmesi için muteksi <em>ReleaseMutex </em>ile serbest bırakıyoruz. Ve yine tekrar ediyorum &quot;<strong>Bir mutekse aynı anda sadece bir tek kanal sahip olabilir.</strong>&quot;</p><p
align="justify"><em>CreateMutex </em>ile oluşturduğumuz muteksin handle&#8217;nı formun <em>OnDestroy </em>olayında <em>CloseHandle </em>ile kapatmayı da unutmuyoruz. Yani kritik bölgelerde yaptığımız <em>DeleteCriticalSection </em>yerine</p><pre>
<pre class="brush: delphi">CloseHandle(birmuteks);</pre></pre><p
align="justify">gibi bir şey yazmalıyız.</p><p
align="justify">Bu örneği, makaleyi fazla uzatmasın diye, burada tüm kodlarını yazmayacağım. Muteks örnek projesini <a
href="http://www.diyezon.com/wp-content/uploads/File/post61/muteks1.rar">buradan</a> indirebilirsiniz.</p><p
align="justify">Eğer tek bir muteks değil de bir den fazla muteksi tetiklemesini bekelemek istersek ne olacak? Bu durumda <strong>WaiıtForMultipleObjects </strong>fonksiyonunu kullanacağız. <em>WaitForSingleObject </em>tek bir muteksi beklerken <em>WaitForMultipleObject </em>fonksiyonu birden fazla muteksi beklemektedir. Kullanım aynı olduğu için bununla ilgili bir örnek yapmayacağım. Bu fonksiyon ile iligli tek söyleyebileceğim şey ikinci parametrede beklediğiniz mutekslerin handle&#8217;larını dizi şeklinde girmeniz olacaktır.</p><h2>Başka İşlemlerden Mutekslere Sahip Olma</h2><p
align="justify">Dediğimiz gibi, mantık olarak mutekslerin kritik bölgelerden tek farkı, başka program ve işlemlerdeki kanallar ile ortak ve eş zamanlı çalışmaya izin vermesidir. Bunun için muteksimize bir isim vermemiz gerektiğini söylemiştik. İşte bu ismi kullanarak başka bir işlem muteksin handle numarasını alabilir. Gerisinde de <em>WaitForSingleObject </em>ve <em>ReleaseMutex </em>fonksiyonlarında bu handle numarasını kullanabiliriz. Yani mevcut bir muteksin handle numarasını alabilmek için:</p><pre>
<pre class="brush: delphi">birmuteks := OpenMutex(0, False, &#039;ornekmuteks&#039;);</pre></pre><p
align="justify">dememiz yeterli. Bunu yukarıdaki örneğimizde <em>CreateMutex </em>yerine kullanabilirsiniz. Ama unutmamanınız gereken nokta bunun ile <strong>mevcut </strong>bir muteksin handle numarasını alırız. Eğer muteks <em>CreateMutex </em>ile hiç oluşturulmamış ise bu fonksiyon <strong>0</strong> değerini dönderir.</p><p
align="justify"><em>OpenMutex </em>fonksiyonunun parametreleri <em>CreateMutex </em>ile aynı olduğundan burada fazladan bir açıklama yapmayacağım.</p><p
align="justify">Bu konuda bir örnek yapmak isterseniz yukarıda verdiğimiz örneği iki uygulamaya bölebilirsiniz. <em>Kanal1</em>&#8216;i birinci uygulamada, <em>Kanal2</em>&#8216;yi de ikinci uygulamada oluşturursunuz. Ve ikisinde de <em>OnCreate</em> olayında şöyle bir şey girersiniz:</p><pre>
<pre class="brush: delphi">var
  BirMuteks: THandle;
.....
procedure TForm1.OnCreate(....);
begin
  BirMuteks := OpenMutex(0, False, &#039;ornekmuteks&#039;);
  if BirMuteks = 0 then
    BirMuteks := CreateMutex(nil, False, &#039;ornekmuteks&#039;);</pre></pre><p
align="justify">Kısaca burada yaptığımız işlem eğer &quot;ornekmuteks&quot; isminde bir mutex henüz oluşturulmamış ise oluşturuyoruz.</p><p
align="justify">Bu şekilde iki veya daha fazla sayıda işlem tek bir muteks üzerinde kanallarını eş zamanlı olarak çalıştırabilirler. Bu örneği kendiniz yapmaya çalışın. Gerçi <a
href="http://www.diyezon.com/wp-content/uploads/File/post61/muteks2.rar">burada</a> yapılmışı var <img
src='http://www.diyezon.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> . Ama lütfen indirip incelemeden önce kendiniz yapmayı deneyin.</p><p>&#8212;</p></p><p
align="justify">İşte size iki adet yöntem. Ve ikisi de windows programlamada çok sık kullanılmaktadır. Kanalları beraber çalıştırmak istediğinizde, istrer muteks kullanın isterseniz kritik bölge tanımlayın. Her ikisinin de tanımlanması ve kullanılması çok kolay. Kafanızdaki &quot;kanal kullanmak çok zordur&quot; düşüncesini birinci bölümde aştığınızı düşünüyorum. Buraya kadar olan kısım ile de &quot;kanalları eş zamanlı çalıştırmak zordur&quot; düşüncesini yendiğinizi zannediyorum.</p><p
align="justify">Peki muteks ve kritik bölge arasında tercih yapmak gerekirse hangisini tercih etmeliyiz. Bence performans açısından pek fark olmaz. Seçim noktanız şu olmalı. Eğer, tek bir işlemden değil de bir çok işlemden yani bir çok programdan oluşan bir gurubun kanalları ortak ve eş zamanlı kullanmasını düşünüyorsak kesinlikle muteks kullanmalıyız. Aksi durumda yani eş zamanlı çalışacak olan kanallar sadece tek bir işlemde bulunuyorlarsa o zaman seçim size kalmış. Hangisi rahatınıza gidiyorsa onu kullanabilirsiniz.</p><p
align="justify">Şimdi farklı bir konuya geçiş yapalım. Hazırsanız ikinci sayfadan devem edelim.</p> ]]></content:encoded> <wfw:commentRss>http://www.diyezon.com/delphi-ile-threadkanal-kullanimi-bolum-2/feed/</wfw:commentRss> <slash:comments>19</slash:comments> </item> <item><title>Delphi ile Thread(Kanal) Kullanımı &#8211; Bölüm 1</title><link>http://www.diyezon.com/delphi-ile-threadkanal-kullanimi-bolum-1/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=delphi-ile-threadkanal-kullanimi-bolum-1</link> <comments>http://www.diyezon.com/delphi-ile-threadkanal-kullanimi-bolum-1/#comments</comments> <pubDate>Tue, 02 Oct 2007 14:27:48 +0000</pubDate> <dc:creator>Fatih Tolga Ata</dc:creator> <category><![CDATA[Delphi]]></category> <category><![CDATA[kanallar]]></category><guid
isPermaLink="false">http://www.diyezon.com/?p=58</guid> <description><![CDATA[Yaptığımız projelerin bir bölümü eninde sonunda bu konuya dayanıyor. Genelde kanal gerekli olduğu halde Timer ya da Application.ProcessMessages gibi çarelere gitmeye çalışıyoruz Ve devamında hem görsel hem de işleyiş açısından istemediğimiz sonuçlarla karşılaşıyoruz. Halbuki kanalların kullanılması sanıldığı kadar zor değildir. Bu ön yargıyı bu makalede aşmaya çalışacağız. Bu makale, ileri seviye Windows programcılığına geçiş kapısıdır. [...]]]></description> <content:encoded><![CDATA[<p
align="justify">Yaptığımız projelerin bir bölümü eninde sonunda bu konuya dayanıyor. Genelde kanal gerekli olduğu halde Timer ya da Application.ProcessMessages gibi çarelere gitmeye çalışıyoruz Ve devamında hem görsel hem de işleyiş açısından istemediğimiz sonuçlarla karşılaşıyoruz. Halbuki kanalların kullanılması sanıldığı kadar zor değildir. Bu ön yargıyı bu makalede aşmaya çalışacağız.</p><p
align="justify">Bu makale, ileri seviye Windows programcılığına geçiş kapısıdır. Bu makeledeki konuları öğrendikten sonra Win32 programcılığının en güçlü özelliklerini öğrenmiş olacaksınız.</p><p
align="justify">Yanlız &quot;ileri seviye&quot; tabiri gözünüzü korkutmasın. &Ccedil;ünkü makeleyi bitirdiğinizde thread kullanımının aslında ne kadar kolay olduğunu göreceksiniz. Bununla birlikte bu makalede bu konu ile ilgili her ayrıntıyı anlatabilmemiz için yüzlerce sayfalık kitap yazmamız gereklidir. Bu yüzden burada işin mantığını kapıp, ihtiyacınız oldukça yardım dosyalarına müracaat etmeniz gerekebilir.</p><p
align="justify">Kanallar hakkında hiç bir bilginiz olmayabilir. Bu makalede işin temelinden alacağız ve makaleyi bitirdiğinizde kendi programlarınıza kanal ekleyebilecek duruma geleceğinize inanıyorum.</p><p
align="justify">Bu makale serisinde kanallar temelden anlatılmakla beraber, ileri seviye kanal kullanımı, VCL bileşenlerinde kanal kullanımı ve Muteksler de ana konularımız arasına girecektir.</p><p><span
id="more-58"></span></p><h1>Giriş</h1><p
align="justify">İlk başta bir kaç teorik bilgi ile başlamak istiyorum. Bu bilgilieri olabildiğince sıkmadan, kısa tutmaya çalışacağım.</p><p
align="justify">Disk üzerindeki her bir dosya çalıştırıldığında artık windows için birer <strong>işlem</strong>(<em>process</em>) olurlar. Bir <strong>işlem </strong>Windows için fazla bir şey ifade etmez. &Ccedil;ünkü işlemler sadece hafızada belli bir bölgede var olmaktan sorumludur. Esas işlemi yapan kısım <strong>kanallardır</strong>(<em>thread</em>). Bir işlem en az bir adet kanala sahiptir. Win 3.1 gibi işletim sistemleri sadece bir adet kanala sahiptir. Ama Windows 95 ve üstü, Unix, OSX gibi işletim sistemleri birden fazla kanala sahip olabilirler.</p><p
align="justify">İşletim sistemi bir programı ya da bir DLL&#8217;i ilk başta işlem olarak hafızaya taşır. Bu esnada işlem eylemsiz olarak durur. Bu işleme ait kanallar ise bizim belirlediğimiz ölçüde programın kodlarını çalıştırmaya başlar.</p><div
align="justify"> Yine teorik olarak bilmemiz gereken diğer bir terim de quantum değeridir. Her kanalın windows tarafından atanmış bir işletilme süresi vardır. Ve terim olarak quantum olarak isimlendirilir. Bunun sadece tanımını bilmeniz yeterli.</div><p
align="justify">Her kanal, çalışma esnasında kendi eax, ebx, edx gibi register verilerini tutan bir yapıya sahiptir. Bu yapıya <strong>context</strong>(<em>içerik</em>) adı verilmektedir. Bu yapının Delphi&#8217;deki karşılığı <em>TContext </em>olup, Windows unitindedir. Bu record tipini incelediğinizde bir çok register&#8217;ı barındırdığını göreceksiniz. Eğer <a
href="http://www.diyezon.com/?p=45">buradaki</a> Delphi ve Assembler ile ilgili makaleyi ve <a
href="http://www.diyezon.com/?p=47">buradaki</a> fonksiyon çağırım makenizmalarını okudu iseniz şimdi anlatacaklarımız size tanıdık gelecektir. &Ccedil;ünkü kanalların işleyiş mantığı fonksiyon çağırımlarına benzemektedir. Ama devam edebilmek için bunları okumak zorunda değilsiniz.</p><p
align="justify">Her bir kanal sahip olduğu önceliğe göre işletim sistemi tarafından işleme alınacaktır. Bu işleme alış esnasında sahip olduğu <em>context</em>&#8216;deki register değerleri yüklenir. Esp, Ebp, Eip gibi registerlar da yüklendiğinden, kanal önceden kaldığı yerden önceki stack değerlerine göre işlemine devam edecektir. Ve yine önceliğine göre belli bir süre sonunda işletim sistemi bu kanalı durdurup register değerlerini <em>context </em>yapısında saklar. Daha sonra diğer bir kanalın context verisini yükleyerek başka bir kanalı işlemeye geçer. Ve hakeza bu işlem tüm kanallar için devam edip gider. İşletilme ve geçiş süreleri çok kısa olduğundan, biz sanki aynı anda tüm kanallar işletiliyormuş gibi zannederiz. Halbuki bütün kanallar belli bir sıra ile çok kısa sürelerde işletilip diğer bir kanala geçilmektedir. Yani her kanal aynı anda çalışmaz, biz öyle zannederiz. Tabi bu anlattıklarımız tek işlemci ve tek çekirdek için geçerlidir. Kanalların işletilme sürelerini ise kanalların <strong>önceliği </strong>belirlemektedir. Hangi kanal daha yüksek öncelikli ise o kanal daha fazla işletilme süresine sahip olur. Eğer kanal arkaplanda çalışma üzere ayarlanmış ise, diğer kanallar işlemlerini tamamladıktan sonra ancak bu kanala sıra gelecektir. Eğer kanal çok yüksek önceliğe sahip ise uzun bir süre bu kanal işleme devam edecek ve diğer kanallara sıra gelmeyecektir.</p><p
align="justify">Demek ki, aynı anda bir kaç işlem yapmak istiyorsak kafa patlatıp, parmak yormamıza gerek yokmuş. &Ccedil;ünkü işletim sistemi zaten sizin için bu sistemi kurmuştur. Sadece biraz vaktiniz ayırmanız ve kanallar nasıl oluşturuluyor öğrenmeniz yeterlidir.</p></p><h1>Kanal Oluşturma Yöntemleri</h1><p
align="justify">Aslında böyle bir başlıkla konuya giriş yapmak istemezdim. Ama kanal oluşturmayı öğrenmeden önce bir kaç şeyi açığa kavuşturmak istiyorum. Gerek forumlardan gerekse haber guruplarından kanallarla ilgili gelen sorulardan bir çoğu kanalların yanlış oluşturulması üzerine kaynaklanan yanlışlıklardan gelmektedir.</p><p
align="justify">Normalde bir kanal windows ortamında <em>CreateThread</em> Windows API&#8217;si ile oluşturulur. Fakat VCL bize <em>TThread </em>isminde bir sınıf da sunmuştur. Kullanım açısından ayırt edebileceğiniz temel nokta şudur. Eğer kanalda yapacağınız işlemler VCL sınıflarını etkilemesi gerekiyorsa <em>TThread </em>sınıfını kanal oluşturmak için kullanıyoruz. Yok eğer VCL sınıflarını ilgilendiren bir işlem söz konusu değilse <em>CreateThread</em> API&#8217;sini kullanıyoruz. Bunun neden böyle olduğunu ileride VCL ve Kanal kullanımı ile alakalı başlıkda vereceğim. Muhtemelen Bölüm 2&#8242;de göreceksiniz. Bu yüzden biraz sabır.</p><p
align="justify">Başlarken bu şekilde bir giriş yapmamın sebibi buradan kaynaklanıyor. Bir çok kişi belki de Windows API&#8217;lerine karşı bir soğukluktan dolayı <em>CreateThread </em>ile uğraşmak istemiyor ve <em>CreateThread</em> ile ilgili kısımları gözü kapalı atlayabiliyor. Bu yüzden eksik kanal bilgisi ile ileride bir çok sorunla karşılaşabiliyor. Ama şimdi göreceğimiz gibi kanal oluşturma işlemi sanıldığı kadar zor ve karmaşık bir işlem değil.</p><h1>Bir Kanal Oluşturalım</h1><p
align="justify">Kanal oluşturma ile sorumlu Windows fonksiyonu <em>CreateThread </em>fonksiyonudur ve aşağıdaki gibi tanımlanmıştır:</p><pre>
<pre class="brush: delphi">
function CreateThread(lpThreadAttributes: Pointer;
  dwStackSize: DWORD; lpStartAddress: TFNThreadStartRoutine;
  lpParameter: Pointer; dwCreationFlags: DWORD; var lpThreadId: DWORD): THandle; stdcall;
</pre></pre><p
align="justify"> Bu fonksiyonun parametrelerinin kısaca açıklamasını verelim.</p><p
align="justify"><font
face="Courier New"><strong>lpThreadAttributes</strong></font>: Kanalımıza atanacak olan bir çok güvenlik özelliğini belirtmemize yarar. Eğer nil girerseniz varsayılan güvenlik özellikleri kullanılacaktır. Win9x ve WinMe için bu değer <font
face="Courier New"><strong>nil</strong></font> olmalıdır. Bu konu ile alakalı daha fazla bilgi almak için sdk yardım dosyalarından <em>SECURITY_ATTRIBUTES</em> başlığına müracaat edebilirsiniz.</p><p
align="justify"><font
face="Courier New"><strong>dwStackSize</strong></font>: Eğer kanal için özel bir stack büyüklüğü belirlemeyecekseniz bu değeri <strong>0</strong> olarak girmelisiniz. Böylelikle kanalın stack büyüklüğü program ile aynı olacaktır. Gerektiğinde stack boyutu otomatik olarak genişletilebilir.</p><p
align="justify"><font
face="Courier New"><strong>lpStartAddress</strong></font>: Bu parametre esas işi gören parametredir. &Ccedil;ünkü kanal çalışmaya bu parametredeki fonksiyon işaretçisi ile beraber başlar. Burada tek yapmamız gereken fonksiyonun isminin önüne <strong>@</strong> işareti vererek girmektir.</p><p
align="justify"><font
face="Courier New"><strong>lpParameter</strong></font>: Bir önceki parametrede girdiğimiz kanal fonksiyonuna parametre yollamak istiyorsanız, bu parametrelerin içinde bulunduğu bir record&#8217;un adresini buraya girmelisiniz. Ayrıca record haricinde karakter veya sayı değelerini de parametre olarak yollayabiliriz.</p><p
align="justify"><font
face="Courier New"><strong>dwCreationFlags</strong></font>: Bu parametre ile kanalın oluşturulur oluşturulmaz çalışıp çalışmayacağını belirliyoruz. Eğer bu parametreye <em>CREATE_SUSPENDED</em> girerseniz, kanal oluşturulur fakat kendisine çalışmaya başlaması için izin verilmez. Bu durumda siz <em>ResumeThread</em> fonksiyonu ile bu kanala çalışma talimatı vermedikçe kanal o şekilde çalışmadan duracaktır. Bir kez <em>ResumeThread</em> ile çalışmaya başladıktan sonra <em>SuspendThread</em> ile tekrar kanalı durdurabilirsiniz. Eğer bu parametreye <strong>0</strong> girerseniz, kanal oluşturulur oluşturulmaz çalışmak için sıraya girecektir.</p><p
align="justify"><font
face="Courier New"><strong>lpThreadId</strong></font>: Bu parametreye kanal kimlik ID&#8217;si atanır. Buraya değeri olmayan bir Dword değişken giriyoruz. Değişken gireceğiz çünkü &quot;<strong>var</strong>&quot; olarak tanımlanmış. &Ccedil;ünkü kanal oluşturulduktan sonra Windows NT,2000 ve üstü işletim sistemlerinde kanalın ID&#8217;si bu değişkene yüklenir. Windows 9x/Me işletim sistemlerinde bu değer daima 0&#8242;dır.</p><p
align="justify">Şimdi gelin bu fonksiyonu kullanarak bir kanal oluşturalım.</p><pre>
<pre class="brush: delphi">var
  KanalID: DWORD;
begin
  CreateThread(nil, 0, @KanalFonksiyonu, nil, 0, KanalID);</pre></pre><p
align="justify">İşte hepsi bu! Tek yaptığımız sadece gerekli parametreleri kullanmak, gerisini duruma göre <strong>nil</strong> veya <strong>0</strong> yapmak. Bu fonskiyon çağrıldığında <em>KanalFonksiyonu </em>isimli fonksiyonumuz kanal içinde çalışmaya başlayacaktır.</p><p
align="justify">Dilerseniz <em>KanalFonksiyonu </em>isimli fonksiyonumuzun tanımlamasını şimdi yapacağımız örnek içinde verelim.</p><h2> Basit Bir Örnek</h2><p
align="justify">Şimdi oluşturduğumuz bu kanalın nasıl çalıştığını basit bir örnek ile görmeye çalışalım. Yeni bir uygulama oluşturalım ve Form üzerine bir adet button ve bir adet label koyalım. Button&#8217;nun <em>Caption </em>özelliğini &quot;Oluştur ve &Ccedil;alıştır&quot; olarak değiştirelim. Ve Button&#8217;nun <em>OnClick </em>olayına kanalımızı oluşturan şu kodları girelim:</p><pre>
<pre class="brush: delphi">procedure TForm1.Button1Click(Sender: TObject);
var
  KimlikID: DWORD;
begin
  CreateThread(nil, 0, @KanalFonksiyonu, nil, 0, KimlikID);
end;</pre></pre><p
align="justify">Şimdi de kanalımızda esas işi yapan <em>KanalFonksiyonu </em>isimli fonksiyonumuzun tanımlamasını girelim:</p><pre>
<pre class="brush: delphi">function KanalFonksiyonu(P: Pointer): Longint; stdcall;

...

implementation

...

function KanalFonksiyonu(P: Pointer): Longint; stdcall;
var
  i: Integer;
  Toplam: Int64;
begin
  Toplam := 0;
  for i := 0 to 1000000000 do
  begin
    Toplam := Toplam + i;
    Toplam := Toplam shl 4;
  end;
  Form1.Label1.Caption := &#039;Kanal işlemini tamamladı. Sonuç:&#039; + IntToStr(Toplam);
end;</pre></pre><div
align="justify"><p>Kanal fonksiyonumuzun tanımlamasını istediğimiz gibi yapabilirdik. Burada mesela <font
face="Courier New">P: Pointer</font> parametresine ihtiyaç yoktu. Ayrıca <em>Longint </em>çıktısını vermeye de ihtiyacımız yok. <em>CreateThread</em> fonksiyonunun parametre açıklamalarını hatırlarsanız, kanal fonksiyonumuza belli parametreler yollayabiliyorduk. İşte bu parametreleri pointer olarak buradan alıyoruz. Ama bu meseleye sonra geçeceğiz. Ayrıca fonksiyon tanımlamasındaki <em>stdcall </em>ifadesine yabancı iseniz <a
href="http://www.diyezon.com/?p=47">buradaki </a>fonksiyon çağırım makenizmaları ile ilgili makaleyi okuyabilirsiniz.</p></div><p
align="justify">Bu fonksiyonda ekstra bir şey yapmıyoruz. Sadece bir for döngüsü var ve matematiksel bir işlem yapılıyor. For döngüsü bittiğinde ise formdaki label&#8217;a bir sonuç yazdırıyoruz.</p><p
align="justify">Bu for döngüsünü, ayrı bir kanalda değil de programın ana kanalında çalıştırmaya kalksaydık muhtemelen <em>Application.ProcessMessages</em> ve <em>TTimer</em> gibi çözümlere gidecektik. Bu durum da çok fazla miktarda performans düşüklüğüne sebep olacaktı. Ayrıca işlemlerimiz genelde buradaki gibi basit matematiksel işlemler olmadığından, programda çökmelere ve donmalara sebep olacaktık.</p><p
align="justify">Bu programı çalıştıralım ama button&#8217;a basmayalım. Eğer açık değilse View menüsünden &quot;Thread Status&quot; penceresini ve &quot;Event Log&quot; penceresini açalım ve görebileceğimiz bir köşeye yerleştirelim. Eğer BDS 2005 ve üstü kullanıyorsanız bu pencereler debug esnasında altta görünür biçimde hazır olacaklardır.</p><p
align="justify">Ardından buttona bir kez basalım ve &quot;Thread Status&quot; penceresine yeni bir eleman eklendiğine dikkat edelim. Eğer buradaki eleman çok çabuk bir şekilde görünüp kayboldu ise işlemciniz benimkinden hızlı demektir <img
src='http://www.diyezon.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> . Bu yüzden for döngüsündeki limit değeri biraz artırıp tekrar çalıştıralım.</p><p
align="justify">Button&#8217;a her tıkladığınızda &quot;Thread Status&quot; pencersinden de takip edebileceğiniz gibi yeni bir kanal eklenecek. Tabi butona basma işini o kadar çok abartmayın. Her kanal oluşturulduğunda kendisine bir ID atanacak. Örneğimizde bu değer <em>KimlikID</em> değişkeninde tutuluyor. &quot;Thread Status&quot; pencersinde ise ThreadID sütununda bu değeri görebiliriz. Her kanal işlemini bitirdiğinde &quot;Thread Status&quot; pencersinden kaybolduğunu göreceksiniz. Aynı şekilde &quot;Event Log&quot; pencersinde de kanal&#8217;ın ID&#8217;sine göre başlama ve bitişi ile ilgili mesajları görebilirsiniz.</p><p
align="justify">Kanal çalışırken program üzerinde istediğiniz işleme devam edebilirsiniz. Formu taşıyabilir, boyutlandırabilir, başka bileşen ve nesneler var ise onları kullanabilirsiniz. Ve bunu yaparken bir yandan kanal(lar) da çalışmaktadır. Aynı kanal fonksiyonunu doğrudan çağırmaya kalktığımızda formumuza döngü bitene kadar müdahale edemeyecektik.</p><p
align="justify">Gördüğünüz gibi bir kaç satır kod ile kanal oluşturuveriyoruz. Tek bilmemiz gereken kısım <em>CreateThread</em> fonksiyonunun parametreleri. Bunların çoğunu da kullanmadık. Ama makalenin ilerleyen kısımlarında bunları da yavaş yavaş kullanıma dahil edeceğiz. İşte bir tanesi geliyor.</p><h1> Kanal Fonksiyonlarına Parametre Gönderimi</h1><p><em>CreateThread</em> fonksiyonunun parametrelerini açıklarken <em>lpParameter</em>&#8216;den söz etmiştik ve bu parametre ile kanal fonksiyonuna istediğimiz parametreyi gönderebileceğimizi belirtmiştik. Şimdi bunu nasıl yapacağımızı görelim.</p><p>Bunun için yukarıda yaptğımız örnekten faydalanacağız. İlk başta <em>type </em>bloğunda aşağıdaki record tanımlamasını yapalım.</p><pre>
<pre class="brush: delphi">
  PKanalParametresi = ^TKanalParametresi;
  TKanalParametresi = record
    BirParametre: string;
  end;
</pre></pre><p> Ardından kanal fonksiyonumuzu aşağıdaki gibi değiştirelim.</p><pre>
<pre class="brush: delphi">function KanalFonksiyonu(P: Pointer): Longint; stdcall;
var
  Parametreler: PKanalParametresi;
begin
  Parametreler := PKanalParametresi(P); //Parametremizi alalım.
  while True do
  begin
    Form1.Label1.Caption := Form1.Label1.Caption + Parametreler^.BirParametre;
    Sleep(1000); //İşlemcinin %99 çalışmasını istemiyiz değil mi....
  end;
  Dispose(Parametreler);
end;</pre></pre><p>En son olarak button&#8217;nun <em>OnClick </em>olayını da aşağıdaki gibi olacak şekilde değiştirelim.</p><pre>
<pre class="brush: delphi">procedure TForm1.Button1Click(Sender: TObject);
var
  KimlikID: DWORD;
  Parametreler: PKanalParametresi;
begin
  New(Parametreler);
  Parametreler^.BirParametre := &#039;a&#039;;
  CreateThread(nil, 0, @KanalFonksiyonu, Parametreler, 0, KimlikID);
end;</pre></pre><p
align="justify">Aslında çok fazla bir şey yapmadık(en azından siz kopyala yapıştır yaptınız:) ). Burada basit bir işaretçi kullanımını görüyoruz. Ve parametre olarak bir record&#8217;u kullandık. Record yerine başka veri tiplerini de kullanabilirdik. Ama genelde karşılaşılan sorunlar recordları parametre olarak yollamaktan gelmektedir.</p><p
align="justify">En başta verdiğimiz record tanımlamasına bakarsanız, <em>PKanalParametresi</em> isminde bu recordumuza <u>işaretçi</u> olan bir tip göreceksiniz. Parametremizi işaretçi olarak kullanacağımızdan bu tipe ihtiyacımız olacak. Ardından Button&#8217;nun <em>OnClick</em> olayına bakalım. Burada önceki örneğe göre fazladan 2 satır ekledik. İlk başta <em>var</em> bloğunda <em>Parametreler</em> isminde bir record işaretçisi tanımladık. Ardından işaretçi hiç bir şey ifade etmediğinden, işaretçiye ait bir recordun hafızada oluşturulması için <em>New</em> rutinini kullandık. Devamında bu record&#8217;un değişkenlerinden birine bir <em>string </em>değer atadık. <em>CreateThread</em> fonksiyonu parametreyi işaretçi olarak istediğinden buraya direk olarak <em>Parametreler</em> değişkenimizi girebiliriz. &Ccedil;ünkü bu değişken bir record işaretçsidir.</p><p
align="justify">En son olarak yeni kanal fonksiyonumuza baktığımızda, işaretçi olarak gelen parametremizin nasıl kullanıldığını görüyoruz. Burada bizim için önemli olan kısım <em>Dispose</em> kısmıdır. &Ccedil;ünkü burada <em>New</em> ile oluşturduğumuz ve işaretçiye ait olan recordu hafızadan siliyoruz.</p><p
align="justify">Record işaretçisi yerine normal record kullansa idik ve sonra da bunu parametre olarak geçirirken pointer olarak geçirse idik olmazmıydı. Olurdu&#8230; &Uuml;stelik <em>New</em> ve <em>Dispose</em> rutinlerini de kullanmak zorunda kalmazdık. Ama, bu durumda boş yere hafızda yer işgal edecektik. &Ccedil;ünkü parametre recordu sadece bir kerelik işimize yarayacak. Bu yüzden işimiz bitince <em>Dispose</em> ile hafızdan siliyoruz.</p><p
align="justify">Bu örneğimizi de çalıştırıp deneyebilirsiniz. Ayrıca Görev Yöneticisi ile işlemciyi yüzde kaç kullandığını da görebilirsiniz. Tabi burada <em>Sleep</em> rutinini kullanmak yerine daha başka yapılabilecek şeyler var ama şimdilik kafa karıştırmamak için bunları sonraya bırakıyorum.</p><h1>Sonuç</h1><p
align="justify">Bu bölümü burada noktalayalım. Gelecek bölümde kanalların nasıl birbirleri ile birlikte çalışabileceğini göreceğiz. Ayrıca Mutex&#8217;ler ve VCL ile kanal kullanımı da gelecek bölümde göreceğimiz konular arasında olacak. Böylece kanallar konusunda hiç bir şüpheniz, korkunuz kalmayacağı gibi programlarınıza da profesyonellik katacaksınız.</p><p
align="justify">Gelecek bölümde buluşmak ümidi ile&#8230; Yorumlarınızı ve eleştirilerinizi bekliyorum. Yine her zaman ki gibi sorularınızı buradan yada Delphi Türkiye <a
href="http://www.delphiturkiye.com/forum">forum</a>larından iletebilirsiniz</p><p
align="justify">Fatih Tolga Ata &copy; 2007</p><h2>Kaynaklar</h2><ul><li>Windows SDK Yardım Dosyası</li><li>Delphi 4 Unleashed, Charlie Calvert, Sams Pub., 1999</li></ul> ]]></content:encoded> <wfw:commentRss>http://www.diyezon.com/delphi-ile-threadkanal-kullanimi-bolum-1/feed/</wfw:commentRss> <slash:comments>22</slash:comments> </item> <item><title>Enine Boyuna Generics &#8211; Bölüm 2</title><link>http://www.diyezon.com/enine-boyuna-generics-bolum-2/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=enine-boyuna-generics-bolum-2</link> <comments>http://www.diyezon.com/enine-boyuna-generics-bolum-2/#comments</comments> <pubDate>Sun, 23 Sep 2007 20:24:43 +0000</pubDate> <dc:creator>Fatih Tolga Ata</dc:creator> <category><![CDATA[Delphi]]></category> <category><![CDATA[.net]]></category> <category><![CDATA[.net 2.0]]></category> <category><![CDATA[delphi.net]]></category> <category><![CDATA[generics]]></category> <category><![CDATA[sınıf]]></category><guid
isPermaLink="false">http://www.diyezon.com/?p=55</guid> <description><![CDATA[Bir önceki bölümde Generics hakkında bazı terminolojiler üzerinde durduk. Ayrıca ilk başta verdiğimiz örnek ile, bir sınıfı veya bir metodu object yaklaşımı ile nasıl genlleştirilebileceğini gördük. Önceki makaleyi okuduktan sonra objcet yaklaşımı ile Generics arasındaki farkı idrak ettiğinize inanıyorum. Ve Generics&#8217;in avantajlarını ve kolaylığını anladığınızı düşünüyorum. Bu bölümde Sınırlandırıcıları(Constraints) göstermeye çalışacağız. Hazırsanız buyrun. Sınırlandırıcılar(Kısıtlamalar, Constraints) [...]]]></description> <content:encoded><![CDATA[<p
align="justify">Bir önceki bölümde Generics hakkında bazı terminolojiler üzerinde durduk. Ayrıca ilk başta verdiğimiz örnek ile, bir sınıfı veya bir metodu object yaklaşımı ile nasıl genlleştirilebileceğini gördük. Önceki makaleyi okuduktan sonra objcet yaklaşımı ile Generics arasındaki farkı idrak ettiğinize inanıyorum. Ve Generics&#8217;in avantajlarını ve kolaylığını anladığınızı düşünüyorum.</p><p
align="justify">Bu bölümde Sınırlandırıcıları(Constraints) göstermeye çalışacağız. Hazırsanız buyrun.</p><p><span
id="more-55"></span></p><h1>Sınırlandırıcılar(Kısıtlamalar, Constraints)</h1><p
align="justify">Öyle olurki bazen, tip argümanlarınızın belli metodları ve özellikleri içermesini isteyebilrsiniz. Mesela çizim yapma özelliği olan sınıfların kolleksiyonunu tutan bir generic sınıf düşünün. İsmi de TCizebilenKolleksiyon gibi bir şey olsun. Bu generic kolleksiyona atayacağınız her elemanın bir çizim gerçekleştiren &#8220;Ciz&#8221; gibi bir metodunun olmasını isteyebilirsiniz. Bu durumda, tip parametresini belli arayüzler ile sınırlandırmanız gerekmektedir. Bu örneğimizi koda dökecek olursak, arayüzümüz şöyle bir şey olacak:</p><pre>
<pre class="brush: delphi">ICizebilen = interface
public
  procedure Ciz;
end;</pre></pre><p
align="justify">Bu arayüz ile sınırlandırılmış olan kolleksiyon sınıfımız da şöyle bir şey olabilir:</p><pre>
<pre class="brush: delphi">TCizebilenKolleksiyon&lt;T: ICizebilen&gt; = class
...
end;</pre></pre><p
align="justify">Bu durumda T parametresine atayacağımız her argümanın ICizebilen arayüzünün sahip olduğu özellik ve metodlara sahip olacağını garantilemiş oluyoruz.</p><p
align="justify">.Net ortamında kullanabilceğiniz bir çok arayüz bulunmaktadır. Bu arayüzler genelde &#8220;able&#8221; son eki ile biterler. &#8220;able&#8221; ekinin Türkçe karşılığı &#8220;-ebilen&#8221;, &#8220;-abilen&#8221; olarak düşünebilirsiniz.. ISerializable, IComparable, ICloneable, vs&#8230;</p><p
align="justify">Tip parametreleri birden fazla arayüz sınırlandırıcısı alabilir. Bu durumda tip parametreleri virgül yerine aynen prosedür ve fonksiyonların parametrelerinde olduğu gibi &#8220;;&#8221; noktalı virgül ile ayrılırlar. Mesela:</p><pre>
<pre class="brush: delphi">TSinif&lt;T: ISerializable; U: IColoneable&gt; = class</pre></pre><p
align="justify">gibi&#8230; Bu örneğe göre T parametresi ISerializable arayüzünü, U parametresi de IColoneable arayüzünü desteklemesi şarttır.</p><p
align="justify">Ayrıca, birden fazla sınırlandırıcı, tek bir tip parametresine atanabilir. Bu durumda bunlar, &#8220;AND&#8221; lojik mantığına göre tip parametresini sınırlandırırlar.</p><pre>
<pre class="brush: delphi">TSinif&lt;T: ISerializable, IColoneable&gt; = class</pre></pre><p
align="justify">gibi&#8230; Bu örnekte T parametresi hem ISerializable, hem de IColoneable arayüzlerini desteklemek zorundadır. Aynı şekilde prosedür ve fonksiyon parametrelerinde olduğu gibi aynı sınırlandırmaya sahip iki tip parametresini virgül ile ayrırak beraber yazabilirsiniz:</p><pre>
<pre class="brush: delphi">TSinif&lt;T, U: ISerializable&gt; = class</pre></pre><p
align="justify">gibi&#8230; Bu örneğe sınırlandırıcıya sahip olmayan bir parametre daha ekleseydik noktalı virgül ile ayırmamız gerekecekti:</p><pre>
<pre class="brush: delphi">TSinif&lt;T, U: ISerializable; Z&gt; = class</pre></pre><p
align="justify">gibi&#8230;</p><p
align="justify">Bunlar grammer ile ilgili kısımlardı. Peki derleyicinin davranışı nasıl olacak? Aşağıdaki örneğe bakalım:</p><pre>
<pre class="brush: delphi">TPrintableCollect&lt;T: IPrintable&gt; = class ...
...
TDeneme = class(IPrintable)
  procedure Print;
end;

THata = class
end;
...
var
  Calisan: TPrintableCollect&lt;TDeneme&gt;;
  Hatali: TPrintableCollect&lt;THata&gt;; //Syntax hatası: THata sınıfı IPrintable arayüzünü desteklemiyor.</pre></pre><p
align="justify">Gördüğünüzü gibi THata sınıfı IPrintable arayüzüne yabancı olduğu için tip argümanı olarak atadığımızda syntax hatası alıyoruz.</p><p
align="justify">Bununla beraber aynı mantık ile arayüz yerine bir sınıfı sınırlandırıcı olarak kullanabilirsiniz. Bu durumda generic sınıfımız ancak ve ancak sınırlayıcı olarak verdiğimiz sınıf ve alt sınıflarından tipleri alabilir. Mesela:</p><pre>
<pre class="brush: delphi">TBirSinif = class...
TBirAltSinif = class(TBirSinif)...
TBirKolleksiyon&lt;T: TBirSinif&gt; = class...</pre></pre><p
align="justify">Bu örneğimizde T tip parametresine ancak ve ancak TBirSinif ve ondan türeyen sınıflar argüman olarak atanabilir.</p><h2>Sınırlandırıcılarda Belirsizlik</h2><p
align="justify">İki adet sınırlandırıcı, bir tip parametresine atandığını düşünelim. Mesela:</p><pre>
<pre class="brush: delphi">TTest&lt;T: ISerializable, ICloneable&gt; = class
  BirNesne: T;
  procedure Klonla;
end;

procedure TTest&lt;T&gt;.Klonla;
begin
  BirNesne.Clone;
end;</pre></pre><p
align="justify">Derleyici &#8220;BirNesne&#8221; değişkeninin ISerializable ve ICloneable arayüzlerini desteklediğini biliyor. Ayrıca IClonable arayüzünde de Clone isminde bir metod olduğunu da biliyor. Bu yüzden Klonla metodunda Clone metodunu çağırırken herhangi bir çevirim işlemi yapmadık. Çünkü derleyici, Clone metodunun hangi arayüze ait olduğunu biliyor.</p><p
align="justify">Farz edelim ki Clone metodu, hem ISerializable hem de ICloneable arayüzlerinde bulunuyor olsun. Bu durumda Clone metodunu yukarıdaki gibi çağırdığımızda ne olacak? Derleyici bu durumda belirsizilik olduğuna dair bir hata mesajı gösterecektir. Bu belirsizliği ortadan kaldırmak için harici çevirim(explicit casting) yapmalıyız. Yani:</p><pre>
<pre class="brush: delphi">procedure TTest&lt;T&gt;.Klonla;
begin
  ISerializable(BirNesne).Clone;
  ICloneable(BirNesne).Clone;
end;</pre></pre><p
align="justify">Bu durumda, ilk Clone çağırımı ISerializable arayüzü üzerinden, ikincisi ise ICloneable arayüzü üzerinden gerçekleşecektir.</p><h2>Sınırlandırıcı Olarak constructor, class ve record Kullanmak</h2><p
align="justify">Eğer sınırlandırıcı olarak record gibi referansız tipler kullanacaksak bu durumda &#8220;record&#8221; ayrılmış kelimesini kullanıyoruz. Ama dikkat etmemiz gereken konu kullanacağımız tip argümanı nullable yani null değer alabilen bir tipten olmamalı. Mesela string nullable&#8217;dır ama Integer değildir.</p><p
align="justify">Aynı şekilde eğer sınırlandırıcımız sadece referanslı tipler olacaksa bu sefer de &#8220;class&#8221; ayrılmış kelimesini kullanmaktayız. Ayrıca kullanacağımız tip nullable olabilmeli. Yani tip argümanı olarak sınıfların yaninda string gibi nullable tipleri de kullanabiliriz. Bunları şu şekilde örneklendirelim:</p><pre>
<pre class="brush: delphi">TReferansiz&lt;T: record&gt; = class...
TReferansli&lt;T: class&gt; = class...
TBirRecord = record
end;

var
  Hatali1: TReferansiz&lt;string&gt;; //Hatalı, çünkü string nullable,
  Hatali2: TReferansli&lt;Integer&gt;; //Hatalı, Integer referanslı bir tip değil
  Uygun1: TReferansiz&lt;TBirRecord&gt;;
  Uygun2: TReferansiz&lt;Integer&gt;;
  Uygun3: TReferansli&lt;string&gt;; //string nullable bu yüzden uygun.
</pre></pre><p
align="justify">gibi&#8230;</p><p
align="justify">Constructor kelimse ise, sınırlandırıcıda kullanılan sınıfın mutlaka parametresiz bir constructor&#8217;a sahip olmasını şart koşmaktadır. Mesela:</p><pre>
<pre class="brush: delphi">TBirSinif = class
  constructor Create;
end;

TBirBaskaSinif = class

end;

TBirKolleksiyon&lt;T: constructor&gt; = class...
...

var
  Uygun: TBirKolleksiyon&lt;TBirSinif&gt;;
  Hatali: TBirKolleksiyon&lt;TBirBaskaSinif&gt;;</pre></pre><p
align="justify">Bu örnekte Uygun nesnesinde tip argümanı olarak TBirSinif tipini girdik. Ve derleyici bunu kabul etti. Çünkü TBirSinif parametresiz bir constructor&#8217;a sahip ve sınırlandırıcımız için uygun. Fakat Hatali nesnesinde tip argümanımıza girilen TBirBaskaSinif tipi parametresiz bir constructor&#8217;a sahip değil. Bu yüzden derleyici burada hata gösterecektir.</p><h1>Sonuç</h1><p
align="justify">Normalde Delphi.NET derleyicisi, generics ve .net 2.0&#8242;a tam destek vermekle beraber, bir çok bug da içeriyor. Gerçi bu cümleleri yazdığım sıralarda, RAD Studio 2007 daha 1 haftadır piyasada. Şu an bildiğim kadarı ile Rad Studio 2007&#8242;in çıkacak olan update&#8217;inde büyük çoğunlukla derleyici ve asp.net&#8217;deki düzeltmeler bulunacak. Derleyici düzeltmelerinde de pastanın büyük payını generics ile ilgili hatalar alacak. Bu yüzden generics ile uğraşırken &#8220;Internal Error XXXX&#8221; gibi hatalar ile karşılaşırsanız kafanızı duvarlara vurmayın. Çünkü internal error&#8217;lerin hepsi bug olarak kabul edilir. Böyle bir durumla karşılaştığınızda eğer yoksa QC&#8217;ye rapor edebilirsiniz.</p><p
align="justify">Fatih Tolga Ata © 2007</p><h2>Kaynaklar:</h2><ul><li>CodeGear&#8217;ın Delphi Parametrize Tipler ile İlgili Taslak Metinleri</li><li>.NET 2.0 SDK Yardım Dosyası</li><li>Microsoft Visual C# 2005 Step by Step, John SHARP, 2005 Edition</li></ul><p
align="justify"> ]]></content:encoded> <wfw:commentRss>http://www.diyezon.com/enine-boyuna-generics-bolum-2/feed/</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>Enine Boyuna Generics &#8211; Bölüm 1</title><link>http://www.diyezon.com/enine-boyuna-generics-bolum-1/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=enine-boyuna-generics-bolum-1</link> <comments>http://www.diyezon.com/enine-boyuna-generics-bolum-1/#comments</comments> <pubDate>Tue, 18 Sep 2007 03:58:02 +0000</pubDate> <dc:creator>Fatih Tolga Ata</dc:creator> <category><![CDATA[Delphi]]></category> <category><![CDATA[.net]]></category> <category><![CDATA[.net 2.0]]></category> <category><![CDATA[generics]]></category> <category><![CDATA[nesne]]></category> <category><![CDATA[sınıf]]></category><guid
isPermaLink="false">http://www.diyezon.com/?p=54</guid> <description><![CDATA[Bir iki önceki makalemizde, Delphi gramerine katılan generics ve parametrize tipleri yüzeysel olarak tanıtmıştık. Generics, RAD Studio 2007&#8242; nin çıkması ile artık sadece beta tester&#8217;larını değil, tüm Delphi programcılarını ilgilendiren bir konu olmuştur. Gerçi şu an için sadece .NET ortamında kullanabildiğimiz bu özellik bir sonraki sürümde Win32 için de kullanılabilir bir gramer olacaktır. Başlangıç için [...]]]></description> <content:encoded><![CDATA[<p
align="justify">Bir iki önceki <a
href="http://www.diyezon.com/?p=48">makalemizde</a>, Delphi gramerine katılan <em>generics</em> ve <em>parametrize tipleri</em> yüzeysel olarak tanıtmıştık. Generics, RAD Studio 2007&#8242; nin çıkması ile artık sadece beta tester&#8217;larını değil, tüm Delphi programcılarını ilgilendiren bir konu olmuştur. Gerçi şu an için sadece .NET ortamında kullanabildiğimiz bu özellik bir sonraki sürümde Win32 için de kullanılabilir bir gramer olacaktır. Başlangıç için <a
href="http://www.diyezon.com/?p=48">önceki makalemizi</a> okuyabilir ya da direk buradan başlayabilirsiniz. Zira bu bölümde Generics ile ilgili temel bilgiler vereceğiz.</p><p><span
id="more-54"></span></p><h1>Giriş</h1><p
align="justify">Generics&#8217;i doğru bir şekilde anlayabilmek için, hangi problemleri çözmek için dizayn edildiğini anlamakta yarar var. Bunun için ilk başta temel bir konudan ve bununla ilgili bir problemden bahsedelim.</p><p
align="justify">.Net ortamı için <em>object</em> tipini herhangi bir değer ya da değişken için referans olarak kullanabilirsiniz. Çünkü her bir referans tipi otomatik olarak <em>System.Object</em> sınıfından türetiilir. Delphi&#8217;nin özel tipleri bile .net ortamına geçtiğinde artık birer <em>object </em>olacaktır. Fakat bazı durumlarda <em>explicit casting</em> yapmadan <em>object </em>sınıfına atama yapmak derleyicinin hata üretmesine neden olabilir (Explicit ve Implicit terimlerine yabancı iseniz <a
href="http://www.diyezon.com/?p=52">buradaki makalede</a> ilgili başlığı bulabilirsiniz.). Bunun için <span
style="font-family: Courier New;">AUTOBOX</span> direktifini kullanmamız gerekebilir. Yani aşağıdaki gibi, Delphi&#8217;ye ait özel bir tipi, .NET&#8217;in <em>object</em> tipine <em>implicit </em>olarak atama yaptığımızda derleyici hata üretecektir.</p><pre>
<pre class="brush: delphi">var
  i: Integer;
  o: &amp;amp;Object;
begin
  o := i;</pre></pre><p>Derleyicinin hata üretmemesi için ya <span
style="font-family: Courier New;">AUTOBOX </span>kullanacağız ya da <em>explicit casting</em> uygulayacağız:</p><pre>
<pre class="brush: delphi">var
  i: Integer;
  o: &amp;amp;Object;
begin
  o := &amp;amp;Object(i); //Explicit casting
  {$AUTOBOX ON}
  o := i;</pre></pre><p
align="justify">Buradan çıkardığımız sonuç, her halükarda eğer .Net ortamından söz ediyorsak, ister Delphi tipi olsun ister olmasın, herşey <span
style="font-family: Courier New;">&amp;Object</span> (<em>System.Object</em>) sınıfından türemiştir. Ve bir şekilde tüm tipler, <em>object </em>nesnesine atanabiliyor.</p><p
align="justify">Biz bu bilgiliyi kullanarak sınıflarımızı ve metodlarımızı bir çok noktada <strong>genelleştirebiliriz</strong>. Mesela VCL&#8217;den hatırlayacağınız <em>TList</em> sınıfına benzer bir liste sınıfı hazırlayabilir ve <em>object</em>&#8216;in yukarıdaki anlattığımız özelliği sayesinde içinde istediğimiz tipte nesneler barındırabiliriz. <em>System.Collections</em> alanında bulunan <em>Queue</em>(kuyruk) sınıfı da bu tarz bir yapıya sahiptir(Kuyruğa ilk giren ilk çıkar.). Mesela aşağıdaki örnekte iki farklı tipi <em>Queue </em>nesnesinin bir elemanı yapıyoruz.</p><pre>
<pre class="brush: delphi">uses
  System.Collections, System.Xml;
  ...

{$AUTOBOX ON}
var
  b: TButton;
  s: XmlDocument;
  q: Queue;
begin
  q := Queue.Create();
  b := TButton.Create(nil);
  x := XmlDocument;
  q.Enqueue(b);
  q.Enqueue(x);</pre></pre><p><em>Queue </em>sınıfının <em>Enqueue </em>metodu, kuyruğa yeni bir <em>object </em>eklemeye yarar. <em>Enqueue </em>metodu <em>object </em>tipinde parametre aldığından her çeşit nesne bu metod ile kuyruğumuza eklenebilir. Bu örneğimizde <em>TButton </em>ve <em>XmlDocument </em>tipinde iki farklı nesneyi kuyruğa ekledik. Kuyruktan bir eleman çıkarmak için ise <em>Dequeue </em>metodunu kullanmamız gerekecektir. <em>Dequeue </em>metodu da aynen <em>Enqueue </em>metodu gibi <em>object </em>nesneleri ile çalışır. Yani <em>Dequeue </em>metodunun çıktısı bir <em>object </em>olacaktır. İşte bu noktada, <em>object </em>sınıfının özelliğinden dolayı <em>Queue </em>sınıfı ve bu iki metodu <strong>genel çapta</strong> kullanılabilir bir yapıdadır.</p><p>Yukarıdaki kodlara şunları ekleyip derlemeye çalışalım:</p><pre>
<pre class="brush: delphi">var
  ...
  ...
  cikanB: TButton;
  cikanX: XmlDocument;
begin
  ....
  ....
  cikanB := q.Dequeue; //ilk button girmişti, ilk de o çıkacak.
  cikanX := q.Dequeue;</pre></pre><p
align="justify">Bu kodu derlemeye kalkdığınızda &#8220;Incompatible types&#8230;&#8221; hatasını alacaksınız. Halbuki yukarıda <span
style="font-family: Courier New;">AUTOBOX </span>ile <em>implicit </em>çevirimi aktifleştirmiştik. Peki bu hatayı neden aldık?</p><p
align="justify">Birincisi, <span
style="font-family: Courier New;">AUTOBOX </span>sadece <em>object </em>nesnelerine atama yapılacağında işe yarıyor, <em>object </em>nesneleri başka bir nesneye atama yapılacağında değil. <em>Object </em>sınıfndan türemiş olan bir sınıfın nesnesi <em>object</em>&#8216;e atama yapacağınızda, derleyici bunu otomatik çevirebilir. Ama aksi durumda, <em>object </em>nesnesinin hangi sınıftaki bir nesneye dönüşeceğini kestiremez. Bu durumda <em>explicit</em> çevirim yapmak zorundayız. Bunun için sondaki iki satırı şu şekilde değiştirmeliyiz:</p><pre>
<pre class="brush: delphi">  ...
  cikanB := TButton(q.Dequeue);
  cikanX := XmlDocument(q.Dequeue);
  ShowMessage(cikanX.Name); //&quot;#document&quot;</pre></pre><p
align="justify">Peki kuyruktan çıkış sırasını yanlışlıkla değiştirseydik ne olurdu? Biliyoruz ki, kuyruk&#8217;ta ilk giren ilk çıkar. Yani örneğimize göre ilk olarak button girmişti ve ilk olarak da o çıkacaktır. Bu durumda sıralarını değiştirelim deneyelim:</p><pre>
<pre class="brush: delphi">  ...
  cikanX := XmlDocument(q.Dequeue);
  cikanB := TButton(q.Dequeue);
  ShowMessage(cikanX.Name);</pre></pre><p
align="justify">Bu kod derlenir fakat çalışma anında yanlış yaptığımızı gösteren bir hata alırız. Bu durumda <em>object </em>sınıfının bu genelleştirme özelliğini, bunun gibi yerlerde kullanmak, bir çok yanlışı ve dikkatsizliği de beraberinde getiriyor.</p><p
align="justify"><strong>Genelleştirilmiş </strong>sınıf ve metodlar oluşturmak için kullanılan bu <em>object </em>yaklaşımının diğer bir dezavantajı ise, <em>object </em>sınıfının diğer bir nesneye çevirim yapılması(unbox işlemi ya da unboxing de denilir) yada başka bir sınıfın <em>object </em>sınıfına çevirim yapılması(box ya da boxing yapılması) esnasında gereken ek hafıza ve işlemci zamanıdır. Yani her defasında <em>Enqueue </em>yapıldığında bir nesne <em>object </em>tipine dönüştürülecek ve her <em>Dequeue </em>yapıldığında ise <em>object </em>nesnesinin tipi başka bir nesneye dönüştürülecektir. Bu da ek işlem gücü ve hafızayı gerektirir.</p><p
align="justify">Sınıfları ve metodları genelleştirmede <em>object </em>yaklaşımının belki başka dezavantajlarda sayılabilir ama, bizim işimiz burada generics ve parametrize tipler. <em>Object </em>yaklaşımı kafamızın bir köşesinde yer etmişken, dilerseniz generics yaklaşımına bir göz atalım.</p><h1>Generics ve Parametrize Tipler(Parameterized Types)</h1><p
align="justify">Aslında Delphi programcıları bunu çok uzun süreden beri bekliyorlardı. Belki de .NET 2.0&#8242;a böyle bir özellik gelmeseydi, uzun bir süre daha Delphi böyle bir özellikten yoksun kalacaktı. Borland ya da CodeGear&#8217;ın .NET ortamına yaptığı yatırımları eleştirenler olmuştur. Fakat bu yatırımlar sayesinde hem Win32 hem de .NET üzerinde Delphi bir çok yeniliğe gitmiştir. <a
href="http://www.diyezon.com/?p=53">Buradaki makaleden</a> bu yeni özellikleri kısaca tanıyabilirsiniz.</p><p
align="justify">Gramere ve kullanışına geçmeden önce bir kaç terim üzerinde duralım. Terimler ilk başta kafa karıştırıcı gibi gelse de bu terimleri kullandıkça alışacağınıza inanıyorum. Bu terimleri önce veriyorum çünkü makale içerisinde bu terimler kullanılacak.</p><h2>Terminoloji</h2><p
align="justify"><strong>Parametrize Tip (Parameterized Type:):</strong> <em>Tip Paremetresi</em>ne ihtiyaç duyan bir çeşit tiptir. <em>Generic tip</em>, parametrize tipin daha çok bilinen ismidir. Ama literatürde ikisi de kullanılmaktadır. Aşağıdaki kodda &#8220;<span
style="font-family: Courier New;">Liste&lt;eleman&gt;</span>&#8221; sınıfı, bir <em>parametrize tip</em>tir.</p><pre>
<pre class="brush: delphi">type
  Liste&lt;eleman&gt; = class
    ...
  end;</pre></pre><p
align="justify"><strong>Tip Parametresi (Type Parameter):</strong> Bir <em>parametrize tip</em> veya metod başlık tanımlamasında, <em>parametrize tip</em>in veya metodun <span
style="text-decoration: underline;">kodlarında kullanılacak</span> olan bir tipi ifade eden bir <em>parametre</em>dir. Daha sonra bu parametreye bir <em>tip argümanı</em> bağlanacaktır. Aşağıdaki kodda &#8220;<em>eleman</em>&#8221; bir <em>tip parametresi</em>dir ve sınıfın kodlarında <span
style="text-decoration: underline;">kullanılacak</span> olan bir tipi ifade eder.</p><pre>
<pre class="brush: delphi">type
  Liste&lt;eleman&gt; = class
    ...
  end;</pre></pre><p
align="justify"><strong>Oluşturulmuş Tip (Instantiated Type, Constructed type):</strong> Parametre kümesi tanımlanmış olan bir <em>parametrize tip</em>tir. <span
style="font-family: Courier New;">Liste&lt;Integer&gt;</span> gibi&#8230;</p><p
align="justify"><strong>Tip Argümanı (Type Argument):</strong> &#8220;<em>Oluşturulmuş</em>&#8221; bir tip yapmak için gereken bir çeşit tiptir. Mesela <span
style="font-family: Courier New;">Liste&lt;Integer&gt;</span> bir <em>oluşturulmuş parametrize tip</em> ise, <span
style="font-family: Courier New;">Integer</span> bunun <em>tip argümanı</em>dır.</p><p
align="justify"><strong>Kapalı Oluşturulmuş Tip (Closed Constructed Type):</strong> Bir <em>parametrize tip</em>in tüm parametreleri, <strong>gerçek tip</strong> olan <em>argüman</em>lar ile çözümlenmiş ise bu tipe, <em>Kapalı Oluşturulmuş Tip</em> denir. <span
style="font-family: Courier New;">Liste&lt;Integer&gt;</span> <span
style="text-decoration: underline;">kapalıdır</span>, çünkü <span
style="font-family: Courier New;">Integer</span> <em>tip argümanı</em> <span
style="text-decoration: underline;">gerçek</span> bir tiptir.</p><p
align="justify"><strong>Açık Oluşturulmuş Tip (Open Constructed Type):</strong> Eğer bir <em>parametrize tip</em>in en az bir parametresi bir <em>tip parametresi</em> ise, bu tip bir <em>açık oluşturulmuş parametrize tip</em>tir. Ayrıca eğer <span
style="font-family: Courier New;">Liste&lt;T&gt;</span> parametrize tipinde <em>T</em> parametresi <span
style="text-decoration: underline;">bir sınıfı</span> içeren parametre ise <span
style="font-family: Courier New;">Liste&lt;T&gt;</span> açık oluşturulmuş bir tiptir. İleride örnekler yaptığımızda bu daha da pekişecektir.</p><p
align="justify"><strong>Oluşturma İşlemi (Instantiation):</strong> Derleyici, parametrize tiplerde tanımlı metodlar için gerçek işlem kodunu oluşturur ve kapalı tipler için gerçek virtual metod tablosunu oluşturur. Bu işlem <span
style="text-decoration: underline;">ön derleme</span> esnasında gerçekleşir. Bu işlem Win32&#8242;de <span
style="font-family: Courier New;">*.dcu</span> ve <span
style="font-family: Courier New;">*.obj</span> dosyalarını çıkarırken gereklidir. Fakat .Net ortamında gerekli değildir. Çünkü .Net oluşturulmuş tipler için gerekli tanımlamalara sahiptir. Bu yüzdendir ki, C++&#8217;da template oluşturduğunuzda, derleyici ilk başta template&#8217;li tipleri ayrı ayrı oluşturmak için ön derleme işlemine girişir. Anlaşılan o ki, Delphi&#8217;nin bir sonraki versiyonunda da Win32&#8242;de generics desteği verebilmek için buna benzer bir yönteme gidilecek. Her neyse&#8230;</p><p
align="justify">Bunlar dışında bir kaç terim daha var. Fakat daha fazla kafa karıştırmamak için, o terimleri ayrı başlıklar halinde ileride görelim. Dilerseniz önceden object yaklaşımı ile yaptığımız işlemleri şimdi de generics ile yapmaya çalışalım.</p><h1>Kullanımı</h1><p
align="justify">Kuyruk örneğimizi hatırlayacak olursanız, ekstradan tip çevirim işlemleri yapmıştık. Ayrıca tip güvenliğinin gerekli olduğundan ve bir dikkatsizlikte belli sorunların oluşabileceğinden bahsettik. Ayrıca fazladan gerçekleştirilen object&#8217;den bir başka tipe ve daha sonra da o tipden tekrar object&#8217;e çevirim yapılmasından dolayı(<em>box / unbox</em>), ekstra hafıza ve işlem gücü gerektiğinden bahsetmiştik.</p><p
align="justify">Bütün bunlara gerek duymadan, genelleştirilmiş sınıflar oluşturabilmek için parametrize tipleri ve generic yapısını kullanabiliriz. Generic sınıflar, tip parametresine sahip olduğundan dolayı, sadece <em>object </em>gibi bir tipe bağlı <span
style="text-decoration: underline;">kalmaz</span>. Böylece yukarıda bahsettiğimiz sıkıntıları da bertaraf etmiş oluruz.</p><p
align="justify">.Net 2.0 ile birlikte <em>System.Collections.Generic</em> alan adının altında bir çok yararlı generic sınıf tanımlanmıştır. Aşağıdaki kod parçasında parametrize <em>Queue </em>sınıf ile oluşturulmuş bir <em>Integer </em>kuyruğu ve bir <em>string </em>kuyruğu görmekteyiz.</p><pre>
<pre class="brush: delphi">uses
  System.Collections.Generic;

  ...
  ...

var
  SayiKuyruk: Queue&lt;Integer&gt;;
  YaziKuyruk: Queue&lt;string&gt;;
  Sayi: Integer;
  Yazi: string;
begin
  SayiKuyruk := Queue&lt;Integer&gt;.Create;
  YaziKuyruk := Queue&lt;string&gt;.Create;
  Sayi := 123;
  Yazi := &#039;Deneme&#039;;
  SayiKuyruk.Enqueue(Sayi);
  YaziKuyruk.Enqueue(Yazi);
  ...</pre></pre><p
align="justify"><em> </em></p><p
align="justify"><em> </em></p><p
align="justify"><em> </em></p><p
align="justify"><em> </em></p><p
align="justify">Gördüğünüz gibi, ne bir çevirim işlemi var ne de tipler uyuşuyor mu diye bir endişe&#8230; &#8220;<em>SayiKuyruk</em>&#8221; ve &#8220;<em>YaziKuyruk</em>&#8221; nesnelerinin ikisi de <span
style="font-family: Courier New;">Queue&lt;T&gt;</span> gibi bir <span
style="text-decoration: underline;">parametrize tip&#8217;den oluşturulmuş</span> tipten oluşturulmuştur <img
src='http://www.diyezon.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> . Terimlerimizde hatırlarsanız &#8220;<em>Kapalı oluşturulmuş tip</em>&#8220;ten bahsetmiştik. Derleyici bu örneğimizde <span
style="font-family: Courier New;">Queue&lt;T&gt;</span> tipinden iki adet <span
style="text-decoration: underline;">yeni tip oluşturmuştur</span>. Bunlardan birisi <span
style="font-family: Courier New;">Queue&lt;Integer&gt;</span> ve diğeri ise <span
style="font-family: Courier New;">Queue&lt;string&gt;</span> tipidir. Ve ikisi de <em>kapalı oluşturulmuş</em> bir tiptir. Ayrıca iki tip tamamen bir birinden <strong>bağımsız</strong> iki farklı tiptir!</p><p
align="justify">Bu örnekten ne öğrendik? Özet olarak, bir generic sınıfa atadığımız <em>tip argümanları</em>na göre(<em>Integer, string, vs..</em>) yeni bağımsız bir tip oluşturuyoruz. Ve bu oluşturulan tip, parametre olarak verdiğimiz argümanlara göre işlem yapıyor.</p><p
align="justify">Önceki Queue sınıfı, sadece <em>object </em>tipinde nesneler ile çalşıyordu. Bu sınıftan sadece bir adet vardı ve oluşturduğumuz nesneler hep aynı sınıftan oluşturuluyordu. Ama <em>generic Queue </em>sınıfında ise iş biraz değişiyor. Artık tanımladığımız farklı tip için ayrı bir sınıf oluşturuluyor. Bu yüzden örneğimizdeki <em>SayiKuyruk </em>ve <em>YaziKuyruk </em>nesnelerinin sınıfları aynı değildir! Derleyici her iki <em>tip argümanı</em> için de ayrı ayrı sınıflar oluşturmuştur. Tip argümanları gerçek tipler olduğu için de oluşturulan bu iki sınıf da <em>kapalı oluşturulmuş</em> tip olmuştur.</p><h1>Tanımlanması</h1><p
align="justify">Generic tiplerin tanımlanması kullanımı kadar kolaydır. Delphi&#8217;de sınıflar, interface&#8217;ler ve record&#8217;lar tip parametreleri ile birlikte tanımlanabilirler. Ayrıca metodlar ve prosedürel tipler de parametrize yapılabilir. Aşağıdaki sınıf tanımlamasına göz önüne alalım:</p><pre>
<pre class="brush: delphi">type
  TBirSinif = class
  public
    procedure BirSeylerYap(Parametre: string); overload;
    procedure BirSeylerYap(Parametre: Integer); overload;
    procedure BirSeylerYap(Parametre: Boolean); overload;
  end;</pre></pre><p
align="justify">Sadece bu sınıf ve metodları düşündüğümüzde aynı iş için üç farklı metod tanımlaması yapıyoruz. Ne için? Sadece bir tipin değişmesi yüzünden. Bir de ikinci bir parametre aldığını düşünün. Veya üç, dört&#8230; Ne yapacağız? 4&#8242;ün 3&#8242;lü kombinasyonunu alıp o kadar farklı metod tanımlaması mı yapacağız?</p><p
align="justify">Bunun yerine bu işi derleyiciye bırakıp, biz sadece tipler ile uğraşıyoruz. Yukarıdaki sınıfımızı parametrize olarak yeniden tanımlayalım:</p><pre>
<pre class="brush: delphi">type
  TBirSinif&lt;T, K, L&gt; = class
  private
    FBirOzellik: K;
  protected
    procedure SetBirOzellik(const Value: K);
  public
    procedure BirSeylerYap(Parametre: T);
    property BirOzellik: K read FBirOzellik write SetBirOzellik;
  end;</pre></pre><p
align="justify">Gördüğünüz gibi, <em>Integer string</em> gibi değişkenler yerine sınıfa eklediğim <em>tip parametreleri</em>ni kullanıyorum. Bu parametrize tipimizde 3 adet <em>tip parametresi</em> mevuct. Gerçi bu örnekte sadece ilk ikisini kullanıyoruz.</p><p
align="justify"><em>Bir genel kültür bilgisi mahiyetinde, ek olarak, bu sınıf derlendikten sonra </em><span
style="font-family: Courier New;">TBirGenericSinif`3</span><em> gibi bir şekle girecektir. Eğer tip parametre sayısı 4 olsaydı, son kısım &#8220;</em><span
style="font-family: Courier New;">`4</span><em>&#8221; olacaktı. Bu yüzden derlenmiş assembly&#8217;nizi ya da programınızı reflect ettiğinizde bu tarz şeyler görürseniz şaşırmayın. Her neyse&#8230;</em></p><p
align="justify">Önceden de bahsettiğimiz gibi yukarıdaki <em>TBirSinif </em>sınıfı ile <em>TBirSinif&lt;T, K, L&gt;</em> generic sınıfı birbirinden çok farklı şeylerdir. İsimleri aynı olabilir ama kesinlikle aralarında bir bağlantı yoktur. Aynı şekilde <em>TBirSinif&lt;Integer, string, Boolean&gt;</em> gibi bir <em>kapalı</em> oluşturulmuş sınıf, <em>TBirSinif&lt;string, string, Integer&gt;</em> gibi başka bir <em>kapalı</em> sınıftan çok çok farklıdır ve bağlantıları yoktur. Çünkü derleyici, <em>tip argümanları</em>na göre tipleri tekrar tekrar tanımlamaktadır.</p><h2>Generic Metodlar</h2><p
align="justify">Sınıflar gibi metodlar ve prosedürel tipler de parametrize olarak tanımlanabilir. Mesela:</p><pre>
<pre class="brush: delphi">type
  TBirProsedur2&lt;T&gt; = procedure(Param1, Param2: T) of object;
  TNormalBirSinif = class
    procedure BirProsedur&lt;X&gt;(Param1, Param2: X);
  end;

procedure TNormalBirSinif.BirProsedur&lt;X&gt;(Param1, Param2: X);
begin

end;</pre></pre><p
align="justify">Görüldüğü gibi parametrize olmayan bir tip içinde(<em>TNormalSinif</em>) parametrize bir metod tanımladık. Bunu yaparken aynen sınıflarda olduğu gibi metod isminden sonra tip parametrelerini giriyoruz. Aynı şeyi parametrize olan bir tip içinde de yapabilirdik. Ama burada karar size ait. Eğer generic bir sınıf içinde farklı parametrelerle tanımlamamız gereken metodlar olursa, bu şekilde parametrize edebiliriz. Yani:</p><pre>
<pre class="brush: delphi">type
  TBirSinif&lt;T&gt; = class
    ...
    procedure BirProc&lt;X&gt;(Param1: X; Param2: T);
  end;</pre></pre><p
align="justify">Görüldüğü gibi sınıf tanımlamasında <em>X</em> parametresi tanımlı olmadığı halde, metod tanımlamamızda kullanabiliyoruz. Peki bu gibi bir şeyi nasıl kullanacağız?</p><p
align="justify">Farkında iseniz bir kaç paragraf yukarıda verdiğimiz bir örneğin tam bir çözümlemesini yapmadık. Yani:</p><pre>
<pre class="brush: delphi">type
  TBirSinif = class
  public
    procedure BirSeylerYap(Parametre: string); overload;
    procedure BirSeylerYap(Parametre: Integer); overload;
    procedure BirSeylerYap(Parametre: Boolean); overload;
  end;</pre></pre><p
align="justify">Bu sınıfta &#8220;BirSeylerYap&#8221; metodu overload edilmiştir. Bu sınıfı generic yaparken şu şekilde bir tanımlama yapmıştık:</p><pre>
<pre class="brush: delphi">type
  TBirSinif&lt;T&gt; = class
  public
    procedure BirSeylerYap(Parametre: T);
  end;</pre></pre><p
align="justify">Burada her bir <em>kapalı tip</em> için sadece bir adet metodumuz olabiliyor. Yani mesela <em>TBirSinif&lt;Integer&gt;</em> gibi kapalı bir sınıf içinde sadece bir adet &#8220;<em>BirSeylerYap</em>&#8221; metodu vardır ve sadece <em>Integer </em>tipinde bir parametre almaktadır. Halbuki bizim esas yapmak istediğimiz her bir <em>kapalı sınıf</em> içinde overload yaptığımız tiplerin sınırlandırmasını <span
style="text-decoration: underline;">kaldırmak</span>. Sadece bir çeşit metod yerine birden fazla çeşitte parametre alan metodlarımız olması gerekiyor. Bu yüzden metodumuzu <strong>genelleştirmek </strong>için onu da parametrize ediyoruz:</p><pre>
<pre class="brush: delphi">type
  TBirSinif&lt;T&gt; = class
  public
    procedure BirSeylerYap&lt;X&gt;(Parametre: X);
  end;</pre></pre><p
align="justify">gibi&#8230; Bu şekilde bir metodun, istediğimiz kadar çeşidini overload yapmadan oluşturabiliriz. Mesela:</p><pre>
<pre class="brush: delphi">var
  BirNesne: TBirSinif&lt;Integer&gt;;
begin
  BirNesne := TBirSinif&lt;Integer&gt;.Create;
  BirNesne.BirSeylerYap&lt;string&gt;(&#039;Deneme bir string&#039;);
  BirNesne.BirSeylerYap&lt;Integer&gt;(123);
  BirNesne.BirSeylerYap&lt;Boolean&gt;(True);
  BirNesne.BirSeylerYap&lt;TForm&gt;(Form1);
  BirNesne.BirSeylerYap&lt;TButton&gt;(Button1);</pre></pre><p>gibi&#8230;</p><p
align="justify">Gördüğünüz gibi overload ile kısıtlı sayıda yaptığımız metod tanımlamalarını bu şekilde artırabiliriz. Bu da aynen sınıflarda olduğu gibi, metodlarımızı da genelleştirmemize imkan sağlıyor.</p><h1>Gelecek Bölümde</h1><p
align="justify">Gelecek bölümde Sınırlandırıcıları ya da başka yerlede göreceğiniz tabir ile Kısıtlamalar ya da Constraints tabirlerini göreceğiz. Diğer bölüme geçmek için <a
href="http://www.diyezon.com/?p=55">buraya</a> tıklayabilirsiniz.</p><p
align="justify">Her zamanki gibi yorum ve eleştirilerinizi bekliyorum.</p><p>Fatih Tolga Ata</p><h2>Kaynaklar:</h2><ul><li>CodeGear&#8217;ın Delphi Parametrize Tipler ile İlgili Taslak Metinleri</li><li>.NET 2.0 SDK Yardım Dosyası</li><li>Microsoft Visual C# 2005 Step by Step, John SHARP, 2005 Edition</li></ul> ]]></content:encoded> <wfw:commentRss>http://www.diyezon.com/enine-boyuna-generics-bolum-1/feed/</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>Delphi 7 ile Şimdiki Delphi Sürümleri Arasındaki Farklar</title><link>http://www.diyezon.com/delphi-7-ile-simdiki-delphi-surumleri-arasindaki-farklar/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=delphi-7-ile-simdiki-delphi-surumleri-arasindaki-farklar</link> <comments>http://www.diyezon.com/delphi-7-ile-simdiki-delphi-surumleri-arasindaki-farklar/#comments</comments> <pubDate>Mon, 13 Aug 2007 22:00:48 +0000</pubDate> <dc:creator>Fatih Tolga Ata</dc:creator> <category><![CDATA[Delphi]]></category> <category><![CDATA[abstract]]></category> <category><![CDATA[class helper]]></category> <category><![CDATA[class static]]></category> <category><![CDATA[delphi.net]]></category> <category><![CDATA[final metodlar]]></category> <category><![CDATA[fonksiyon]]></category> <category><![CDATA[for-in]]></category> <category><![CDATA[inline]]></category> <category><![CDATA[miras]]></category> <category><![CDATA[nesne]]></category> <category><![CDATA[nested classes]]></category> <category><![CDATA[operator overloading]]></category> <category><![CDATA[property]]></category> <category><![CDATA[record]]></category> <category><![CDATA[sealed]]></category> <category><![CDATA[sınıf]]></category> <category><![CDATA[strict private]]></category> <category><![CDATA[strict protected]]></category><guid
isPermaLink="false">http://www.diyezon.com/?p=53</guid> <description><![CDATA[Bildiğiniz gibi VCL, .NET ortamına taşınırken derleyici bazında bir çok değişikliğe ve geliştirmeye gidildi. Bu geliştirmeler, hem Delphi.NET derleyicisini hem bildiğimiz klasik Delphi for Win32 derleyicisini etkiledi. Tabi bu geliştirmeler, en çok biz programcıların işine yaradı. Bu makalemizde bu yeniliklere değinmeye çalışacağız. Bunlardan bir kısmını önceki makalelerimizde ayrıntılı olarak işlemiştik. Burada sadece bu sitede bahsetmediğimiz [...]]]></description> <content:encoded><![CDATA[<p
align="justify">Bildiğiniz gibi VCL, .NET ortamına taşınırken derleyici bazında bir çok değişikliğe ve geliştirmeye gidildi. Bu geliştirmeler, hem Delphi.NET derleyicisini hem bildiğimiz klasik Delphi for Win32 derleyicisini etkiledi. Tabi bu geliştirmeler, en çok biz programcıların işine yaradı.</p><p
align="justify">Bu makalemizde bu yeniliklere değinmeye çalışacağız. Bunlardan bir kısmını önceki makalelerimizde ayrıntılı olarak işlemiştik. Burada sadece bu sitede bahsetmediğimiz kısımlar bahsedilecek ve önceden bahsettiklerimize linkler içerecektir.</p><p><span
id="more-53"></span></p><h1>Operator Overloading:</h1><p
align="justify">Buradaki makalede ayrıntılı olarak işlenmiştir: <a
href="http://www.diyezon.com/?p=52">http://www.diyezon.com/?p=52</a></p><h1>Inline Belirleyicisi:</h1><p
align="justify">Derleyiciye tavsiye niteliğinde olan bu belirleyici (ya da direktif), fonksiyon ya da prosedürün kod çıktısında, çağrıldığı yere gömülmesini sağlar. Tavsiye niteliğinde diyorum çünkü, eğer prosedür ya da fonksiyon inline yapılmaya müsait değilse derleyici bu direktifi es geçecektir.</p><p
align="justify">Inline yapılmış prosedür ya da fonksiyon, &quot;call&quot;, &quot;ret&quot; gibi assembler komutlarına ihtiyacı olmadığı gibi, stack&#8217;ın push ve pop ile yedeğinin de alınmasına ihtiyaç yoktur. &Ccedil;ünkü inline yapılan fonksiyon, direk olarak çağrıldığı kısma yerleşecektir. Halbuki normal fonksiyonlarda call komutu ile hafızada başka bir kod kısmına dallanmaktadır. Ve burada stack&#8217;ın yedeği alınıp, fonksiyon sonunda yedek tekrar yüklenmektedir. Daha fazla bilgi için (<a
href="http://www.diyezon.com/?p=45">Delphi ve C++ Builder ile Assembler</a> ve <a
href="http://www.diyezon.com/?p=47">Fonksiyon &Ccedil;ağırım Mekanizmaları</a>).</p><p
align="justify">Inline prosedür ve fonksiyonda bu gibi işlemler yapılmadığından kod daha hızlı çalışacaktır. Ama her fonksiyon çağırımında fonksiyon kodları direk olarak çağrıldığı yere yerleşeceğinden, programınızın boyutu biraz büyüyecektir. Tanımlaması normal fonksiyon ve prosedür gibi olup sadece son tarafa inline belirleyicisi eklenir:</p><pre>
<pre class="brush: delphi">procedure MyProc(x:Integer); inline;
begin
    // ...
end;</pre></pre><h1>Strict Private</h1><p
align="justify">Sınıf kavramına katılan bu alan tipi, normal private alan gibidir. Tek fark bu alan daha da gizlidir <img
src='http://www.diyezon.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> Bu dediğimizin anlaşılabilmesi için private alanın ne olduğu bilinmesi gerek. Tabi ondan önce sınıfların ne olduğunu biliyorsunuzdur umarım(<a
href="http://www.diyezon.com/?p=40">Bileşen Yazım Klavuzu</a>nun ilk bölümleri sınıfları anlatmaktadır).</p><p
align="justify">Private sınıflar normalde, aynı unit içindeki başka sınıflar içinden erişilebilir. Strict Private ise gerçekten gizler. Yani aynı unit içinde bile olsa başka bir sınıf strict private alana erişemez.</p><h1>Strict Protected</h1><p
align="justify">Private için geçerli olan durumun aynısı protected için de geçerlidir. Strict Protected ise sadece aynı sınıf ve sınıfın çocukları tarafından görülebilirler. Aynı unit bile olsa başka bir sınıf bu alanı göremez.</p><h1>Metodlu Record&#8217;lar</h1><p
align="justify">Artık Recordlar için private, public gibi alanlar tanımlayıp procedure, function, constructor gibi metodlar tanımlayabiliyoruz. Ayrıca property&#8217;ler de tanımlanabiliyor. Yani aynı sınıflar gibi&#8230;</p><pre>
<pre class="brush: delphi">type
  TMyRecord = record
    type
      TBirTip = Integer; //tip tanımlaması
    var
      BirAlanDegiskeni: TColor;
    class var
      StatikDegisken: Integer;
    procedure BirProsedur();
    constructor Create;
    property Color: TColor read BirAlanDegiskeni write BirAlanDegiskeni;
    class property StatikOzellik: TBirTip read StatikDegisken write StatikDegisken;
  end;

constructor TMyRecord.Create;
begin

end;

procedure TMyRecord.BirProsedur;
begin
  ShowMessage(IntToStr(Color));
end;</pre></pre><h1>Abstract ve Sealed Sınıflar</h1><p
align="justify">Önceden abstract sınıf tanımlanamıyordu. Bunun yerine interface&#8217;ler kullanılıyordu. Ama bu her zaman işe yaramayabiliyor. Yabancı olanlar için kısaca abstract, kendisinde herhangi bir tanımlama ve kod bulunayan, <strong>kod kısmı</strong>, sınıflar için çocuk sınıflarda, metodlar için ise mirasçı metodlarda tanımlı olan bir yapıdır.</p><pre>
<pre class="brush: delphi">type
  TAbstractClass = class abstract
    procedure SomeProcedure;
end;</pre></pre><p
align="justify">Bu sınıfın kod tanımlaması TAbstractClass sınıfından türeyecek olan sınıflarda yapılacaktır.</p><p
align="justify">Sealed sınıflar ise, kısır sınıflardır. Yani artık sınıf hiyerarşisinde en sonda bulunan ve kendisinden herhangi bir türetme yapılamayacak olan sınıflardır.</p><pre>
<pre class="brush: delphi">type
  TSealedClass = class sealed
    procedure SomeProcedure;
end;</pre></pre><p
align="justify">Bu sınıftan başka bir sınıf türetilemez.</p><h1>Class Helper&#8217;lar</h1><p
align="justify">Buradaki makalede ayrıntılı olarak işlenmiştir: <a
href="http://www.diyezon.com/?p=50">http://www.diyezon.com/?p=50</a></p><h1>Class Statik Tipler, Değişkenler, Özellikler, Sabitler, Metodlar</h1><p
align="justify">Bazen olurki nesneyi tanımlamadan, sınıf içinde bulunan bazı değerlere erişilmesini istersiniz. Bu durumda erişilmesini istediğiniz şeyi, statik olarak tanımlamalısınız. Mesela aşağıdaki örneğe bakalım:</p><pre>
<pre class="brush: delphi">type
  TAClass = class
    strict private
      class var
        StatikDegisken: Integer;
    strict protected
      class function GetStatikOzellik: Integer; static;
      class procedure SetStatikOzellik(val: Integer); static;
    public
      class property StatikOzellik: Integer read GetStatikOzellik write SetStatikOzellik;
  end;

TAClass.StatikOzellik := 879;</pre></pre><p
align="justify">Burada gördüğünüz gibi TAClass sınıfından bir nesne oluşturmadan içindeki bir özelliğie eriştik. Class Static tekniğini kullanırken dikkat etmeniz gerken bir kaç önemli nokta şudur:</p><ul><li>Statik metod&#8217;lar içinde artık Self değişkenini kullanamazsınız. &Ccedil;ünkü sınıfa referans içeren bir nesne yoktur.</li><li>Statik metodlar ve özelliklerde ancak ve ancak statik olan değişken ve metodları kullanabilirsiniz.</li><li>Statik metodları virtual ya da dynamic olarak tanımlayamazsınız.</li></ul><p
align="justify">Bunun dışında bir sınıf içinde statik sabitler ve statik tipler de oluşturulabilir:</p><pre>
<pre class="brush: delphi">type
  TBirClass = class
  private
    type
      TBirStatikRecord = record
        BirAlan: string;
      end;
  public
    class var
      BirStatikRecord: TBirStatikRecord;
    const StatikSabit = &#039;Burada class kelimesini kullanmanıza gerek yok!!&#039;;
  end;

....
ShowMessage(TBirClass.StatikSabit);</pre></pre><p
align="justify">gibi&#8230;</p><h1>İç içe Geçmiş Sınıflar &#8211; Nested Classes</h1><p
align="justify">Artık bir sınıf tanımlamasının içinde başka bir sınıf tanımlanabiliyor:</p><pre>
<pre class="brush: delphi">type
  TDistakiSinif = class
  public
    type
      TIctekiSinif = class
      public
        procedure IctekiMetod;
      end;
      procedure DistakiMetod;
  end;

procedure TDistakiSinif.TIctekiSinif.IctekiMetod;
begin
  ...
end;</pre></pre><h1>Final Metodlar</h1><p
align="justify">Sealed sınıflar gibi final metodlar da bir sınıfın çocuk sınıflarında, final olarak tanımlanmış olan virtual metodun miras  alınamadığını ifade eder. Mesela:</p><pre>
<pre class="brush: delphi">TSonMetodluSinif = class(TBirAtaSinif)
public
  procedure SonMetod; override; final;
end;</pre></pre><p
align="justify">Bu tanımlamadan sonra artık SonMetod isimli metod, &quot;TSonMetodluSinif&quot; isimli sınıfın çocuk sınıfları tarafından override yapılamayacaktır. Final metodların sadece virtual metodlarda işe yaradığına dikkat edin.</p><h1>For-In Döngüsü</h1><p
align="justify">Artık bir dizi veya kolleksiyon içinde for-in döngüsü ile iterasyon yapılabilmekte:</p><pre>
<pre class="brush: delphi">
for DiziElemani in BirDizi do
begin
  ....
end;

for BirKarakter in BirString do
begin
  ...
end;

for BirKumeElemani in BirKume do
begin
  ...
end;

for BirKolleksiyonElemani in BirKolleksiyon do
begin
  ...
end;</pre></pre><h1>Sonuç</h1><p
align="justify">Yeni gelen özelliklerin unutmamış isem tamamına değinmeye çalıştım. Bunlara ek olarak Highlander çıktığında generic yani parametrize tiplere de ayrı olarak değineceğiz. Generics&#8217;in nasıl bir şey olduğunu merak edenler <a
href="http://www.diyezon.com/?p=48">buraya</a> tıklayarak meraklarını giderebilirler.</p><p
align="justify">Bunlarla ilgili uygulama yapıp geliştirmek sizlere kalmış. Buradaki bilgiler önfikir mahiyetindedir. Derleyici bazındaki yenilikler bunlar olmakla birlikte, VCL ve diğer alanlarda da bir çok yeniliğe gidilmiştir. Yeri geldikçe bunlardan da bahsedebiliriz.</p><p
align="justify">Yorumlarınız önemlidir, bekliyorum.</p></p> ]]></content:encoded> <wfw:commentRss>http://www.diyezon.com/delphi-7-ile-simdiki-delphi-surumleri-arasindaki-farklar/feed/</wfw:commentRss> <slash:comments>4</slash:comments> </item> <item><title>Delphi ve Operator Overloading</title><link>http://www.diyezon.com/delphi-ve-operator-overloading/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=delphi-ve-operator-overloading</link> <comments>http://www.diyezon.com/delphi-ve-operator-overloading/#comments</comments> <pubDate>Sun, 05 Aug 2007 22:00:40 +0000</pubDate> <dc:creator>Fatih Tolga Ata</dc:creator> <category><![CDATA[Delphi]]></category> <category><![CDATA[delphi.net]]></category> <category><![CDATA[nesne]]></category> <category><![CDATA[operator overloading]]></category> <category><![CDATA[sınıf]]></category><guid
isPermaLink="false">http://www.diyezon.com/?p=52</guid> <description><![CDATA[&#34;Operator Overloading&#34;(operator aşırı yükleme), Delphi&#8217;ye Delphi.NET ile birlikte gelen class helper&#8217;lar gibi yeni özelliklerden birisidir. Bu teknik, class helper&#8217;lar gibi hem Win32 hem de .NET için kullanabilirsiniz. Delphi&#8217;ye, belki de çok uzun zaman önce eklenmesi gereken bu özellik ile, herhangi bir sınıf veya record için toplama(+), çıkarma(-), çarpma(*) gibi operatörlere bazı özel anlamlar yükleyebiliyoruz. C++ [...]]]></description> <content:encoded><![CDATA[<p
align="justify">&quot;Operator Overloading&quot;(operator aşırı yükleme), Delphi&#8217;ye Delphi.NET ile birlikte gelen class helper&#8217;lar gibi yeni özelliklerden birisidir. Bu teknik, class helper&#8217;lar gibi hem Win32 hem de .NET için kullanabilirsiniz.</p><p
align="justify">Delphi&#8217;ye, belki de çok uzun zaman önce eklenmesi gereken bu özellik ile, herhangi bir sınıf veya record için toplama(+), çıkarma(-), çarpma(*) gibi operatörlere bazı özel anlamlar yükleyebiliyoruz. C++ programcılarının hiç de yabancı olmadıkları bu teknik ile kendimize özel veri tipleri tanımlayıp, bu tiplerin Integer, string gibi başka tipler ile etkileşimlerinin nasıl sonuç vereceğini belirleyebilirsiniz.</p><p><span
id="more-52"></span></p><h1>Giriş</h1><p
align="justify">Delphi programcılarının uzun zamandır en çok yakındıkları ve Delphi derleyicisi tasarımcılarının başını ağrıttıkları belli başlı konular vardır. Birisi şüphesiz parametrize tiplerdir(generics, c++ için STL). Highlander sürümü ile birlikte bu özellik de Delphi&#8217;ye kazandırılmış olacak. Diğerleri de nested tipler, class static tipler ve operator overloading olarak sıralanabilir. Parametrize tipler hariç diğerlerini hem Delphi, hem de Delphi.NET için şu anda kullanabiliyoruz.</p><p
align="justify">Bu makalemizde göreceğimiz operator aşırı yükleme veya operator overloading, sınıf ya da record&#8217;ların kullanımını büyük bir ölçüde genişletmektedir.  Delph.NET için hem record hem de class veri tipleri için operator overloading yapılabilirken, Delphi for Win32 için sadece record veri tiplerinde operator overloading yapılabilir. Ama bu kullanım bakımından o kadar da etki etmemektedir. Hatta çoğu durumda operator overloading için record kullanılmaktadır. Vereceğimiz örnekler sınıflar ile yapılmış olduğundan Delphi.NET ortamında denemelisiniz. Sınıflar ile örnek veriyorum, çünkü recordlar&#8217;dan farklı olarak yapılacak fazladan bir kaç işlem var. Mesela record&#8217;larda çoğu durumda Result çıkış değeri Record olduğundan, Create ile oluşturmanıza gerek yok.</p><h1>Ayran&#8217;nın Suyu Fazla mı Olmuş, Ne?</h1><p
align="justify">Operator overloading&#8217;i anlatabilmek için böylesine uçuk sayılabilecek bir örnek seçtim. Belki ayran içtikçe bu konuyu hatırlarsınız <img
src='http://www.diyezon.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /></p><p
align="justify">Şimdi 3 adet tipimiz olsun. TAyran, TYogurt ve TSu. TAyran sınıfı içinde hem TYogurt tipinde, hem de TSu tipinde iki adet özelliğimiz olsun. TTuz da olurdu ama şimdilik ayranımız tuzsuz olsun <img
src='http://www.diyezon.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /></p><pre>
<pre class="brush: delphi">TYogurt = class
  Miktar: Integer;
end;

TSu = class
  Miktar: Integer;
end;

TAyran = class
  Yogurt: TYogurt;
  Su: TSu;
public
  constructor Create;
end;

constructor TAyran.Create;
begin
  inherited;
  Yogurt := TYogurt.Create;
  Su := TSu.Create;
end;</pre></pre><p
align="justify">Şimdi diyelimki TAyran tipimize TYogurt ve TSu tiplerinde ekleme yapılabilsin. Ayrıca TYogurt ve TSu kendi aralarında toplanabilsin. Ayrıca bir de TAyran ile diğer tipler karşılaştırılmaya tabi tutulabilsin. Bu işlemleri siz, ayrı ayrı metodlar tanımlayarak ve bu metodları gerektiği yerde kullanarak gerçekleştirebilirisiniz. Ama operator overloading kullanarak aşağıdaki gibi işlemler yapabileceğiz:</p><pre>
<pre class="brush: delphi">var
  Yogurt: TYogurt;
  Su: TSu;
  Ayran: TAyran;
begin
  Yogurt := 15;
  Su := 5;
  Ayran := Yogurt + Su;
  Yogurt := Yogurt - 5;
  if Ayran &gt; Yogurt then
    ShowMessage(&#039;Ayrandaki yogurt miktarı daha fazla&#039;);
end;</pre></pre><p
align="justify">Nesne oluşturma kısımlarını konumuz dışında olduğu için atladım. Tabi ki bu işlemler, operator overloading olmadan hataya sebep olacaktır. &Ccedil;ünkü bir sınıf tipini kalkıp başka bir sınıf tipi ile topluyorsunuz, sonra bu sınıf tipine Integer ekliyorsunuz ve sonra da başka bir sınıf tipi ile karşılaştırmaya tabi tutuyorsunuz. Dediğim gibi bu işlemleri prosedür ve fonksiyonlarla da halledebilirsiniz. Ama bu işlemleri operator overloding üzerinden yaparsanız, kullanımda yukarıdaki gibi bir kolaylık sağlamış olacaksınız. Şimdi veri tiplerimiz ile yukarıdaki işlemleri gerçekleştirebilmek için gereken operator tanımlamalarını yapalım.</p><h1>Add ve Subtract</h1><p
align="justify">Şimdi TYogurt nesnesi için aşağıdaki tanımlamayı yapalım. Ama ondan önce TYogurt içinde TAyran ve TSu sınıflarına referanslarımız olduğundan ikisi için forward tanımlaması gerekmekte. &Ccedil;ünkü TAyran ve TSu, TYogurt&#8217;dan sonra tanımlanmıştır. Her neyse bu zaten nesne programlama konusu. Şu an bizim konumuzla alakası fazla yok.</p><pre>
<pre class="brush: delphi">
  TAyran = class; //forward tanımlama
  TSu = class; //forward tanımlama

  TYogurt = class
    Miktar: Integer;
    class operator Add(Yogurt: TYogurt; Su: TSu): TAyran; //Ayran := Yogurt + Su
    class operator Add(Yogurt1: TYogurt; Yogurt2: TYogurt): TYogurt; //Yogurt3 := Yogurt1 + Yogurt2
    class operator Subtract(Yogurt1: TYogurt; Yogurt2: TYogurt): TYogurt; //Yogurt3 := Yogurt1 - Yogurt2
  end;</pre></pre><p
align="justify">Gördüğünüz gibi üç adet satır ekledik. Bunlardan ilki &quot;+&quot; yani toplama operatorünün üzerine yazar. Bu class statik metod ile Add metodunun üzerine yazmak ile nesnemiz ile yapılacak olan her &quot;+&quot; toplama işleminde bu metod çağrılacaktır. Ama verdiğimiz kriterlere göre&#8230; Örneğimizde gördüğünüz gibi iki adet Add operatoru tanımlandı. Bunlardan ilki TYogurt ile TSu değerlerini topluyor ve sonucu TAyran olarak çıktı veriyor. İkincisi ise iki adet TYogurt nesnesin topluyor ve yine TYogurt cinsinden çıktı veriyor.</p><p
align="justify">Buradaki altın kural, eğer <strong>iki parametreli</strong> operator metodu var ise, tanımlayacağınız operator metodundaki parametrelerden <strong>en az birisi</strong> işleme girecek olan <strong>sınıftan</strong> olmalı. İki parametre alan çoğu operator metodları için bu mantık vardır. Bu yüzden bir operator&#8217;e overload yapacaksanız, kara kara hangi sınıfta tanımlıyacağınızı düşünmenize gerek yok. &Ccedil;ünkü işleme girecek olan herhangi bir sınıf bu metodun bir parametresi olacağından, işleme girecek olan herhangi bir sınıf içinde bu overload işlemini yapabilirsiniz. Mesela ilk Add tanımlamasını TSu sınıfında da yapabilirdik. &Ccedil;ünkü TSu da işleme girmektedir. Ama artık gerek kalmadı. &Ccedil;ünkü:</p><pre>
<pre class="brush: delphi">Ayran := Yogurt + Su;</pre></pre><p
align="justify">gibi bir ifade için bu Add metodu çağırılacaktır.</p><p
align="justify">Tek parametreli operator metodlarında ise aslında mantık ile parametresinin ne olacağını bilebiliyorsunuz. Zaten yanlış bir tanımlama yaptığınızda derleyici sizi uyaracaktır. Operator metodlarının parametreleri dahil tam listesi için yardım dosyasına müracaat etmelisiniz.</p><p
align="justify">En son satırdaki Subtract operator metodu ise &quot;-&quot; çıkarma işleminin görevini görmektedir. Burada gördüğünüz örnekte iki adet TYogurt nesnesi birbirinden çıkarılıp sonuç olarak TYogurt cinsinden çıktı veriliyor. Yani:</p><pre>
<pre class="brush: delphi">Yogurt3 := Yogurt1 - Yogurt2;</pre></pre><p>gibi.. Şimdi bu metodların kod kısımlarını da verelim.</p><pre>
<pre class="brush: delphi">class operator TYogurt.Add(Yogurt: TYogurt; Su: TSu): TAyran;
begin
  Result := TAyran.Create;
  Result.Yogurt.Miktar := Yogurt.Miktar;
  Result.Su.Miktar := Su.Miktar;
end;

class operator TYogurt.Add(Yogurt1, Yogurt2: TYogurt): TYogurt;
begin
  Result := TYogurt.Create;
  Result.Miktar := Yogurt1.Miktar + Yogurt2.Miktar;
end;

class operator TYogurt.Subtract(Yogurt1, Yogurt2: TYogurt): TYogurt;
begin
  Result := TYogurt.Create;
  Result.Miktar := Yogurt1.Miktar - Yogurt2.Miktar;
end;</pre></pre><p
align="justify">Gördüğünüz gibi Result değerlerini her defasında Create ile oluşturduk. &Ccedil;ünkü Result bir nesnedir. Ve Result ile atanacak olan nesne birbirinden farklıdır. İşte record kullanmanın bir yararı da burada. Create ile ikide bir oluşturmanıza gerek yok. Record kullanmanın tek dez avantajı, forward tanımlama yapamamanız. Ama bu sorun, her bir record&#8217;u ayrı dosyalara alınarak üstesinden gelinebilir. Ben bununla kafanızı karıştırmamak için sınıf&#8217;ları ve forward tanımlamaları kullandım. Ayrıa record&#8217;un bir avantajı da sınıf gibi private, public, protected alanlarda prosedür ve fonksiyon oluşturabilmenizdir. Gerçi bu özellik operator overloading ile aynı zamanda Delphi&#8217;ye kazandırılmıştır. Her neyse&#8230;</p><h1>Implicit ve Explicit</h1><p
align="justify">Şimdi diyelimki TYogurt tipindeki nesnemizi TAyran nesnesine dönüştüreceğiz. Yani TYogurt nesnesindeki miktarı, TAyran üzerine yazacaz. Bunun için TYogurt nesnesine şunu eklemeliyiz:</p><pre>
<pre class="brush: delphi">class operator Implicit(Yogurt: TYogurt): TAyran; //Ayran := Yogurt;</pre></pre><p
align="justify">Implicit operator metodu hangi sınıf veya record içinde tanımlanmış ise o sınıf veya record&#8217;u metodun Result kısmında bulunan sınıfa dönüştürür. Örneğimiz için bu Ayran değişkene Yogurt&#8217;u atamak ile olur. Bu metodun kodları ise örneğimize göre şöyle olabilir:</p><pre>
<pre class="brush: delphi">class operator TYogurt.Implicit(Yogurt: TYogurt): TAyran;
begin
  Result := TAyran.Create;
  Result.Yogurt.Miktar := Yogurt.Miktar;
end;</pre></pre><p
align="justify">Eğer sınıf için operator overloading kullanıyorsanız, burada ve diğer tanımlamalarda dikkat etmeniz gereken nokta parametre ile gelen nesneleri direk olarak Result içine atama yapmamanız. &Ccedil;ünkü bu taktirde sınıfın işaretçisini eşitlemiş olursunuz ve işler istemediğiniz noktalara gelebilir.</p><p
align="justify">Explicit operatorü ise <strong>mantık </strong>olarak aynıdır. Sadece kullanımda fark vardır. Yani yukarıdaki &quot;Ayran := Yogurt&quot; işleminde, kullanıcının böyle kolay bir şekilde eşitlemesini istemeyebiliriz. Yani direk böyle bir eşitleme yaptığında hata yapmış olabileceğini düşündürtmek için bu kullanıma izin vermeyebiliriz. Ve sadece type casting ile tip dönüşümüne izin verebiliriz. İşte bunu bize sağlayan operator Explicit&#8217;dir.</p><p
align="justify">&quot;Ayran := Yogurt&quot; eşitlemesini &quot;Ayran := TAyran(Yogurt)&quot; şeklinde yapabilmek için sadece yukarıda yazdığımız Implicit yazan yeri Explicit olarak değiştirin:</p><pre>
<pre class="brush: delphi">class operator TYogurt.Explicit(Yogurt: TYogurt): TAyran;
begin
  Result := TAyran.Create;
  Result.Yogurt.Miktar := Yogurt.Miktar;
end;</pre></pre><p
align="justify">Kod aynı kod, ama sadece Implicit yerine Explicit var. Bunu yapmakla artık &quot;Ayran := Yogurt&quot; çevirimi derleyici tarafından hatalı olarak görülecek ve &quot;Incompatible types&quot; yani uyumsuz tipler gibi bir hata verecektir. Böylece kullanıcıyı &quot;Ayran := TAyran(Yogurt)&quot; işlemini yapmaya zorlamış oluyoruz.</p><p
align="justify">Peki neden Implicit&#8217;i kaldırdık? Hem Implicit hem de Explicit operatorunu kullanamazmıydık. Kullanabiliridik. Ama Explicit çalışmazdı. &Ccedil;ünkü Implicit daima Explicit&#8217;den önceliklidir.</p><h1>Karşılaştırma</h1><p
align="justify"> Karşılaştırma işlemleri için bir çok operator vardır. Burada hepsine değinmemiz biraz zor. Sadece makalemizin başında verdiğimiz kodda kullandığımız &quot;&gt;&quot; büyüktür karşılaştırma operatoru için overload işlemini yapalım. Bu sefer dilerseniz TAyran sınıfımızda böyle bir tanımlama yapalım:</p><pre>
<pre class="brush: delphi">  TAyran = class
    Yogurt: TYogurt;
    Su: TSu;
  public
    constructor Create;
    class operator GreaterThan(Ayran: TAyran; Yogurt: TYogurt): Boolean;
  end;</pre></pre><p
align="justify">Karşılaştırma operatorlerini overload yaparken dönüş değerinin Boolean olmasına dikkat edin. Bu operator metodunu aşağıdaki gibi kodlayabiliriz:</p><pre>
<pre class="brush: delphi">class operator TAyran.GreaterThan(Ayran: TAyran; Yogurt: TYogurt): Boolean;
begin
  if Ayran.Yogurt.Miktar &gt; Yogurt.Miktar then
    Result := True
  else
    Result := False;
end;</pre></pre><p
align="justify">Yada daha kısa ve şık bir birçimde şöyle de olabilir:</p><pre>
<pre class="brush: delphi">class operator TAyran.GreaterThan(Ayran: TAyran; Yogurt: TYogurt): Boolean;
begin
  Result := Ayran.Yogurt.Miktar &gt; Yogurt.Miktar;
end;</pre></pre><p
align="justify">İçini nasıl doldurmanız gerektiği size kalmış. Ama burada Yogurt için de büyüktür operatorünü overload yapabilirdik. Bu durumda</p><pre>
<pre class="brush: delphi">Ayran.Yogurt.Miktar &gt; Yogurt.Miktar</pre></pre><p>yazmak yerine:</p><pre>
<pre class="brush: delphi">Ayran.Yogurt &gt; Yogurt</pre></pre><p
align="justify">yazabilirdik. Gerisi size kalmış.</p><h1>Sonuç</h1><p
align="justify">Burada değinmediğimiz bir çok operatöre aşırı yükleme yapabilirsiniz. Bunların tam listesi için yardım dosyasında &quot;Operator Overloading&quot; ile ilgili kısma bakmanız yeterli.</p><p
align="justify">Tabi burada verdiğimiz sınıflar için tek tek operator tanımlaması yaptığınızda ortalık biraz karışabiliyor. Bunların önüne geçmek için operator tanımlamalarını daha çok ata sınıflarda gerçekleştirmelisiniz. Böylece çocuk sınıflarda bu tanımlamaları bir daha yapmak zorunda kalmazsınız. Mesela örneğimizde TSu ve TYogurt sınıflarının her ikisinin de Miktar isminde bir özelliği mevcut. Bu iki sınıfı TSiviMadde gibi &quot;Miktar&quot; özelliği olan bir ata sınıftan türetebilirdiniz. Ve bu operator tanımlamalarını ata sınıf için yapıp, ayrı ayrı TSu ve TYogurt için yapmanıza gerek kalmazdı.</p><p
align="justify">Yorumlarınız önemlidir, bekliyorum.</p> ]]></content:encoded> <wfw:commentRss>http://www.diyezon.com/delphi-ve-operator-overloading/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Başka bir bilgisayarda debug yapın!</title><link>http://www.diyezon.com/baska-bir-bilgisayarda-debug-yapin/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=baska-bir-bilgisayarda-debug-yapin</link> <comments>http://www.diyezon.com/baska-bir-bilgisayarda-debug-yapin/#comments</comments> <pubDate>Mon, 30 Jul 2007 15:34:15 +0000</pubDate> <dc:creator>Fatih Tolga Ata</dc:creator> <category><![CDATA[Delphi]]></category> <category><![CDATA[debug]]></category> <category><![CDATA[remote debug]]></category><guid
isPermaLink="false">http://www.diyezon.com/?p=49</guid> <description><![CDATA[Uzun uğraşlar vererek hazırladığınız projeniz üzerinde bir çok testler yaptınız, hataları bulup giderdiniz. Ardından programı son kullanıcıya verdiniz. Kullanıcı belli bir süre sonra size telefon açtı ve programın hata verdiğini söyledi. Siz de hatayı nasıl çıkarttığını öğrenip, aynı şeyi kendi bilgisayarınızda denediniz ama ne yazık ki sizin bilgisayarınızda böyle bir hata oluşmadı ve siz kafayı [...]]]></description> <content:encoded><![CDATA[<p
align="justify">Uzun uğraşlar vererek hazırladığınız projeniz üzerinde bir çok testler yaptınız, hataları bulup giderdiniz. Ardından programı son kullanıcıya verdiniz. Kullanıcı belli bir süre sonra size telefon açtı ve programın hata verdiğini söyledi. Siz de hatayı nasıl çıkarttığını öğrenip, aynı şeyi kendi bilgisayarınızda denediniz ama ne yazık ki sizin bilgisayarınızda böyle bir hata oluşmadı ve siz kafayı yeme hadlerine yaklaştınız.</p><p
align="justify">Bu senaryo hemen hemen çoğu programıcının başına gelebilecek hadiselerden. Ve çok sinir bozucu bir durum. O an herkesin içinden, son kullanıcının bilgisayarına Delphi, C++ Builder, ya da VS&#8217;yi kurup programı orada test etmek geçer. Ama bu her zaman için müsait olmayan bir durum. &Uuml;stelik kaybolan zaman da cabası.</p><p
align="justify">Bu gibi sorunların üstesinden gelebilmek için Remote Debugging denen bir sistem geliştirilmiştir. Kullanıcının bilgisayarına Remote Debugger programı kurulur ve siz kendi bilgisayarınızdan uzaktaki bilgisayara bağlanarak debug işleminizi yaparsınız. Ve hatanın ne olduğunu anlamaya çalışırsınız.</p><p
align="justify">Bir çok geliştirme ortamı remote debug işlemine izin vermektedir. Bizim bu yazıda bahsedeceğimiz işlem, Delphi ve C++ Builder için olacak. Bir kaç adımda bu işlemin nasıl yapıldığını öğrenin ve yukarıdaki senaryonun aktörlerinden biri olmayın.</p></p><p><span
id="more-49"></span></p><h1>Uzak Bilgisayarda Yapılacak İşlemeler</h1><p
align="justify">İlk önce Remote Debugger&#8217;ın kurulum programına ihtiyacımız var. Delphi ya da BCB kurulum diskinizde eğer yoksa CodeGear ya da Borland&#8217;ın sitesinden bu programı edinelim. Ardından üzerinde debug yapacağımız uzak makinada şu işlemleri gerçekleştirelim:</p><ul><li><div
align="justify">Eğer uzak bilgisayar LAN üzerinde değilse, daha rahat çalışabilmek için Hamachi kurabilirsiniz. Ama zorunlu değil.</div></li><li><div
align="justify">Uzak masaüstü bağlantısı, Vnc, DameWare ya da Radmin gibi programlar ile uzaktaki bilgisayara bağlantı kurun. Burada bu programların nasıl kurulup kullanıldığından bahsetmeyeceğim.</div></li><li><div
align="justify">Remote Debugger&#8217;ı uzak bilgisayara kurun.</div></li><li><div
align="justify">Ardından C:\Program Files\Remote Debugger altındaki rmtdbtXXX.exe programını çalıştıralım. Buradaki XXX, programın versiyonu olacak. Karşınıza bir dos penceresi çıkacak ve saatin yanında bir tray ikonu göreceksiniz.</div></li><li><div
align="justify">Ardından bu bilgisayarda &quot;DebugKullanicisi&quot; isimli bir kullanıcı oluşturun ve şifre verin. Eğer bu kulanıcının bilgisayarı kullanan kişi tarafından görünmesini istemiyorsanız Users gurubundan kaldırırmanız gerekmekte. Bu kullanıcı sadece klasör paylaşımı için kullanılacak.</div></li><li><div
align="justify">Uzak bilgisayarın C sürücüsünü, sadece &quot;DebugKullanicisi&quot;na izin vermek şartıyla paylaşıma açın. Ayrıca paylaşımı &quot;yazılabilir&quot; de yapın.</div></li><li><div
align="justify">Programınızın hangi klasörde çalışacaksa oraya dosyaları kopyalayın. Mesela &quot;C:\Program Files\ProgramAdi&quot;</div></li><li><div
align="justify">Programın uzak bilgisayarda çalışabilmesi için gereken veritabanı, bde, registry ayarları vs.. gibi işlemleri gerçekleştirin. Eğer programın çalışması için böyle şeylere ihtiyaç yoksa bu maddeyi atlayabilrsiniz.</div></li></ul><h1>Kendi Bilgisayarımızda Yapılacak Olanlar</h1><p
align="justify">Bu işlemlerden sonra artık uzaktaki bilgisayar debug yapılmak için hazır. Şimdi kendi bilgisayarımızda şu işlemleri gerçekleştirelim:</p><ul><li><div
align="justify">Map Netowork Drive(Ağ Sürücüsüne Bağlan) ile uzaktaki bilgisayarda paylaşıma açtığınız C sürücüsünü Z gibi bir sürücü ismine bağlayın. Kullanıcı adı ve şifre olarak uzaktaki bilgisayarda oluşturduğumuz &quot;DebugKullanicisi&quot;nı kullanın. Artık kendi bilgisayarımızdan &quot;Z:\&quot; ile uzaktaki bilgisayarın C sürücüsüne bağlanabiliyoruz.</div></li><li><div
align="justify">Projemizi açalım ve &quot;Project Options&quot; ile proje özelliklerini açalım. Burada Directories kısmından Output Folder olarak Z:\ altından programın çalışacağı klasörü belirleyin. Mesela &quot;Z:\Program Files\ProgramAdi&quot;</div></li><li><div
align="justify">Proje özelliklerinde Linker sekmesinde remote debugger ile ilgili kısmı seçin. RAD Studio 2007 için bu seçenek &quot;Include remote debug symbols&quot; şeklinde geçiyor.</div></li><li><div
align="justify"> Programınızı Shift+F9 ile bir kez derleyin böylece &quot;Z:\Program Files\ProgramAdi&quot; altına programın çalıştırılabilir hali oluşacaktır.</div></li><li><div
align="justify">Şimdi remote debug işlemine başlayabiliriz. Bunun için ilk önce Run menüsünden Load Process kısmına gelelim.</div></li><li><div
align="justify">Burada &quot;Remote&quot; sekmesine geçelim.</div></li><li><div
align="justify">&quot;Remote Path&quot; olarak uzak masa üstündeki programın yolunu yazalım. Burada dikkat etmeniz gereken nokta buraya &quot;Z:\&quot; üzerindeki yolu yazmamanız gerekmekte. Uzak bilgisayarda programımıza ulaşmak için hangi yol gerekiyorsa onu yazın. Mesela bizim örneğimiz için &quot;C:\Program Files\ProgramAdi\Program.EXE&quot; gibi bir şey yazmanız gerekmekte.</div></li><li><div
align="justify">&quot;Remote Host&quot; olarak uzak bilgisayarın IP&#8217;sini buraya yazın.</div></li><li><div
align="justify">Eğer gerekiyorsa &quot;Parameters&quot; ve &quot;Working Directory&quot; kısımlarını da doldurun.</div></li><li><div
align="justify">Eğer bir adım atlamadı isek &quot;Load&quot; düğmesine basabiliriz.</div></li></ul><p
align="justify">Eğer her şey yolunda gittiyse artık remote debug işlemine başladık. Uzak masaüstü bağlantısı, Vnc ya da Radmin gibi programlar ile programınızı görebelir, kullanabilir ve kendi bilgisayarınızda da debug işlemini gerçekleştirebilirsiniz. Bu arada bu işlemler, Firewall ayarları göz ardı edilerek anlatılmıştır. Gerekli port izinleri size kalmış.</p><h1>Karşılaşabileceğiniz Bazı Sorunlar</h1><p
align="justify">Bazen remote debugger inatçılık yapabiliyor. Özellikle bazı durumlarda remote debugger kilitlenip çökebiliyor. Ve tekrar remote debugger&#8217;ı çalıştırmaya kalktığınızda çalışmadığını göreceksiniz. &Ccedil;ünkü arkaplanda halen çalışmakta olduğundan ikinci bir debugger açılamıyor. Görev yöneticisi ile bu işlemi sonlandırarak tekrar açmayı denemelisiniz.</p><p
align="justify">Diğer sorunlardan biri de, debugger&#8217;ın breakpointleri bazen es geçmesi. Bu gibi bir durumda programı tekrar derlemeyi deneyebilir ve debugger&#8217;ı tekrar çalıştırmayı deneyebilirsiniz.</p><p
align="justify">Bunun gibi sorunlarınız olduğunda ingilizceniz varsa doğrudan CodeGear ekibine newsgrouplar üzerinden danışabilirsiniz. Ya da <a
target="_blank" href="http://www.delphiturkiye.com/forum">http://www.delphiturkiye.com/forum</a> adresinden karşılaştığınız sorunları iletebilirsiniz.</p><p
align="justify">Umarım faydası olmuştur.</p><p
align="justify">Hayırlı çalışmalar.</p> ]]></content:encoded> <wfw:commentRss>http://www.diyezon.com/baska-bir-bilgisayarda-debug-yapin/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Delphi programcıları, Generics ile tanışıyor&#8230;</title><link>http://www.diyezon.com/delphi-programcilari-generics-ile-tanisiyor/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=delphi-programcilari-generics-ile-tanisiyor</link> <comments>http://www.diyezon.com/delphi-programcilari-generics-ile-tanisiyor/#comments</comments> <pubDate>Wed, 25 Jul 2007 08:55:16 +0000</pubDate> <dc:creator>Fatih Tolga Ata</dc:creator> <category><![CDATA[Delphi]]></category> <category><![CDATA[.net]]></category> <category><![CDATA[.net 2.0]]></category> <category><![CDATA[generics]]></category> <category><![CDATA[highlander]]></category> <category><![CDATA[list]]></category> <category><![CDATA[sınıf]]></category><guid
isPermaLink="false">http://www.diyezon.com/?p=48</guid> <description><![CDATA[Şu an betası test edilen Highlander sürümünde göreceğimiz generic tipler, aslında c++ programcılarına pek de yabancı olmayan bir terim. .Net 2.0 ile birlikte, managed kodlar generics ile tanıştı. Haliyle Delphi de, highlander sürümü ile birlikte .net 2.0&#8242;ı ve generics&#8217;i destekliyor. Ama IDE ve kod editörü tam manasıyla generics&#8217;e destek vermeyecek. Ama derleyici olarak generics&#8217;in tüm [...]]]></description> <content:encoded><![CDATA[<p
align="justify">Şu an betası test edilen Highlander sürümünde göreceğimiz generic tipler, aslında c++ programcılarına pek de yabancı olmayan bir terim. .Net 2.0 ile birlikte, managed kodlar generics ile tanıştı. Haliyle Delphi de, highlander sürümü ile birlikte .net 2.0&#8242;ı ve generics&#8217;i destekliyor. Ama IDE ve kod editörü tam manasıyla generics&#8217;e destek vermeyecek. Ama derleyici olarak generics&#8217;in tüm özelliklerinden faydalanabileceğiz. Özellikle generics için refactoring bulunmayacak. Ayrıca help insight &#8216;da da generic tipler &quot;&lt;&gt;&quot; şeklinde görünecek. Ayrıca editor, generic tipleri generic tip olarak nitelememekte. Generic tipi ayrı bir sınıf olarak almakta. Her neyse bu gibi sorunlar bir update ile ya da 2008&#8242;in ilk çeyreğinde çıkması beklenen Tiburon sürümünde halledilecektir. Önemli olan generic tipleri derleyici bazında tam olarak kullanabileceğimiz.</p><p>Bütün bunları geçelim ve generic tiplere yabancı olanlar için bir kaç küçük örnek verelim.</p><p><span
id="more-48"></span></p><pre>
<pre class="brush: delphi">TGenericClass&lt;T&gt; = class
private
  FProperty: T;
public
  property AProperty&lt;T&gt; read FProperty write FProperty;
end;</pre></pre><p>Basit bir generic sınıf tanımlaması bu şekilde. Bu sınıfı aşağıdaki gibi kullanabiliriz.</p><pre>
<pre class="brush: delphi">var
  AStringClass: TGenericClass&lt;string&gt;;
  AIntegerClass: TGenericClass&lt;Integer&gt;;
begin
  AStringClass := TGenericClass&lt;string&gt;.Create;
  AIntegerClass := TGenericClass&lt;Integer&gt;.Create;

  AStringClass.AProperty := &#039;Bu bir string&#039;;
  AIntegerClass.AProperty := 456;
end;</pre></pre><p>Burada T parametresi bir tipi ifade etmektedir. İçine koyacağımız tipe göre sınıfımız kendini şekillendirmektedir. İstersek sınıfmızı &lt;T, U&gt; gibi birden fazla tip ile tanımlayabiliriz. Önceden de dediğim gibi eğer fikirlerini değiştirmezlerse highlander sürümünde editör, generic tipleri normal bir tip gibi algılıyor. Yani siz &lt;T, U&gt; gibi tanımladığınız generic sınıfınızı kullanırken iki yerine bir parametre kullanırsanız delphi böyle bir tanımlamanın olmadığından felan bahsedecektir. Ama eksik parametre kullanıldığını söylemesi gerekmektedir. Bu da çok karmaşık uygulamalarda sizi şaşırtabiliyor.</p><p>Aslında generic tiplerin en çok kullanıldığı yerler belki de kolleksiyon tiplerindedir. Mesela:</p><pre>
<pre class="brush: delphi">var
  AStringList: List&lt;string&gt;;
  AStreamList: List&lt;TMemoryStream&gt;;
begin
  AStringList := List&lt;string&gt;;
  AStreamList := List&lt;TMemoryStream&gt;;

  AStringList.Add(&#039;Birinci sıradaki yazı&#039;);
  AStringList.Add(&#039;İkinci sıradaki yazı&#039;);
end;</pre></pre><p
align="justify">Bu List&lt;T&gt; sınıfını kullanabilmek için System.Collections.Generic&#8217;i uses kısmına eklemelisiniz. Yukarıda gördüğünüz işlem Delphi&#8217;de TList ve pointer&#8217;lar ile yaptığımız işlemleri daha basit bir şekilde yapmamıza yarıyor. List dışında, .net kütüphanesi bir çok generic tip barındırmaktadır. Ama şimdilik highlander&#8217;da generic&#8217;lerin nasıl olacağına dair bu kadar bilgi yeterli. Highlander çıktıktan sonra bununla ilgili bir makale buralarda olabilir.</p><p
align="justify">Yorumlarınızı bekliyorum.</p></p> ]]></content:encoded> <wfw:commentRss>http://www.diyezon.com/delphi-programcilari-generics-ile-tanisiyor/feed/</wfw:commentRss> <slash:comments>3</slash:comments> </item> <item><title>Delphi ve C++ Builder ile Assembler</title><link>http://www.diyezon.com/delphi-ve-c-builder-ile-assembler/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=delphi-ve-c-builder-ile-assembler</link> <comments>http://www.diyezon.com/delphi-ve-c-builder-ile-assembler/#comments</comments> <pubDate>Wed, 20 Jun 2007 16:13:54 +0000</pubDate> <dc:creator>Fatih Tolga Ata</dc:creator> <category><![CDATA[Assembler]]></category> <category><![CDATA[Delphi]]></category> <category><![CDATA[assembly]]></category> <category><![CDATA[kod optimizasyonu]]></category><guid
isPermaLink="false">http://www.diyezon.com/?p=45</guid> <description><![CDATA[Bu makalede, başlangıç düzeyi için, Borland Inline Assembler(Basm) kullanımını göstermeye çalışacağız. Basm kullanarak Delphi&#8217;de ve C++ builderda assembler kullanarak, kodlarınızı daha da hızlandırabilirsiniz. Bildiğinizi gibi Delphi ve C++ Builder&#8217;a monte edilen FastMM projesi, temelde çokca kullanılan fonksiyonların, optimize edilmiş assembler hallerini barındırmaktadır. Böylelikle hem derleme performansı artmakta, hem de runtime&#8217;da çalışan kod daha verimli ve [...]]]></description> <content:encoded><![CDATA[<p
align="justify">Bu makalede, başlangıç düzeyi için, Borland Inline Assembler(Basm) kullanımını göstermeye çalışacağız. Basm kullanarak Delphi&rsquo;de ve C++ builderda assembler kullanarak, kodlarınızı daha da hızlandırabilirsiniz. Bildiğinizi gibi Delphi ve C++ Builder&rsquo;a monte edilen FastMM projesi, temelde çokca kullanılan fonksiyonların, optimize edilmiş assembler hallerini barındırmaktadır. Böylelikle hem derleme performansı artmakta, hem de runtime&rsquo;da çalışan kod daha verimli ve hızlı çalışmaktadır. Fakat derleyici her ne kadar kodu optimize etse de ileride göreceğiniz gibi bazı yerlerde yapay zeka yetersiz kalmaktadır. Bu yüzden bu kısımlara müdahale edip kodların boyutunu azaltıp,  performansını artırmaya çalışacağız.</p><p
align="justify">Ayrıca derleyicinin kod üretimi(code generation) safhasının nasıl işlediği hakkında da fikir sahibi olacaksınız.</p><p
align="justify">Örneklerimizi Delphi üzerinde yapacağız. Az da olsa assembler bildiğinizi kabul ediyorum.</p><p><span
id="more-45"></span></p><div><h1><strong>Fonksiyon ve Parametreler</strong></h1></div><p
align="justify">Klasik makale ve kitap yapısına aykırı olarak direk örnekler ile başlamak istiyorum. &Ccedil;ünkü bu makale, assembler diline giriş değil, assembler&rsquo;ın Delphi ve CB&rsquo;de kullanımı ile ilgilidir.</p><p
align="justify">Şimdi yeni bir uygulama açalım ve şöyle bir fonksiyon yazalım:</p><div><pre>
<pre class="brush: delphi">function CarpBakalim(Sayi: Integer): Integer;
begin
  Result := Sayi * 2;
end;</pre></pre></div><div
align="justify">Forma bir button ekleyelim ve bir parametre ile birlikte bu fonksiyonu OnClick olayında çağıralım. Şimdi Fonksiyon içinde ilk satıra breakpoint koyalım ve programı çalıştıralım. Button&rsquo;a basıp breakpoint koyduğumuz yere gidelim. Eğer CPU penceresi açık değilse View menüsünden ya da ctrl+alt+c ile CPU penceresini açalım. CPU penceresinde Ctrl+O ile mevcut satıra gidebilirsiniz.</div><div><strong> <img
width="531" vspace="0" hspace="0" height="184" border="0" src="/wp-content/uploads/Image/post45/1.jpg" /> </strong></div><p
align="justify">Buradan gördüğümüz kadarı ile &ldquo;Sayi&rdquo; parametresi, eax register&rsquo;ı ile geliyor ve tekrar çıktı olarak eax olarak çıkıyor. Bu register çağırımı, Delphi&rsquo;de default olarak kullanılır. Burada yapılan işlem çok basittir. 2 ile çarpmak için, değişkenin kendisini kendisi ile toplatılıyor. Ret ile de tekrar çalışma satırına dönülüyor. Şimdi buna göre bunu inline assembler ile nasıl yapabileceğimize bakalım:</p><pre>
<pre class="brush: delphi">function CarpBakalimADD(Sayi: Integer): Integer;
asm
  ADD eax,eax//Result := Sayi * 2;
end; //ret</pre></pre><div
align="justify">Şimdi form üzerine bir edit koyalım ve buttonun OnClick olayını aşağıdaki gibi değiştirelim:</div><pre>
<pre class="brush: delphi">procedure TForm1.Button1Click(Sender: TObject);
var
  Sayi1, Sayi2: Integer;
begin
  Sayi1 := StrToInt(Edit1.Text);
  Sayi2 := CarpBakalimADD(Sayi1);
  Edit1.Text := IntToStr(Sayi2);
end;</pre></pre><div
align="justify">Bir breakpoint ile CPU pencersinde şu satırlara bakalım:</div><pre>
<pre class="brush: delphi">call StrToInt
call CarpBakalimADD
mov esi, eax</pre></pre><p
align="justify">StrToInt çağırımından sonra sonuç eax içine alınıyor. Bir önceki örnekte CarpBakalimADD fonksiyonumuz da eax&rsquo;dan parametre alıp, yine eax&rsquo;a çıktı verdiğini gördük. Böylece ikinci satırdaki fonksiyon çağırımından sonra sonuç, eax içinde olacaktır. En son satırda da eax, esi içine kopyalanıyor.</p><p
align="justify">İki ile çarpma işlemi iki yolla daha yapılabilir. Bir tanesi mul komutunu kullanarak, diğeri shift ile kaydırarak. Mul komutu eax içindeki değeri başka bir register&rsquo;daki değer ile çarpar ve sonucu edx:eax register çiftinde tutar. Sonuç için iki çift register gereklidir çünkü iki adet 32 bit sayı çarpıldığından maksimum 64 bit olacaktır.</p><p
align="justify"><strong>Bir asm bloğu için altın kural, EDI, ESP, ESI, EBX ve EBP registerlarını korumasıdır. Ama EAX, ECX ve EDX registerlarını da özgürce değiştirebilmesidir. Bu açıklamayı Delphi yardım dosyalarından da bulabilirsiniz.</strong></p><p
align="justify">Mul komutunun edx ve eax&rsquo;ı kullandığından bahsetmiştik. Öyle ise yukarıdaki tanıma göre, fonksiyonumuzu mul ile de yapabiliriz:</p><pre>
<pre class="brush: delphi">function CarpBakalimAsmMUL(Sayi: Integer): Integer;
asm
  MOV ecx,2
  MUL ecx
end;</pre></pre><p
align="justify">Ekstradan ecx de kullanıyoruz ama bu sorun değil. &Ccedil;ünkü ecx&rsquo;i de istediğimiz gibi değiştirebiliriz. Altın kuralı hatırlayın.</p><p
align="justify">Sonucumuz eax&rsquo;in içine <strong>sığabilecek</strong> durumda olduğunda, mul komutunun sonucu eax&rsquo;de olacaktır. &Ccedil;ünkü edx:eax sonuç çiftinde, edx sayının yüksek kısmını, eax ise düşük kısmını tutacaktır. Ama sonuç, eax&rsquo;e sığamayacak kadar <strong>büyükse</strong>, yani sonucun yüksek kısmı edx&rsquo;e aktarıldığında, fonksiyonun çıktısı yanlış olacaktır. &Ccedil;ünkü fonksiyon çıktısı eax&#8217;de çıkmaktadır.</p><div
align="justify">Aynı şeyi shift ile kaydırarak yapalım.</div><pre>
<pre class="brush: delphi">function CarpBakalimAsmSHL(Sayi: Integer): Integer;
asm
  SHL eax,1
end;</pre></pre><div
align="justify">Şimdi bu üç tanımlamadan hangisinin hızlı olduğu bizim için önemli. Intel ve AMD dökümanlarından hangisinin daha hızlı olduğunu latency(saykıl) değerlerini hesaplayarak anlayabiliriz. P4 işlemci ile add ve mov komutları 0.5 saykıl gecikmeye sahip. Mul komutu ise 14-18 saykıl arasında değişiyor. Shl ise 4 saykıl gecikmeye sahip. Delphi&rsquo;nin seçimi bu değerlere göre en hızlı ve en az yer tutan seçenektir. Aynı şey P3 ve Amd için de geçerlidir.</div><div><div></div></div><div></div><div><h1><strong>Basit bir Optimizasyon</strong></h1></div><div
align="justify">Aşağıdaki bölme işlemine göz atalım:</div><pre>
<pre class="brush: delphi">function BolBakalim(Bolunen, Bolen: Integer): Integer;
begin
  Result := Bolunen div Bolen;
end;</pre></pre><div
align="justify">Derleyici bu kodu aşağıdaki gibi derleyecektir.(CPU penceresinden bakabilirsiniz).</div><pre>
<pre class="brush: delphi">function BolBakalim(Bolunen, Bolen: Integer): Integer;
begin{
push ebx
mov  ebx,edx
mov  ecx,eax }
  Result := Bolunen div Bolen; {
  mov  eax,ecx
  cdq
  idiv ebx}
end;{
pop  ebx
ret}</pre></pre><p
align="justify">Eğer sağlam bir optimize işlemi yapmak istiyorsanız, ilk başta optimize yapacağınız kodları güzel bir şekilde analiz yapmalısınız. Şimdiki gelecek bir kaç paragraf kodların nasıl çalıştığını anlatmaktadır.</p><p
align="justify">İlk baştaki begin&rsquo;den sonra gelen 3 satırlık assembler kodları, bir çeşit hazırlık kodlarıdır. Önceki örnekte altın kuralımızda bahsettiğimiz gibi EBX register&rsquo;ı serbest bir şekilde değiştirilemez. Serbest bir şekilde sadece EAX, ECX ve EDX register&rsquo;larını değiştirebildiğimizi hatırlayınız. EBX register&rsquo;ı ilerleyen satırlarda IDIV tarafında kullanılması gerekecek. Bu yüzden ebx&rsquo;i stack üzerine almamız gerekecek. Bunu ilk satırdaki &quot;push ebx&quot; talimatı ile yapıyoruz.</p><p
align="justify">Stack&rsquo;a ebx&rsquo;i atmak ile, stack işaretçisinin değeri &ldquo;4&rdquo; byte düşüyor. Bildiğiniz gibi Intel tabanlı işlemcilerde stack işaretçisi, stack&rsquo;a bir değer atmakla yükselmez, tam tersine değer <strong>düşer</strong>. Her register 32 Bit olduğundan, bir register&rsquo;ı stack&rsquo;a atmak, işaretçi değerini 32 bit yani 4 byte <strong>azaltır</strong>.</p><p
align="justify">Fonksiyonun iki parametresi başlangıçta eax ve edx registerlarında saklanırlar. Bolunen parametresi eax içinde, Bolen parametresi de edx içinde olması, Delphi&rsquo;nin <strong>standart register çağrımları</strong> tarafından belirlenmiştir. &quot;push ebx&quot; talimatından sonra gelen iki adet mov komutu ile edx ve eax registerları, ebx ve ecx içine kopyalanıyor.</p><p
align="justify">Bu hazırlık aşamasından sonra esas bölme işlemini yapan komutlarımız sonraki gelen 3 satırdır. Bu satırların ilkinde ecx&rsquo;i tekrar eax üzerine kopyalandığını görüyorsunuz (<font
face="Courier New">mov eax, ecx</font>). Bu satır gereksizdir. &Ccedil;ünkü eax değişmemiştir.</p><p
align="justify">Cdq komutuna geçmeden önce idiv&rsquo;den bahsetmek istiyorum. Idiv komutu, edx:eax değer çiftindeki 64 Bitlik sayıyı, bu örnekteki 32 Bitlik sayı olan ebx&rsquo;e bölmektedir. Yani Bolen ebx&rsquo;de ve Bolunen ise edx:eax çiftinde olmalıdır. Idiv komutu edx:eax çiftini, ebx ile böler. İşte cdq burada devreye giriyor. Cdq komutu, eax içinde bulunan sayıyı <strong>işareti </strong>ile birlikte edx:eax register çiftine taşır. Yani Dword(32Bit) olan bir değeri QWord(64Bit) yapar. Cdq komutu, idiv&rsquo;den önce gereklidir. &Ccedil;ünkü eax&rsquo;in 31&rsquo;inci biti <strong>işaret </strong>bitidir. Bu yüzden eax&rsquo;deki değeri edx:eax register çiftine taşıyabilmek için sayının işaretini de göz önünde bulundurmalıyız. &Ccedil;ünkü edx:eax register çifti için, 63&rsquo;üncü bit yani, edx&rsquo;in 31&rsquo;inci biti, sayının işaret biti olmuştur artık. İşareti ile edx:eax&#8217;e taşıma işlemini ise cdq yapmaktadır.</p><p
align="justify">Idiv komutunun sonucu yani bölüm değeri, eax içinde bulunur. Bölme işleminin kalan değer ise edx içinde bulununur.</p><p
align="justify">Bizim için önemli olan şey, eax içinde sonuç değerimiz ile fonksiyondan çıkış yapmamızdır. Zaten idiv komutunun sonucu da eax içinde bulunmaktadır. Yani yapacağımız bir işlem kalmadı. Fonksiyonun sonucu eax içinde çıktı vermektedir.</p><p
align="justify">En sonda bulunan iki satırlık kod ise, bitiş kodlarıdır. Hazırlık kodları ile ebx&rsquo;i stack&rsquo;a atmıştık, şimdi tekrar ebx&rsquo;in ilk baştaki değerini stack&rsquo;dan almalıyız. Bunun için pop komutunu kullanıyoruz ve ret ile de fonskiyondan çıkıyoruz.</p><p
align="justify">Şimdi bu kodlar ile fonksiyonumuzun assembler halini yazalım. Yapacağımız işlem oldukça basit sadece begin yerine asm yazıyoruz ve ret komutu yerine end ile fonksiyonumuzu bitiriyoruz.</p><pre>
<pre class="brush: delphi">function BolBakalimAsm(Bolunen, Bolen: Integer): Integer;
asm
  PUSH ebx
  MOV  ebx,edx //edx: Bolen
  MOV  ecx,eax //eax: Bolunen
  //Result := Bolunen div Bolen;
//  MOV eax,ecx
  CDQ
  IDIV ebx
  POP  ebx
end;</pre></pre><p
align="justify">Kodları incelerken bir satırın fazlalık olduğunu görmüştük. Burayı açıklama satırı ile çıkardık.</p><p
align="justify">Ayrıca farkında iseniz ecx hiç kullanılmıyor. Tek yaptığımız edx:eax&rsquo;i ebx&rsquo;e bölmek. Bu yüzden eax&rsquo;deki Bolunen parametresini ecx&rsquo;de saklamamıza gerek yok:</p><pre>
<pre class="brush: delphi">function BolBakalimAsm(Bolunen, Bolen: Integer): Integer;
asm
  PUSH ebx
  MOV  ebx,edx//edx: Bolen
//  MOV ecx, eax //eax: Bolunen
 //Result := Bolunen div Bolen;
//  MOV eax,ecx
  CDQ
  IDIV ebx
  POP  ebx
end;</pre></pre><p
align="justify">Farkında iseniz, ecx register&rsquo;ı boşa çıktı. Altın kurala göre ecx&rsquo;i serbestçe kullanabildiğimizi biliyoruz. Ama ebx öyle değildi. Bu yüzden ebx&rsquo;i kullanabilmek için stack&rsquo;da değerini saklayıp ardından tekrar stack&rsquo;dan değerini okutmalıydık. Ama ecx boşa çıktığına göre artık ebx kullanmamıza gerek kalmadı. Haliyle ebx yerine ecx kullanacağımızdan, ebx&rsquo;i stack&rsquo;de saklamak için pop ve push komutlarına da ihtiyacımız yok artık:</p><pre>
<pre class="brush: delphi">function BolBakalimAsm(Bolunen, Bolen: Integer): Integer;
asm
//  PUSH ebx
  MOV ecx,edx//edx: Bolen
//  MOV ecx, eax //eax: Bolunen
 //Result := Bolunen div Bolen;
//  MOV eax,ecx
  CDQ
//  IDIV ebx
  IDIV ecx
//  POP ebx
end;</pre></pre><p
align="justify">Yeni kodlarımıza göre artık edx:eax çiftini ecx register&rsquo;ına bölüyoruz. Kodları temizlediğimizde, optimize edilmiş son kodumuz şu şekilde olacaktır:</p><pre>
<pre class="brush: delphi">function BolBakalimAsm(Bolunen, Bolen: Integer): Integer;
asm
  MOV  ecx,edx //edx: Bolen
  CDQ  //eax =&gt; edx:eax
  IDIV ecx //Result(eax) := Bolunen(edx:eax) div Bolen(ecx);
end;</pre></pre><div></div><p
align="justify">Gördüğünüz gibi fonksiyonumuz daha hızlı ve daha az yer kaplayarak optimize edilmiş duruma geldi.</p><div></div><div><h1><strong>Lokal Sabitler ve Virgüllü Sayılar</strong></h1></div><div
align="justify">Aşağıdaki fonksiyona göz atalım:</div><pre>
<pre class="brush: delphi">function BirFonksiyon(Param: Double): Double;
const
  LocalConst1: Double = 1;
  LocalConst2: Double = 2;
  LocalConst3: Double = 3;
begin
  Result := LocalConst1*Param*Param + LocalConst2*Param + LocalConst3;
end;</pre></pre><div
align="justify"> CPU penceresinden aldığımız derlenmiş kodlar aşağıdaki gibidir:</div><pre>
<pre class="brush: delphi">function BirFonksiyonAsm(Param: Double): Double;
const
  LocalConst1: Double = 1;
  LocalConst2: Double = 2;
  LocalConst3: Double = 3;
begin{
  push  ebp
  mov   ebp,esp
  add   esp,-$08}
  Result := LocalConst1*Param*Param + LocalConst2*Param + LocalConst3;
  {
  fld   qword ptr [LocalConst1]
  fmul  qword ptr [ebp+$08]
  fmul  qword ptr [ebp+$08]
  fld   qword ptr [LocalConst2]
  fmul  qword ptr [ebp+$08]
  faddp st(1)
  fadd  qword ptr [LocalConst3]
  fstp  qword ptr [ebp-$08]
  wait
  fld   qword ptr [ebp-$08]
  }
end;{
  pop   ecx
  pop   ecx
  pop   ebp}</pre></pre><div
align="justify">İlk başta &ldquo;begin&rdquo; kelimesinin karşılığı olan 3 satırlık koda bakalım:</div><pre>
<pre class="brush: delphi">
push ebp
mov  ebp, esp
add  esp, -$08</pre></pre><div
align="justify">Burada yapılan kısaca, stack üzerinden yer ayırmak içindir. Ayrılan bu parçaya <strong>stackframe</strong> denilmektedir. Bir stackframe&rsquo;de iki adet işaretçi bulunur. Birisi, taban işaretçisi, diğeri stack işaretçisidir. Taban işaretçisi(<strong>b</strong>ase<strong>p</strong>ointer) e<strong>bp</strong> içinde bulunurken, stack işaretçisi(<strong>s</strong>tack<strong>p</strong>ointer) ise e<strong>sp</strong> içinde bulunur. Taban işaretçisi, stack&rsquo;ın hafızadaki ilk hücresine işaret eder. Yani ebp içinde stack&rsquo;ın başlangıç adresi tutulur. Aynı şekilde, stack işaretçisi ise, stack&rsquo;ın mevcut pozisyonuna işaret eder. Push ve pop komutu ile stack işaretçisinin gösterdiği yer, azalır ve artar.</div><p
align="justify">İlk satırdaki kod ile ebp stack üzerine saklanıyor. &Ccedil;ünkü bu değer fonksiyondan çıkıldığında eski değerine tekrar kavuşmalıdır. Taban işaretçisinin yeni değeri ikinci satırdaki mov komutu ile esp&rsquo;deki değer olarak belirleniyor. Yani stack işaretçisinin değeri, taban işaretçisine kopyalanıyor. Böylelikle, taban işaretçisi olan ebp, stack&rsquo;ın tepe noktasına işaret ediyor. Tabi hatırlarsanız stack, Intel işlemcilerde geriye doğru işlemektedir. Yani stack&rsquo;ın tepe noktası, sayı değeri olarak en düşük noktadır. Son satırdaki komut ile, stackpointer 8 byte kadar ilerletiliyor.</p><p
align="justify">Bu üç satırlık kod ile fonksiyonumuz için bir stackframe oluşturulmuş oluyor. Şimdi esas işlemi yapan kodlara bakalım:</p><pre>
<pre class="brush: delphi">
fld   qword ptr [LocalConst1]
fmul  qword ptr [ebp+$08]
fmul  qword ptr [ebp+$08]
fld   qword ptr [LocalConst2]
fmul  qword ptr [ebp+$08]
faddp st(1)
fadd  qword ptr [LocalConst3]
fstp  qword ptr [ebp-$08]
wait
fld   qword ptr [ebp-$08]</pre></pre><p
align="justify">Burada görülenler basit bir virgüllü sayı hesabıdır. İlk satırdaki fld komutu, LocalConst1 isimli sabiti, <strong>virgüllü sayı stack</strong>&rsquo;ının <strong>tepesine </strong>taşır. Buradadaki komutlar, bildiğimiz mul, add gibi komutların virgüllü sayı (floating point) için olan versiyonlarıdır. Başlarına &ldquo;f&rdquo; harfi getirilmesi ile bu fonksiyonların floating point yani virgüllü sayı işlemi yapan komutlar olduğunu anlıyoruz.</p><p
align="justify">Bizim için en mühim nokta &ldquo;qword ptr [ebp+$08]&rdquo; gibi ifadelerdir. &ldquo;Qword ptr&rdquo;, sonrasında gelecek olan değerin tipini belirtmektedir. Qword ile değerin 64 bit yani 8 byte&rsquo;lık bir değer olduğunu, ptr ile de, değerin aslında başka bir değere işaret eden bir adres olduğunu gösterir.  Sonrasında gelen [ ] köşeli parantezleri içine yazılan değer, işaret edilen adrestir. Yani [ebp+$08] ile ifade edilen, ebp&rsquo;den 8 byte yani 64bit önceki değerdir. Bildiğiniz gibi ebp, bu fonksiyon için oluşturulan stackframe&rsquo;in başlangıç adresini tutmaktadır. Öyle ise ebp&rsquo;den 8 byte önceki değer, önceki stackframe&rsquo;e işaret etmektedir. Fonksiyonumuzun Double tipinde bir parametre aldığını biliyoruz. Önceki örneklerimizde, fonksiyon parametreleri eax, ecx gibi registerlarda saklanıyordu. Ama Double tipi 64bit uzunluğundadır. Ve eax gibi registerlar ise 32bit uzunluğundadır. Double tipi bu registerlara sığmayacağından, Delphi burada bir seçim yapmıştır ve, parametreleri stack üzerine taşımıştır. Tabi burada double tipindeki bir değer için stack yerine, <strong>virgüllü sayı registerları</strong> kullanılsa idi daha verimli ve hızlı olacaktı. Borland&#8217;ın seçimi stack çözümü üzerine olmuştur. Kısacası, [ebp+$08] ile ifade edilen, fonksiyonumuzun ilk ve tek parametresi olan &quot;Param&quot;dır. &ldquo;qword ptr&rdquo; ise bu parametrenin 64bit uzunluğunda olduğunu ifade ediyor.</p><p
align="justify">&ldquo;faddp st(1)&rdquo; satırına bakalım. &ldquo;st(1)&rdquo;, <strong>ikinci sıradaki virgüllü sayı registerı</strong>dır. İkinici sıradakidir, çünkü birincisi st(0) register&rsquo;ıdır. Virgüllü sayı registerları bir stack üzerinde birleşik halde bulunurlar. Ve virgüllü sayı komutları, dolaylı olarak bu stack&rsquo;ın tepesinde bulunan st(0) ile işlem yaparlar. Fld komutu, değeri virgüllü sayı stack&rsquo;inin tepesine yani st(0)&rsquo;a yerleştiriyor. Bunu ilk satırdaki fld komutu ile gördük. Fmul işlemleri yapıldığında sonuç yine st(0) üzerinde duracaktır. &Ccedil;ünkü <strong>virgüllü sayı komutları</strong> st(0) ile işlem yapıyorlardı. İki fmul komutundan sonra gelen fld komutu LocalConst2 değerini st(0)&rsquo;a yerleştirir. Haliyle st(0)&#8217;da bulununan önceki değer st(1)&#8217;e kayar. Ardından gelen fmul ile işleminin sonucu st(0)&rsquo;da depolanır. Devamında gelen &ldquo;faddp st(1)&rdquo; ile st(0)&rsquo;da bulunan değer ile st(1) toplanır ve toplam sonucu st(0) üzerine kaydedilir.  st(1) içinde &ldquo;LocalConst1*Param*Param&rdquo; sonucu olduğunu biliyoruz. &Ccedil;ünkü birinci sıraya &ldquo;LocalConst2&rdquo; fld ile yerleştirilmiş, &ldquo;LocalConst1*Param*Param&rdquo; sonucu st(0)&#8217;dan st(1)&#8217;e kaymıştır.</p><p
align="justify">&ldquo;faddp&rdquo; komutundaki &ldquo;p&rdquo; harfi, st(1)&rsquo;ın stack&rsquo;dan çıkarılması yani &ldquo;pop&rdquo; yapılmasını sağlar. Böylece sonuç st(0) üzerinde durmakla birlikte, st(1)&#8217;de stack&#8217;dan silinecektir.</p><p
align="justify">Buraya kadar olan komutlar &ldquo;LocalConst1*Param*Param + LocalConst2*Param&rdquo; işlemi gerçekleştirdi ve sonucu st(0) üzerine kaydetti. Sonrasında gelen fadd ile LocalConst3 değeri st(0) üzerine eklenmiştir. Fonksiyonun çıktısı st(0) üzerinden verilmektedir.</p><p
align="justify">Bu işlemleri FPU ve CPU pencerelerinden debug yaparak takip edebilirsiniz.</p><p
align="justify">Sonrasında gelen 3 satırlık kod gereksiz parçalar taşımaktadır. &ldquo;fstp&rdquo; kısmına bakalım. &ldquo;fstp&rdquo; ile sonuç, fonksiyonun stackframe&rsquo;i üzerine taşınırken, fld ile tekrar stacktan yükleniyor. Bu işlem gereksizdir. Arada bulunan wait komutu gereklidir. &Ccedil;ünkü floating point komutları sırasında herhangi bir hata olup olmadığının kontrolü gerekmektedir. Wait komutu için ayrıntılı bilgi Intel&rsquo;in yardım dosyalarında bulunmaktadır.</p><div
align="justify">Son olarak, fonksiyonumuzu sonlandıran:</div></p><pre>
<pre class="brush: delphi">
pop  ecx
pop  ecx
pop  ebp
</pre></pre><p
align="justify">bu komutlar, stackframe&rsquo;i ortadan kaldırıyor. Ard arda iki adet pop çağrılmak ile esp&rsquo;ye başta eklenen 8 byte geriye çekiliyor, yani eski pozisyonuna geliyor. Sonrasında ise ilk başta stack üzerine kaydettiğimiz ebp&rsquo;nin eski değeri tekrar yükleniyor.</p><p
align="justify">Şimdi bu fonksiyonu assembler versiyonunu yazalım ve optimize edelim:</p><div><pre>
<pre class="brush: delphi">function BirFonksiyonAsm(Param: Double): Double;
const
  LocalConst1: Double = 1;
  LocalConst2: Double = 2;
  LocalConst3: Double = 3;
asm
  //begin
  push  ebp
  mov   ebp,esp
  add   esp,-$08

  //Result := LocalConst1*Param*Param + LocalConst2*Param + LocalConst3;
  fld   qword ptr [LocalConst1]
  fmul  qword ptr [ebp+$08]
  fmul  qword ptr [ebp+$08]
  fld   qword ptr [LocalConst2]
  fmul  qword ptr [ebp+$08]
  faddp st(1)
  fadd  qword ptr [LocalConst3]
  fstp  qword ptr [ebp-$08]
  wait

  //end
  fld   qword ptr [ebp-$08]
  pop   ecx
  pop   ecx
  pop   ebp
end;</pre></pre></div><p
align="justify">Bunu derlemeye kalktığınızda &ldquo;faddp st(1)&rdquo;in tanınamadığına dair bir hata mesajı alacaksınız ve bu kodları derleyemeyeceksiniz. Intel dökümanlarına baktığınızda, faddp komutunun sadece bir çeşitinin olduğunu göreceksiniz. Yani başka kullanımı yoktur. Yani her zaman st(1) ile st(0)&rsquo;ı toplar ve sonucu st(0)&rsquo;a yazar. Bu yüzden st(1)&rsquo;i yazmaya gerek yoktur. st(1) yazan kısmı açıklama satırı yapıyoruz. Bundan sonra program derlenecektir.</p><p
align="justify">Bu fonksiyonu 2 değerini verek çağırdığımızda sonucun &ldquo;1*2*2 + 2*2 + 3 = 11&ldquo; olması gerekirken 3 sonucunu verdiğini göreceğiz. Peki nerede yanlış yaptık?</p><p
align="justify">FPU ve CPU pencereleri ile adım adım debug yaptığımızda bu sorunu çözebileceğiz. CPU View&rsquo;e bakarsanız baş tarafa eklediğimiz &ldquo;push ebp&rdquo; ve &ldquo;mov ebp, esp&rdquo; talimatları iki defa tekrar etmiş. Biz kodları direk kopyala yapıştır ile aldığımızdan, derleyicinin otomatik olarak eklediği bu kodlarını da almış olduk. Bu yüzden bu ilk iki satırı açıklama satırı ile kadırmalıyız. &Ccedil;ünkü zaten derleyici bu satırları ekliyor. Fonksiyonun girişinde ebp&rsquo;nin otomatik olarak stack&rsquo;a alınması ile haliyle bizim tekrar ebp&rsquo;yi en son satırda pop yapmamız da gereksizdir.</p><p
align="justify">Bu sorunu hallettikten sonra pascal versiyonu ile aynı sonuca ulaşıyoruz. Artık optimize işlemine geçebiliriz.</p><p
align="justify">İlk başta en son satırlarda bulunan gereksiz fstp ve fld komutlarını çıkaralım. Bunların neden gereksiz olduğunu yukarıda bahsettik.</p><div><pre>
<pre class="brush: delphi">function BirFonksiyonAsm(Param: Double): Double;
const
  LocalConst1: Double = 1;
  LocalConst2: Double = 2;
  LocalConst3: Double = 3;
asm
  //begin
//  push  ebp
//  mov   ebp,esp
  add   esp,-$08

 //Result := LocalConst1*Param*Param + LocalConst2*Param + LocalConst3;
  fld   qword ptr [LocalConst1]
  fmul  qword ptr [ebp+$08]
  fmul  qword ptr [ebp+$08]
  fld   qword ptr [LocalConst2]
  fmul  qword ptr [ebp+$08]
  faddp //st(1)
  fadd  qword ptr [LocalConst3]
  //  fstp qword ptr [ebp-$08]
  wait

//end
//  fld   qword ptr [ebp-$08]
  pop   ecx
  pop   ecx
//  pop   ebp
end;</pre></pre></div><p
align="justify">Bu işlemden sonra kodlara baktığımızda tüm işlemlerin virgüllü sayı registerları ile yapıldığını görüyoruz. Yani ek bir stackframe&rsquo;e ihtiyaç yok. Bu yüzden stack frame ekleyen &ldquo;add esp, -$08&rdquo; satırını da kaldırmalıyız. Haliyle esp değişmediğinden en sonda bulunan iki adet pop komutu ile esp&rsquo;nin eski değerini tekrar yüklememize de gerek yok.</p><div><pre>
<pre class="brush: delphi">function BirFonksiyonAsm(Param: Double): Double;
const
  LocalConst1: Double = 1;
  LocalConst2: Double = 2;
  LocalConst3: Double = 3;
asm
  //begin
//  push  ebp
//  mov   ebp,esp
//  add   esp,-$08

 //Result := LocalConst1*Param*Param + LocalConst2*Param + LocalConst3;
  fld   qword ptr [LocalConst1]
  fmul  qword ptr [ebp+$08]
  fmul  qword ptr [ebp+$08]
  fld   qword ptr [LocalConst2]
  fmul  qword ptr [ebp+$08]
  faddp //st(1)
  fadd  qword ptr [LocalConst3]
//  fstp qword ptr [ebp-$08]
 wait
 //end
//  fld   qword ptr [ebp-$08]
//  pop   ecx
//  pop   ecx
//  pop   ebp
end;</pre></pre></div><div
align="justify"><p> Şimdi açıklama satırlarını temizleyip optimize edilmiş kodumuzu analiz etmeye devam edelim.</p></div><div><pre>
<pre class="brush: delphi">function BirFonksiyonAsm(Param: Double): Double;
const
  LocalConst1: Double = 1;
  LocalConst2: Double = 2;
  LocalConst3: Double = 3;
asm
  fld   qword ptr [LocalConst1]
  fmul  qword ptr [ebp+$08]
  fmul  qword ptr [ebp+$08]
  fld   qword ptr [LocalConst2]
  fmul  qword ptr [ebp+$08]
  faddp
  fadd  qword ptr [LocalConst3]
  wait
end;</pre></pre></div><div
align="justify"><p> Bu kodlara baktığımızda &ldquo;Param&rdquo; parametresi üç defa üst üste fmul komutu ile hafızadan FPU&rsquo;ya aktarılıyor. Hafızadan alma işlemini bir defa yapıp, sonrasında bunu tekrar kullanmamız daha verimli ve hızlı olacaktır. Bunun için ilk başta &ldquo;Param&rdquo; parametresini fld ile st(0) içine alalım ve hep bunu kullanalım.</p></div><div><pre>
<pre class="brush: delphi">function BirFonksiyonAsm(Param: Double): Double;
const
  LocalConst1: Double = 1;
  LocalConst2: Double = 2;
  LocalConst3: Double = 3;
asm
  fld   qword ptr [ebp+$08] //Param&#039;ı st(0)&#039;a at
  fld   qword ptr [LocalConst1] //st(0)&#039;a LocalConst1 atıldı, Param şimdi st(1)&#039;e kaydı
  fmul  st(0), st(1) //LocalConst ile Param&#039;ı çarp sonucu st(0)&#039;a at
  fmul  st(0), st(1) //st(0) ile Param&#039;ı çarp sonucu st(0)&#039;a at
  fld   qword ptr [LocalConst2] //LocalConst2 st(0)&#039;da, sonuç st(1)&#039;de ve Param st(2)&#039;de
  fmul  st(0), st(2) //LocalConst2 ile Param yani st(2)&#039;yi çarp ve sonucu st(0)&#039;a at.
  ffree st(2) //Param&#039;ı boşalt.
  faddp //st(0) ile st(1)&#039;i topla ve sonucu st(0)&#039;e at ve st(1)&amp;rsquo;i boşalt.
  fadd  qword ptr [LocalConst3] //st(0) ile LocalConst3&#039;ü topla ve sonucu st(0)&#039;a at.
  wait
end;</pre></pre></div><p
align="justify">Kodların açıklamaları yanlarında mevcut. Burada ekstradan ffree adlı komutu gördük. Kodlar bu haliyle optimize edilmiş gibi.</p><p
align="justify">Son olarak bir optimizasyon daha yapalım. LocalConst1 sabitimizin değeri 1&rsquo;dir. Bu değeri fld ile hafızadan yüklemek yerine, st(0)&rsquo;ı bir yapan fld1 komutu kullanılabilir. Böylelikle hem hafıza kullanımını hem de saykıl gecikmesini azaltmış olacağız. Kodlarımızın son hali şu şekilde olacaktır:</p><div><pre>
<pre class="brush: delphi">function BirFonksiyonAsm(Param: Double): Double;
const
//  LocalConst1: Double = 1;
  LocalConst2: Double = 2;
  LocalConst3: Double = 3;
asm
  fld   qword ptr [ebp+$08] //Param&#039;ı st(0)&#039;a at
//  fld   qword ptr [LocalConst1] //Param şimdi st(1)&#039;de
  fld1
  fmul  st(0), st(1) //LocalConst ile Param&#039;ı çarp sonucu st(0)&#039;a at
  fmul  st(0), st(1) //st(0) ile Param&#039;ı çarp sonucu st(0)&#039;a at
  fld   qword ptr [LocalConst2] //LocalConst2 st(0)&#039;da, sonuç st(1)&#039;de ve Param st(2)&#039;de
  fmul  st(0), st(2) //LocalConst2 ile Param yani st(2)&#039;yi çarp ve sonucu st(0)&#039;a at.
  ffree st(2) //Param&#039;ı boşalt.
  faddp //st(0) ile st(1)&#039;i topla ve sonucu st(0)&#039;e at.
  fadd  qword ptr [LocalConst3] //st(0) ile LocalConst3&#039;ü topla ve sonucu st(0)&#039;a at.
  wait
end;</pre></pre></div><p
align="justify">Bu bölümde bu kadar yeterli. Gelecek bölümlerde if-else, while-do, for döngüsü gibi yapılara ve sse, sse2 ve mmx gibi teknolojilere de göz atmaya çalışacağız.</p></p> ]]></content:encoded> <wfw:commentRss>http://www.diyezon.com/delphi-ve-c-builder-ile-assembler/feed/</wfw:commentRss> <slash:comments>11</slash:comments> </item> </channel> </rss>
<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Minified using memcached
Page Caching using memcached
Database Caching 1/49 queries in 0.078 seconds using memcached
Object Caching 898/1017 objects using memcached

Served from: www.diyezon.com @ 2012-02-08 11:42:14 -->
