<?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</title>
	<atom:link href="http://www.diyezon.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.diyezon.com</link>
	<description>programlama sanatı...</description>
	<lastBuildDate>Wed, 16 Sep 2009 14:13:58 +0000</lastBuildDate>
	
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>7 Adımda Delphi Uygulamalarını Daha da Hızlandırın</title>
		<link>http://www.diyezon.com/2009/02/24/7-adimda-delphi-uygulamalarini-daha-da-hizlandirin/</link>
		<comments>http://www.diyezon.com/2009/02/24/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 uygulama [...]]]></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/2009/02/24/7-adimda-delphi-uygulamalarini-daha-da-hizlandirin/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Derleyici Tasarım Notları &#8211; 1</title>
		<link>http://www.diyezon.com/2009/02/22/derleyici-tasarim-notlari-1/</link>
		<comments>http://www.diyezon.com/2009/02/22/derleyici-tasarim-notlari-1/#comments</comments>
		<pubDate>Sun, 22 Feb 2009 00:45:26 +0000</pubDate>
		<dc:creator>Fatih Tolga Ata</dc:creator>
				<category><![CDATA[Derleyici Tasarımı]]></category>

		<guid isPermaLink="false">http://www.diyezon.com/?p=216</guid>
		<description><![CDATA[Derleyici tasarımı, uzun zamandır kendimi geliştirmek için uğraştığım bir konu. Bu konu ile ilgili çok kitaplar ve makaleler okudum ve araştırmalar yaptım. Ama ne yazık ki, bu konuda kendimi ciddi manada ilerleyebilmiş olarak hissetmiyorum. Şimdi ise bu araştırma ve notlarımı burada toplamayı düşünüyorum. Böylece kendimi bu konuda geliştirmeyi ve bununla beraber bu notları sizlerle paylaşmayı düşünüyorum.
Bu yazı [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">Derleyici tasarımı, uzun zamandır kendimi geliştirmek için uğraştığım bir konu. Bu konu ile ilgili çok kitaplar ve makaleler okudum ve araştırmalar yaptım. Ama ne yazık ki, bu konuda kendimi ciddi manada ilerleyebilmiş olarak hissetmiyorum. Şimdi ise bu araştırma ve notlarımı burada toplamayı düşünüyorum. Böylece kendimi bu konuda geliştirmeyi ve bununla beraber bu notları sizlerle paylaşmayı düşünüyorum.</p>
<p style="text-align: justify;">Bu yazı serisi, önceden yazdığım makaleler gibi tecrübe ve bilgiye dayalı olmayacak. Okuduğum kitap ve makalelerden ve denemelerimden oluşacak. Bu yüzden bu makale serisinin hedeflediği kitle, sadece bu işin meraklılarına hitap ediyor. Derleyici-yorumlayıcı yazmak isteyenlere faydalı bir <strong>konu özeti</strong> olacağını düşünüyorum. Ayrıca bu işe yeni başlayacak olanlara veya başlamış ama bir sebepten dolayı yarım bırakmış kimselere de bir <strong>fihrist</strong>, bir <strong>yol haritası</strong> olacağını düşünüyorum. Tabi bu notları çıkarmakla ben de kendimi bu konuda geliştirmeyi düşünüyorum.</p>
<p><span id="more-216"></span></p>
<h1>Başlarken..</h1>
<p style="text-align: justify; padding-left: 60px;">İlk derleyici yazma merakım, bir işletim sistemi yazma merakım olduğunda başladı <img src='http://www.diyezon.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  Gerçi işletim sistemi olarak sadece disketten boot edilebilir ve dos benzeri bir şeyler yazabildim ancak. Fakat bu gayretim bana çok şeyler kazandırdı. Öncelikle bilgisayar ve işletim sisteminin çalışma mantığını çok iyi bir şekilde sindirdim. Ayrıca assembler ve diğer programlama dilleri noktasında da çok faydası oldu. Ayrıca derleyicilerin işletim sistemleri için ne kadar <strong>önemli</strong> ve <strong>vazgeçilmez</strong> bir şey olduğunu da gördüm. Bütün o kodları assembler ile yazmak bir yana, herhangi bir hata olduğunda kafayı yememek bir yana. Bu tabiki ilk başlarda derleyici yazma merakından çok, derleyici yazma ihtiyacını doğurdu.</p>
<p style="text-align: justify; padding-left: 60px;">Tabi bunlar çok öncelerde kaldı. Artık eskisi gibi işletim sistemi yazmak gibi uçuk bir düşüncenin altına girmeyi düşünmüyorum. Fakat derleyici yazmak o kadar da uçuk, ütopik olan bir şey değil. Zira en ünlü programalama dillerine baktığınızda hemen hemen hepsinin ilk başlarda bir veya iki kişi ile yazıldığını görürsünüz. Bu konu ile ilgili araştırma yaptıkça, bu konunun anlaşılmasının çok zor olmadığını fakat teoriden pratiğe, uygulamaya dökmenin zorluğunu gördüm. Son zamanlarda derleyici tasarım teknolojisinin gelişmesiyle beraber uygulamaya dökme işlemi de artık -eskisi gibi- çok zor değil. Zamanı geldiğinde bu teknolojilerden bahsetmeye çalışacağım.</p>
<h2 style="padding-left: 30px;">Bu Makale Serisi Kime Hitap Ediyor</h2>
<p style="text-align: justify; padding-left: 60px;">İlk başlarda da dediğim gibi öncelikle bu işe meraklı olmalısınız. Bu konu sadece derleyici veya yorumlayıcı yazmaktan ibaret değil tabiki. Eğer yabancı bir dilden (mesala İngilizce&#8217;den) Türkçe çeviri yapan veya tam tersi işlemi yapan bir çeviri programı yapmak istiyorsanız, yine bu konu iyi bilmeniz gerekiyor.</p>
<p style="text-align: justify; padding-left: 60px;">Bir derleyici, yorumlayıcı yazabilmek için bir programlama diline iyi hakim olmanız da gerekmektedir. Bu yüzden bu makaleyi okuyabilmeniz için en az bir adet programlama dilini iyi bilmeniz gerekiyor. Üstelik nesne tabanlı programlamayı da bilmeniz işinizi daha da kolaylaştıracaktır.</p>
<p style="text-align: justify; padding-left: 60px;">Ayrıca şunu da belirtmek isterim. Bu konuda Türkçe kaynak yok denecek kadar az. Bu konuda kendinizi geliştirmek istiyorsanız ilk başta İngilizce ile başlasanız fena etmezsiniz.</p>
<h2 style="padding-left: 30px;">Kitaplar ve Kaynaklar</h2>
<p style="text-align: justify; padding-left: 60px;">Piyasada bu konu ilgili bir çok kitap mevcut. Kimi çok eski ve şu anki teknolojiye hitap etmiyor, kimisi de çok teoride kalıp pratikte hiç bir kullanımından bahsetmiyor. Onca kitap arasından kendimize referans kabul edebileceğimiz kitabı seçmek zor olabiliyor. Ben bunlardan beğendiğim ve gerçekten kaliteli kitapları tavsiye edeceğim. Bu kitaplar genel yorumlarda da iyi gözle bakılan ve günümüz derleyici teknolojilerine de uygun kitaplardır.</p>
<p style="padding-left: 60px;">Benim tavsiye edebileceğim kitaplar şunlar:</p>
<p style="text-align: justify; padding-left: 60px;"><strong>Compilers, Principles, Techniques and Tools; 2nd Edition:</strong> Bu kitap, derleyici tasarımının temel ve vazgeçilmez kitabı olarak kabul edilir. Derleyici tasarımcıları arasında ilk baskısının kapağındaki ejder figüründen dolayı &#8220;Dragon Book&#8221; ismiyle anılmaktadır. Kitabın ilk baskısı yanılmıyorsam 1986 yılında yayınlanmış. Fakat benim tavsiye edeceğim 2. baskısıdır. 2. baskısı 2006&#8242;da çıktı ve değişen bir çok derleyici teknolojisini barındırmaktadır. İyi bir başlangıç yapmak isteyen ve yanı başında referans kitap olarak bulundurmak isteyenlere tavsiye ediyorum.</p>
<p style="text-align: justify; padding-left: 60px;"><strong>Engineering a Compiler (<em>Cooper, Torczon</em>):</strong> Bu kitabı çok aramama rağman ancak ebay üzerinde bulabildim. Sıfır almak isteyenler için amazonda da mevcut. Ebook olarak bulmayı niyetlenenlere söyleyeceğim şey şu ki, bu kitabın internet üzerindeki ebookları eksik. Daha doğrusu draft, yani yazarın kitap çıkmadan çok önceki taslak metinlerinden oluşuyor. Şu ana kadar incelediğim kitaplar arasında işe yarar, teori ve pratik bilgisi iyi olan ve yeni derleyici teknolojilerine hitap eden nadir kitaplardan birisi.</p>
<p style="text-align: justify; padding-left: 60px;"><strong>The Definitive ANTLR Reference:</strong> İleride bahsedeceğim derleyici derleyicilerinden birisi olan ANTLR ile ilgili yazılmış güzel bir kitap. ANTLR derleyici derleyicisini tasarlayan ve yazan Terence Parr tarafından yazılmış olan kitapta, sadece ANTLR ile ilgili bilgi bulunmamakta. Bu kitapta derleyici tasarımının temel bilgileri yanında gramer oluşturma, lexer, parser çalışma mantıkları ve LL çakışmaları gibi bir çok konuda bilgi de bulunmakta. Önceki iki kitap, her ne kadar pratikte kullanılabilecek bilgiler içerse de genelde teoriyi hedefleyen kitaplardır. Fakat bu kitap, öğrendiğimiz teoriyi uygulumaya nasıl dökebilceğimizi ayrıntısı ile anlatmakta.</p>
<p style="text-align: justify; padding-left: 60px;">Derleyici-yorumlayıcı yazmak isteyen herkese bu üç kitabı şiddetle tavsie ediyorum. Bunlar dışında yardımcı olabilecek şu kitaplara da göz atabilirsiniz. </p>
<div style="padding-left: 60px;">
<ul>
<li>Advanced Programming Language Design (Raphael A. Finkel)</li>
<li>Build Your Own Net Language And Compiler (Edward G. Nigles)</li>
<li>Let&#8217;s Build a Compiler (Jack W. Crenshaw) (şu linkte bedava dağıtılmaktadır: http://compilers.iecc.com/crenshaw/)</li>
<li>Building An Optimizing Compiler (Bob Morgan)</li>
<li>Compiler and Compiler Generators (P.D. Terry)</li>
<li>Algorithms for Compiler Design (O.G. Kakde)</li>
<li>Modern Compiler Implementation in Java (Andrew W. Appel)</li>
<li>Compiler Construction (Kneeth C. Louden)</li>
</ul>
</div>
<p style="text-align: justify; padding-left: 60px;">Bu kitaplarla beraber bir sorununuz olduğunda, internet üzerinde ANTLR ve Coco/R mail listeleri sizlere yardımcı olacaktır.</p>
<p style="text-align: justify; padding-left: 60px;">Ayrıca sourcefoge.net gibi bir çok sitede bulabileceğiniz açık kaynak kodlu derleyici projeleri bulunmaktadır. Özellikle Tiny C benzeri bir çok açık kaynak kodlu ve küçük (yani incelenebilir) derleyicilere ulaşabilirsiniz. Gcc, freepascal gibi büyük projeleri de inceleyebilirsiniz. Fakat bu projeler çok büyük olduklarından pek öğretme/öğrenme amaçlı değiller. Bunun yerine daha çok küçük ve deneysel projeleri bulup incelemekte yarar var.</p>
<p style="text-align: justify; padding-left: 60px;">Ben yeri geldikçe bu tarz projeleri bulup sizlerle paylaşacağım. Ayrıca yeni çıkmış olan veya benim yeni keşfettiğim güzel kitapları da burada yeri geldikçe tanıtmaya çalışacağım.</p>
<p style="text-align: justify; padding-left: 60px;">Bir sonraki bölümde derleyiciler hakkında temel teorik bilgiler bulacaksınız. Bir sonraki bölümde anlamadığınız yerler olursa burada verdiğim kitap ve kaynaklardan istifade edererek araştırırsınız. Çünkü bunlar temel konulardır ve sonrasında gelecek olan bölümler bu temel bilgiler üzerine inşa edilecektir.</p>
<h1>Bölüm 1: Derleyiciler ve Çalışma Mantığı</h1>
<p style="text-align: justify; padding-left: 60px;">Derleyici tasarımı, bilgisayar biliminde önemli bir sahayı oluşturmaktadır. En basit olarak bir derleyici, büyük bir bilgisayar programıdır. Bir derleyici, kaynak kodu alır ve belli bir bilgisayar mimarisine uygun çalıştırılabilir bir hedefe çevirir. Bu çevirimin bir parçası da girilen kaynak kodun imla analizini yani syntax analizini yapmaktır. Bu imla analizi sonucunda, kaynak kodlarda hata tespiti yapılır ve kaynak kodun derleyici içindeki karşılıklarına çevrilir. Derleyeci, daha sonra, bu çevrilmiş olan kaynaklara bir mânâ, anlam yükler. Bu anlam yüklemesi sonucunda, derleyici anlam hatalarını araştırır. Bu işlemden sonra da kaynak kodun derleyicide ifade ettiği anlama göre hedeflenen çıktı üretilir. Tabi bu açıklama çok yüzeysel bir açıklama ve adeta bu bölümde göreceklerimizin bir özeti hükmünde oldu.</p>
<p style="text-align: justify; padding-left: 60px;">Derleyici tasarımı, bir mühendislik çalışmasıdır ayrıca. Derleyici yazarı, bu yolda kendisine bol alternatifi olan, masrafı, avantajları ve zorluğu belirgin olan bir tasarım ortamı oluşturmak zorundadır. Her bir karar, oluşturulacak olan derleyiciyi etkileyecektir. Oluşturulacak olan ürünün kalitesi bu alınan kararlara bağlıdır.</p>
<p style="text-align: justify; padding-left: 60px;">Derleyici tasarımı, çok eski olmamasına rağmen, tatmin edici seviyelere kadar gelişmiş bir konudur. Bu konuda bir çok başarılara imza atılmıştır. Piyasada olan bir çok derleyici ve binlerce çeşit programalama dili ile beraber, bu konudaki bilgiler kullanılarak geliştirilmiş bir çok program, bunun ispatıdır. Bir problemi çözmek için bir çok yöntem geliştirilmiştir. Ben burada en güncel ve popüler tekniklerden bahsetmeye çalışacağım. Ama bilinki, bahsedeceğimiz her bir konu ve teknik için alternatifler mevcuttur. Bu alternatifleri araştırmak size kalmış.</p>
<h2 style="padding-left: 30px;">Derleme İşlemine Genel Bakış</h2>
<p style="text-align: justify; padding-left: 60px;">Bir program çalıştırılabilir olmadan önce, bir çok çevirim işlemlerine tabi tutularak hedef çalıştırılabilir kodlar oluşturulurlar. Bu çevirim işlemini yapan programın özel adına <strong>derleyici</strong> (<em>compiler</em>) diyoruz. Derleyici, kendisinin anlayacağı bir dilde kaynak kodlarını alır ve çıkışta çalıştırılabilir kodlar üretir. Bu çalıştırılabilir kodlar Windows&#8217;da exe, dll, Linux&#8217;de elf, so gibi dosyalar olabilceği gibi, .net, java gibi sanal makinalar için çalıştırılabilir kodlar da üretebilir.</p>
<p style="text-align: justify; padding-left: 60px;">Ayrıca çalıştırılabilir kodlar, direk olarak işleme de tabi tutulabilirler. Yani javascript, vbscript gibi direk olarak derleme anında çalışan derleyiciler de mevcuttur. Bu tip derleyicilerin özel adına ise <strong>yorumlayıcı</strong> (<em>interpreter</em>) denilir. Her ne kadar bazı eski kaynaklarda yorumlayıcılar ve derleyiciler iki farklı şeylermiş gibi gösterilse de çalışma mantıkları aynı olduğu için hepsine genel olarak derleyici demeyi uygun buluyorum. Çünkü derleme işleminde, ileride ayrıntısı ile göreceğimiz gibi, ilk safha yorumlama, ikinci safha ise kod üretmedir. Yorumlayıcıların tek farkı, kod üretme safhası bulunmamasıdır.</p>
<p style="text-align: justify; padding-left: 60px;">Ayrıca yorumlayıcı kodları, çalışabilmek için bir yorumlayıcıya ihtiyaç duyarlar. Mesela php kodları php derleyicisine, basic kodları basic derleyicisine, javascript kodları ecmascript destekli bir browser&#8217;a ihtiyaç duyarlar. Bu kodlar, yorumlayıcı olmadan çalıştırılamazlar. Fakat C++, Delphi gibi derleyicilerin ürettiği kodları, direk olarak işletim sistemi üzerinden işlemci çalıştırmaktadır, daha sonradan derleyiciye ihtiyaçları yoktur.</p>
<p style="text-align: justify; padding-left: 60px;">Bir de günümüz teknolojisinde yorumlayıcı olan ama temelde derleyiciler gibi kod üretebilen derleyiciler de mevcuttur. Mesela php derleyicisi, yorumlayıcı gibi ayrı bir dosyaya işletim sistemine uygun bir kod üretmeden, hemen çalışmaya başlar. Fakat yorumlayıcılardan farklı olarak &#8220;kod üretir&#8221;. Bu kod tabiki ayır bir dosya değil, IIS, Apache gibi server&#8217;lara uygun yapıda opcode&#8217;lardır.</p>
<p style="text-align: justify; padding-left: 60px;">Günümüzde derleme işlemi yapan bir programı, yorumlayıcı, derleyici olarak kesin bir şekilde ayırmak zorlaşmıştır. Bunun yerine hepsine derleyici demek ve derleme işlemindeki ilk bölüme yorumlayıcı demek daha doğru olacaktır. Ve bu mantığın kavram karmaşasını da önleyeceğini düşünüyorum. Ve herhangi bir yorumlayıcıya derleyici demeyi de yanlış bulmuyorum. (Php yorumlayıcısı yerine, php derleyicisi demek bana daha uygun geliyor&#8230;)</p>
<p style="text-align: justify; padding-left: 60px;">Peki, kaynak kodumuz, derleyici ile tanıştıktan sonra hangi aşamalardan ve safhalardan geçiyor? Bu konuyu ayrıntısı ile ilerleyen bölümlerde göreceğiz. Burada özet mahiyetinde şöyle bahsedebiliriz:</p>
<div id="attachment_250" class="wp-caption aligncenter" style="width: 519px"><img class="size-full wp-image-250" title="Derleyici İşleyiş Diagramı" src="http://www.diyezon.com/wp-content/uploads/post216_fig1.png" alt="Derleyici İşleyiş Diagramı" width="509" height="302" /><p class="wp-caption-text">Derleyici İşleyiş Diagramı</p></div>
<p style="text-align: justify; padding-left: 60px;">Genel manada bu şekilde olsa da hem arka planda, hemde görüntüde bir çok safha değişebilmektedir.</p>
<p style="text-align: justify; padding-left: 60px;">Kaynak kod derleyicimize geldiği vakit, ilk olarak, kaynak kodun harfleri teker teker işlenir ve tanımlanarak <strong>token</strong> dediğimiz nesnelere dönüştürülürler. Bu işlemi gerçekleştiren parçaya <strong>Lexer</strong> denilir. Lexer&#8217;ın görevi, imla (syntax) analizi yapıp sonuç olarak bir <strong>token</strong> listesi oluşturmaktır. Bu imla analizi sırasında kaynak kodda hatalar da bulunabilir. Bu hatalar Lexer tarafından tespit edilir ve <strong>imla hatası</strong> (<em>syntax error</em>) olarak bildirilir. Lexer, kaynak kodun ifade ettiği, edeceği anlam ile uğraşmaz. Hangi token&#8217;nın hangisinden sonra geldiği ile ilgilenmez.</p>
<p style="text-align: justify; padding-left: 60px;">Mesela aşağıdaki karakter katarına bakalım.</p>
<p style="text-align: justify; padding-left: 60px;"><img class="aligncenter size-full wp-image-249" title="post216_fig2" src="http://www.diyezon.com/wp-content/uploads/post216_fig2.png" alt="post216_fig2" width="230" height="53" /></p>
<p style="text-align: justify; padding-left: 60px;">Lexer, bu karakter katarındaki her bir karakteri ayrıştırır. &#8220;width&#8221; kelimesini bir token, &#8220;:=&#8221; atama operatörünü bir token, 2 sayısını bir token ve &#8220;;&#8221; işaretini bir başka token olarak belirler. Eğer gramere göre bu karakter katarında hata mevcut değilse token listesini üretir. Burada ayrıca her bir boşluk da, kimi lexer&#8217;lar tarafından, birer token olarak kabul edilmektedir. İleride ayrıntısını göreceğimiz token&#8217;ı Delphi&#8217;deki bir record veya C++&#8217;daki bir struct yapısına benzetebilirsiniz. Bu record ya da struct içinde, token&#8217;nın türü, bulunduğu pozisyon gibi değerler bulunur.</p>
<p style="text-align: justify; padding-left: 60px;">Lexer, token listesini oluşturduktan sonra işlemini tamamlamıştır. Artık bu token listesini, <strong>Parser</strong> dediğimiz başka bir derleyici parçasına iletir.</p>
<p style="text-align: justify; padding-left: 60px;">Parser, bu token listesini alır ve bir ağaç yapısı üretir. Bu oluşturulan ağaca, <strong>Parse Ağacı</strong> denilir. Token listesinden oluşturulmuş olan parse ağacı, yine tokenlardan oluşmuş bir yapıdır ve derleyicinin tanıdığı gramere göre oluşturulur. Parser eğer ağacı oluştururken gramere uygun olmayan bir token ile karşılaşırsa bunu hata olarak bildirir. Parserın ürettiği bu hataya &#8220;<strong>Gramer Hatası</strong>&#8221; yada &#8220;<strong>Parse Hatası</strong>&#8221; (<em>Parse Error</em>) denilir.</p>
<p style="text-align: justify; padding-left: 60px;">Parser, token içinde bulunan ve tokenın ifade ettiği text ile uğraşmaz. Tokenın karakter katarında hangi pozisyonda bulunduğu ile ilgilenmez. Ayrıca parser, lexer gibi, tokenın ifade ettiği anlam ile uğraşmaz. Parser sadece token listesinin dizilişine göre gramere uygunluğu kontrol eder. Bunun sonucunda da bu token listesinden oluşan bir parse ağacı üretir.</p>
<p style="text-align: justify; padding-left: 60px;">Misal olarak aşağıdaki karakter katarından, Lexer ve Parser&#8217;ın ürettiği çıktılara bakalım:</p>
<p style="text-align: justify; padding-left: 60px;"> </p>
<div id="attachment_252" class="wp-caption aligncenter" style="width: 317px"><img class="size-full wp-image-252" title="Lexer ve Parser İşleyişi" src="http://www.diyezon.com/wp-content/uploads/post216_fig3.png" alt="Lexer ve Parser İşleyişi" width="307" height="459" /><p class="wp-caption-text">Lexer ve Parser İşleyişi</p></div>
<p style="text-align: justify; padding-left: 60px;">Bu ağacın oluşturulmasının amacına daha sonra değineceğiz.</p>
<p style="text-align: justify; padding-left: 60px;">Parse ağacı oluştuktan sonra bu ağaç, <strong>Semantik Analiz&#8217;</strong>e (<em>Semantic Analysis</em>) tabi tutulur. Ağacın her bir yaprağında bulunan tokenlar, bu aşamada derleyici için bir mânâ ifade etmeye başlarlar. Yani artık integer olan bir token, derleyici tarafından integer, değişken olan token ise değişken olarak anlam verilir. Bunun sonucunda ise, anlam olarak belli hatalar oluşabilir. Bu aşamada oluşabilecek hatalara <strong>Semantik Hatalar</strong> (<em>Semantic Errors</em>) denilir. Mesela integer olan bir değişken, string olan bir değişkene direk olarak eşitlenemez! Tabi bu örnek programlama dillerine göre farklılık da gösterebilir. Fakat genel manada integer bir değeri string bir değere direk olarak eşitleyemezsiniz. Çünkü bunların token tipleri farklıdır. Bu tarzdaki, yani anlam bakımından yanlış olan işlemler semantik hataları oluştururlar.</p>
<p style="text-align: justify; padding-left: 60px;">Semantik analiz sonucunda herhangi bir hata oluşmamış ise parse ağacı yardımı ile <strong>intermediate code</strong> ya da <strong>intermediate representation</strong> dediğimiz ara bir kod oluşturulur. Tabi bu safha her derleyicide bulunmayabilir. Bu ara kod oluşturulduktan sonra kodlar optimize edilir ve tekrar son çıktı için kod üretilir. Bazı derleyiciler ara kod oluşturmadan direk olarak kod oluşturabilmektedir.</p>
<p style="text-align: justify; padding-left: 60px;">Bu işlemlerden bizi en çok zorlayan kısım lexer ve parser kısımlarıdır. Çünkü bu parçalar, programlama dilinin gramerini ifade eden, tanımlanmasında hata kabul etmeyen, hamaliyesi çok olan parçalardır. Bu zorluklardan dolayı bir çok &#8220;derleyici derleyicisi&#8221; denilen yardımcı programlar geliştirilmiştir. Her büyük derleyicide bu derleyici derleyicisi kullanılmaktadır. Bunu daha sonra ayrıntısı ile göreceğiz.</p>
<p style="text-align: justify; padding-left: 60px;">Burada bahsettiğimiz tabirler yabancı gelse de, ileride bunları daha iyi ve ayrıntısı ile bahsedeceğiz. Ama genel olarak &#8220;Derleyici Tasarım Notları&#8221; başlıklı bu yazı serimiz bunlardan bahsedecektir. Burada bir nevi, anlatacaklarımızı özetlemiş olduk. Fakat ayrıntıda gizli, çok farklı teknikler ve yöntemler mevcut. Bunlara yeri geldikçe bahsedeceğiz.</p>
<p style="text-align: justify; padding-left: 60px;"> </p>
<p style="text-align: justify; padding-left: 60px;">________________________________</p>
<p style="text-align: justify;">Birinci bölüm henüz bitmiş değil. Devamını bir başka zamana atıyorum. </p>
<p style="text-align: justify;">Kolay gelsin.</p>
<p style="text-align: center;"><strong>Fatih Tolga Ata © 2009</strong></p>
]]></content:encoded>
			<wfw:commentRss>http://www.diyezon.com/2009/02/22/derleyici-tasarim-notlari-1/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>C / C++ &#8216;dan Delphi&#8217;ye Kod Çevirme Klavuzu (1)</title>
		<link>http://www.diyezon.com/2009/02/19/c-den-delphiye-kod-cevirme-klavuzu-1/</link>
		<comments>http://www.diyezon.com/2009/02/19/c-den-delphiye-kod-cevirme-klavuzu-1/#comments</comments>
		<pubDate>Thu, 19 Feb 2009 02:30:30 +0000</pubDate>
		<dc:creator>Fatih Tolga Ata</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[header]]></category>
		<category><![CDATA[kod çevirme]]></category>

		<guid isPermaLink="false">http://www.diyezon.com/?p=57</guid>
		<description><![CDATA[Özellikle işe yarar bir kütüphane bulduğunuz zaman eğer yoksa hemen Delphi sürümlerini araştırırız. Çoğu zaman &#8220;Delphi port&#8221; ismiyle, kütüphanelerin Delphi versiyonlarına erişebiliriz. Eğer Delphi versiyonlarını bulamazsak, iş başa düşmüş demektir.  Kütüphaneler bir çok dilde olabilirler. Fakat genel manada en çok C++ veya C kodlarının adaptasyonu ve çevrilmesi programcıları zorlayabilmektedir.
Bu yazımızda C / C++ ve Delphi [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">Özellikle işe yarar bir kütüphane bulduğunuz zaman eğer yoksa hemen Delphi sürümlerini araştırırız. Çoğu zaman &#8220;Delphi port&#8221; ismiyle, kütüphanelerin Delphi versiyonlarına erişebiliriz. Eğer Delphi versiyonlarını bulamazsak, iş başa düşmüş demektir.  Kütüphaneler bir çok dilde olabilirler. Fakat genel manada en çok C++ veya C kodlarının adaptasyonu ve çevrilmesi programcıları zorlayabilmektedir.</p>
<p style="text-align: justify;">Bu yazımızda C / C++ ve Delphi gramer bilgisi vermek yerine, daha çok C / C++ kodlarını en doğru şekilde Delphi&#8217;ye nasıl adapte edebileceğimizi göreceğiz. Yapacağımız işlem sadece bire bir kod çevirisi olmayacak. Yeri geldiğinde hiç bir kod çevirmeden sadece orjinal kütüphaneyi Delphi programımızın içine gömebileceğiz.</p>
<p style="text-align: justify;">Eğer siz de, ister derlenmiş olsun, ister açık kaynak kodlu olsun, bir C / C++ kütüphanesini Delphi&#8217;ye nasıl çevirebileceğimizi merak ediyorsanız buyurun beraber yazımıza giriş yapalım.</p>
<p><span id="more-57"></span></p>
<h1>Giriş</h1>
<p style="text-align: justify;">C / C++ &#8216;dan Delphi&#8217;ye kod çevirme derken ilk akla gelen, binlerce satırlık C kodlarını teker teker uğraşıp Delphi olarak yazmak gelecektir. Halbuki bu en son tercihimiz olması gerekmektedir. İlerleyen bölümlerde de göreceğimiz gibi, Delphi derleyicisinin hünerli parmakları bizi bir çok dertten kurtarmaktadır. Yine de bütün kodları baştan çevirmek gibi bir derdin altına girmek isterseniz bu makale size bir başlangıç noktası olacaktır.</p>
<p style="text-align: justify;">Hemen kodlara dalmak gerçekten gerekli mi? Kesinlikle gerekli değil. Çünkü bizim uğraştığımız şey, önceden birileri tarafından yapılıp internet üzerinde dağıtılıyor olabilir. Bu yüzden ilk yapacağımız işlem bir &#8220;Delphi port&#8221; bulmamız gerekmetedir.</p>
<p style="text-align: justify;">Mesela şuanda en hızlı ve en stabil xml kütüphanesi kabul edilen libxml2 kütüphanesini kullanmak istediğimizde, hemen kodları çevirmeye kalkmak yerine bir Delphi port bulmamız en akıllıca işlem olacaktır. Ki bu kütüphanenin başlık dosyaları zaten sourceforge üzerinde dağıtılmaktadır.</p>
<p style="text-align: justify;">Bu makalede, C objelerinin kullanımı, C veri tiplerinin Delphi&#8217;deki karşılıkları, C başlık (header .h) dosyalarının Delphi&#8217;ye çevrilmesi gibi bir çok konu bulacaksınız. Bu bilgiler ile istediğiniz bir C kütüphanesini korkmadan Delphi&#8217;ye adapte edebilirsiniz.</p>
<p style="text-align: justify;">Bu makalede çok fazla C / C++ bilgisi gerekli mi? Gerekli olsa bu makaleye ihtiyaç duymazdınız. Fakat çok  az da olsa C / C++ gramerine aşinalığınız olmak zorunda. En basit olarak bir bir fonksiyon nasıl tanımlanır, bir değişken nasıl tanımlanır, bir pointer nasıl atanır, bunları bilmeniz gerekmektedir.</p>
<h1>Fonksiyon Çağırım Mekanizmaları</h1>
<p style="text-align: justify;">Bir çinli ile anlaşabilmek için bir tercüman tutmanız gerekti. Fakat çok aramanıza rağmen çinli bir tercüman bulamadınız. Bunun yerine bir birine benziyor diye kalkıp, japonca bir tercüman tuttunuz. Çinli olan birisi ile ne kadar anlaşabilirsiniz. Her ne kadar bize &#8220;benzer&#8221; gibi gözükse de iki dil bir birinden farklı dildir. Bu örneği şimdi göreceğimiz konuya hazırlamak için verdim. Bu misalden hakikate şu şekilde geçebiliriz. Derlenmiş olan her bir kütüphanedeki fonksiyonlar belli bir dili konuşmaktadırlar. Bu konuştukları diller görünüşte bir birlerine benzeseler de, ayrıntıda bir birlerinden farklıdırlar. Bu konuşulan dillere fonksiyon mekanizmaları ya da ingilizce terim olarak &#8220;calling conventions&#8221; denilmektedir. Eğer kullanacağınız fonksiyon farklı bir dili kullanıyor ve siz de fonksiyonu farklı bir dil ile çağırıyorsanız muhtemel hatalar ile karşılaşacaksınız. Bu yüzden ilk öğreneceğimiz konu bu olmalıdır.</p>
<p style="text-align: justify;">Direk olarak çağırım mekanizmalarının delphi karşılıklarını vermek yerine az da olsa bir kaç özelliklerinden bahsetmek istiyorum. Çünkü hangi çağırımı nerede ne için kullanacağımızı bilmemiz şarttır.</p>
<p style="text-align: justify;">Şu an literatürde en çok kullanılan 4 çeşit fonksiyon çağırım mekanizması vardır. Bunların ne olduğunu ayrıntısı ile anlatan <a href="http://www.diyezon.com/index.php/2007/06/23/fonksiyon-cagirim-mekanizmalari/" target="_blank">buradaki</a> makalemize göz atabilirsiniz. Hatta okumadı iseniz bir göz gezdirmenizi şiddetle tavsiye ediyorum. Biz burada bu mekanizmaların çalışma şekillerinden çok işimize yarayan kısmını anlatmaya çalışacağız. Ama linkini verdiğim makalede bunların çalışma şekillerini ayrıntılı bir şekilde bulabilirsiniz.</p>
<p style="text-align: justify;">Birinci olarak göreceğimiz çağırım mekanizması <strong>register </strong>çağırımıdır. Delphi varsayalın olarak bütün fonksiyon ve metodlarda bu çağırımı kullanmaktadır. Normalde Dehlpi&#8217;de &#8220;register&#8221; ayrılmış kelimesini fonksiyon ya da metodun sonuna eklemekle, fonksiyon ya da metodu bu çağırım için ayarlamış oluruz. Fakat varsayılan olarak kullanıldığı için bunu yazmanıza gerek yoktur:</p>
<pre class="brush: delphi">procedure RegisterCagirimliProcedure; register;</pre>
<p style="text-align: justify;">Fonksiyon çağırım mekanizmaları ile ilgili yazıyı okuduysanız en verimli ve performanslı çağırımın bu olduğunu biliyorsunuzdur. Bu mekanizma ilk olarak Borland tarafından geliştirilip C++ Builder ve Delphi derleyicilerinde kullanıldı. Şu anda bildiğim bütün C / C++ derleyicileri bu çağırımı desteklemektedir. C / C++ derleyicilerinde bu çağırım<strong> __fastcall</strong> olarak isimlendirilmektedir.</p>
<p style="text-align: justify;">Her ne kadar en hızlı çağırım __fastcall yani register olsa da ve çoğu derleyici tarafından desteklense de, belki de alışkanlıklardan dolayı, C / C++&#8217;da pek kullanılmamaktadır. Genelde Linux ve Windows&#8217;da ileride gelecek olan başka çağırımlar kullanılmaktadır.</p>
<p style="text-align: justify;">__fastcall yani register çağırımları, Microsoft&#8217;uın derleyicilerinde <strong>__msfastcall</strong> olarak başka bir biçimi de almıştır. Fakat şu anda Delphi bu çağırımı desteklememektedir. Umarım ilerideki Delphi derleyicilerinde bunu görebiliriz. Fakat bu çağırım __fastcall kadar bile yaygın ve fazla kullanılmadığı için şu anda anlattığımız çevirim işlemi için çok da önemli değil. Hatta, eğer bir kütüphane bir çok dili desteklemek gibi bir gayesi varsa __fastcall çağırımını da desteklememektedir.</p>
<p style="text-align: justify;">Kısacası bu çağırımı delphi&#8217;ye aktarmak için hiç bir şey yapmamız gerekmiyor. Çünkü delphi bunu varsayılan olarak kullanıyor. Mesela aşağıdaki C fonksiyonunu delphi&#8217;ye çevirelim:</p>
<pre class="brush: c++">__fastcall int birFonksiyon(int i);</pre>
<pre class="brush: delphi">function BirFonksiyon(i: Integer): Integer;</pre>
<p style="text-align: justify;">İkinci çağırım mekanizmamız <strong>__pascal</strong> yada delphi&#8217;de <strong>pascal</strong> çağırımıdır. Ki bu çağırım Win 3.1 zamanından kalma eski bir çağırımdır. Bu yüzden eğer karşılaşırsanız yapmanız gereken __pascal yerine pascal yazmalısınız.</p>
<p style="text-align: justify;">Üçüncü çağırım mekanizmamız <strong>__stdcall</strong> ya da delphi&#8217;de <strong>stdcall</strong> çağırımıdır. Bu çağırım, Windows işletim sisteminin varsayılan çağırım mekanizmasıdır. Eğer windows&#8217;a ait bir DLL&#8217;i kullanacaksak, bilinki %99 stdcall olarak tanımlanmıştır. __stdacall çağırımı derleyicilerde çeşitli takma adlar ile kullanılmaktadır. Genelde &#8220;WINAPI&#8221; kelimesi __stdcall için kullanılmaktadır. Mesela aşağıdaki Windows API&#8217;sinden bulunan fonksiyonu şöyle çevireceğiz:</p>
<pre class="brush: c++">WINAPI BOOL ReleaseCapture(VOID);</pre>
<pre class="brush: delphi">function ReleaseCapture: Boolean; stdcall;</pre>
<p style="text-align: justify;">Yardım dosyalarında bu fonksiyonun başında WINAPI ya da __stdcall ifadesini görmesek de, biliyoruz ki bu bir Windows fonksiyonudur. Öyleyse bu fonksiyon stdcall çağırımıdır. Bu çıkarım ile hem delphi hem de C kodunda bu çağırımı belli ettim.</p>
<p style="text-align: justify;">Dördüncü ve son çağırımımız ise <strong>__cdecl</strong> ya da delphi&#8217;de <strong>cdecl</strong> çağırımıdır. Bu çağırım C / C++ derleyicilerinin varsayılan çağırımıdır. Eğer C dosyalarında hernangi bir çağırım belirtilmemiş ise, bu fonksiyon cdecl olarak çağırılacaktır. Yani aşağıdaki fonksiyonlar aynı şeyi ifade edip her iksinin de çevirimi aşağıda gösterilmektedir.</p>
<pre class="brush: c++">void bir_proc();
__cdecl void ikinici_proc();</pre>
<pre class="brush: delphi">procedure bir_proc; cdecl;
procedure ikinci_proc; cdecl;</pre>
<p>İleride makroları göreceğiz. Aşağıdaki makro tanımlamaları da geçerlidir ve kullanılmaktadır.</p>
<p>__stdcall = _stdcal = CALLBACK = WINAPI = APIENTRY = PASCAL = APIPRIVATE</p>
<p>__cdecl = _cdecl = WINAPIV</p>
<p>__fastcall = _fastcall</p>
<p>Yukarıda eşitliklik ile ifade edilenleri birbirlerinin aynısı olarak kabul edebilirsiniz.</p>
<h2>DLL Kullanımı ve Çağırımlar</h2>
<p style="text-align: justify;">Normalde bir DLL&#8217;den bir fonksiyonu kullanmak için fonksiyonu nasıl tanımlayacağımızı bir hatırlayalım:</p>
<pre>
<pre class="brush: delphi">
const
  DLLDosyasi = &#039;birkutuphane.dll&#039;;
procedure bir_proc(); cdecl; external DLLDosyasi;
</pre>
</pre>
<p style="text-align: justify;">Kendi DLL&#8217;imizi kullandığımızda fonksiyonu nasıl tanımlayacağımızı biliyoruz. Peki DLL&#8217;i biz yazmamış isek ne yapacağız?</p>
<p style="text-align: justify;">Bu durumda, eğer herkes tarafından kullanılabilir bir DLL dosyası ise, DLL sağlayan firma, ekip, yazılımcı, vs. DLL dosyası ile beraber bir &#8220;header&#8221; dosyası sağlayacaktır.</p>
<p style="text-align: justify;">Delphi&#8217;de, bildiğiniz gibi, unit iki bloktan oluşur. Birinci blok yani <strong>interface </strong>bloğu, unit tarafından &#8220;unit dışında&#8221; kullanılacak olan sınıf, değişken, fonksiyon, vs. &#8216;den oluşur. İkinci blok yani <strong>implementation </strong>bloğu ise, interface bloğunda tanımlanmış olan fonksiyon ve sınıfların esas kodlarından oluşur. Yani esas çalıştırılan kodlar implementation kısmında bulunur. Ayrıca implementation&#8217;da tanımlı herhangi bir değişken, fonksiyon, vs. eğer interface&#8217;de tanımlı değilse diğer unitlerde görünmezler ve kullanılamazlar.</p>
<p style="text-align: justify;">C / C++ dosyaları da Delphi&#8217;nin unit dosya mantığının aynısıdır. Fakat Delphi&#8217;nin aksine C / C++ dosyaları tek parça halinde değil iki parça halinde tutulurlar. Delphi&#8217;de interface olarak ifade ettiğimiz blok C &#8216;de &#8220;header&#8221; olarak ifade edilir ve &#8220;.h&#8221; uzantısına sahiptir. Nadir de olsa C++&#8217;da header dosyaları &#8220;.hpp&#8221; uzantısı alabilirler. Delphi&#8217;de implementation olarak ifade edilen esas çalışan kodlar ise C&#8217;de &#8220;.c&#8221; ve C++&#8217;da ise &#8220;.cpp&#8221; uzantısı ile ifade edilen dosyalardadır.</p>
<p style="text-align: justify;">Header dosyaları, şart olmasa da, genelde kütüphanenin &#8220;include&#8221; adlı klasöründe barındırılmaktadır. Kullanacağınız DLL&#8217;in header dosyası da bu veya bunun benzeri bir klasörde bulunacaktır. Header dosyası olmadan Delphi çevirimi yapmamız çok zor olacaktır. Hatta üst düzey assembler bilginiz olması gerekebilir. Bu yüzden DLL ile beraber en az bir adet header dosyasına sahip olduğunuzu varsayıyorum.</p>
<p style="text-align: justify;">Bu başlık altında esas olarak anlatmak istediğim mevzuya şimdi geliyoruz.</p>
<p style="text-align: justify;">DLL&#8217;e ait header dosyasına baktığımızda fonksiyonların hangi çağırım mekanizmalarını kullandıklarını görebiliyor ve anlayabiliyoruz. Fakat derlenmiş olan DLL dosyalarında bu çağırım mekanizmalarını belirten hernhangi bir işaret var mı?</p>
<p style="text-align: justify;">Bunun için yardımcı bir araca ihtiyaç duyacağız. Aslında TDump isimli ve delphi ile beraber gelen bir konsol uygulaması bulunmakta. Ancak konsol uygulaması yerine göresel bir aracı tercih ederim. Ama illaki tdump kullanmak isteyen olursa &#8220;-b&#8221; parametresi ile beraber kullanabilirsiniz. Her neyse, ihtiyacımız olan program &#8220;<a href="http://www.dependencywalker.com/" target="_blank">Dependency Walker</a>&#8221; . Bu program ile DLL&#8217;de bulunan tüm fonksiyonları isimleri ile beraber görebiliriz.</p>
<p style="text-align: justify;">Peki header dosyasında fonksiyonlar ayrıntılı bir şekilde olmasına rağmen neden ekstra bir araca ihtiyaç duyuyoruz. Aslında ekstra bir programa ihtiyacımız yok. Header dosyaları fazlasıyla yeterlidir. Fakat bazen DLL&#8217;ler varsayılan ayarlarla derlenmemektedir. Bu da bize sorun çıkarabilmektedir.</p>
<p style="text-align: justify;">DLL&#8217;ler derlenirken fonksiyon isimleri çeşitli ekler alırlar. Bu ekler, fonksiyonun hangi çağırımı kullandığını belirler. Ama bazen bu varsayılan isimlendirmenin dışına çıkan kütüphane yazılımcıları, bütün ekleri kaldırabiliyorlar. Mesela cdecl fonksiyonları önlerinde &#8220;_&#8221;  işaretini barındırırlar. Veya stdcall fonksiyonları fonksiyon isiminden sonra &#8220;@&#8221; işareti ve parametrelerin boyutlarını içeren bir dizi ekten ibarettirler. Dependency Walker gibi programlar, bize fonksiyonların hangi isimlerden oluştuğunu gösterecektir. Eğer header dosyasını Delphi&#8217;ye hatasız aktardık fakat halen fonksiyonun bulunmadığına dair hata alıyorsak demek ki isimlendirmeler yanlış demektir. Bunun için aşağıdaki yöntemi kullanmalıyız:</p>
<pre class="brush: delphi">procedure BirProc; cdecl; external DLLDosyasi name &#039;_bir_proc&#039;;</pre>
<p style="text-align: justify;">Gördüğünüz gibi &#8220;external&#8221; direktifinin hemen sonuna bir boşluk ile beraber bir <strong>name </strong>direktifi ekledik. Bu &#8220;name&#8221; direktifi ile DLL&#8217;deki fonksiyona direk olarak işaret etmiş oluyoruz. Bu tekniği sadece ihtiyaç dahilinde kullanabilirsiniz. Veya C / C++ ismi size hoş gelmedi ise fonksiyonun ismini Delphi&#8217;de kullanım adını değiştirmek için kullanabilirsiniz.</p>
<p style="text-align: justify;">
<table style="background-color: #e9eaeb; width: 100%;" border="0">
<caption>Not</caption>
<tbody>
<tr>
<td style="text-align: justify;">Bir tecrübemi aktarmak istiyorum. Eğer C / C++&#8217;a fazla hakim değilseniz, muhtemelen header dosyalarını çeviriken bir çok hata yapacaksınız. Bu durumda DLL&#8217;i statik olarak kullandığınızdan dolayı, delphi programı derleyecek ama çalıştıramayacktır. Ve program donacaktır. Çünkü istediği DLL içinde sizin tanımladığınız gibi bir fonksiyon bulamamıştır. Bunun yerine ilk başlarda, header dosyasının çevirimi doğru bir şekilde çevrilene kadar, DLL dosyasını dinamik olarak yükleyin. Bu şekilde çıkan hataları, hangi fonksiyon tanımlamasından kaynaklandığını belirleyebilirsiniz. Dinamik DLL yüklemesinin nasıl yapıldığını internetten araştırarak bulabilirsiniz.</td>
</tr>
</tbody>
</table>
<table style="background-color: #e9eaeb; width: 100%;" border="0">
<caption>Not</caption>
<tbody>
<tr>
<td style="text-align: justify;">Header dosyalarını çevirmek için aslında JEDI kütüphanesine ait bir araç bulunmaktadır. Fakat bu araç da tam olarak header dosyalarını çevirememektedir. Daha doğrusu yine burada anlattığımız bilgilere ihtiyaç duyacaksınız. Bu araç sadece çok büyük dosyalarda yazım kolaylığı sağlamak açısından bir kolaylık getirmektedir. Ayrıca ileride anlatacğımız konular için bu araç yetersiz kalmaktadır. Bu araca Dr. Bob&#8217;un sitesinden ya da JEDI kütüphanesinden erişebilirsiniz. (Header Conversation Tool)</td>
</tr>
</tbody>
</table>
<p style="text-align: justify;">Çağırım mekanizmalarına hakim olmak Delphi çevirimi noktasında size büyük kolaylık sağlayacaktır. Bu yüzden bu konuyu ilk sıraya aldım ve biraz da uzun tuttum.</p>
<h1>Veri Tipleri</h1>
<p style="text-align: justify;">C / C++ kodları, çok fazla pointer ile ilgili işlemleri barındırmaktadır. Basit bir tip tanımlamasında bile pointerlara ihtiyaç duyarsınız. Fakat C / C++&#8217;da bulunan bu pointer kullanımı Delphi&#8217;ye geçtiğinde illaki pointer kalacak diye bir şart yoktur. Kullanımda bu olay farklılık göstermektedir.</p>
<p style="text-align: justify;">İlk önce basit veri tiplerinden başlayalım. Aşağıdaki fonksiyona bir göz atalım:</p>
<pre class="brush: c++">char* birfonksiyon(void);</pre>
<p style="text-align: justify;">Burada &#8220;char*&#8221; olarak çıktı veren bir fonksiyonuz mevcut. C&#8217;de &#8220;*&#8221; işareti pointer tanımlamak ve çarpım işlemlerini yapmak için kullanılır. Eğer char, int gibi bir tip ile birlikte kullanılıyorsa bu, pointer operatörüdür. Bu örnekte &#8220;char*&#8221; ifadesi bir char işaretçisini ifade eder. Bu Delphi&#8217;de PAnsiChar ile ifade edilir. PChar demiyorum çünkü Delphi 2009 ile beraber artık PChar unicode oldu. Fakat buradaki &#8220;char&#8221; ansi olan bir char tipidir. Bunun Delphi karşılığı:</p>
<pre class="brush: delphi">function birfonksiyon: PAnsiChar; cdecl;</pre>
<p style="text-align: justify;">olacaktır. Burada kullanılan C / C++ pointer&#8217;ı aynı şekilde Delphi&#8217;de de pointer olarak geçmiştir. Fakat bazen C&#8217;de pointer&#8217;lar hem pointer tanımlamak için, hem dizi tanımlamak için hem de parametrelerde çıkış için kullanılabiliyor. Bunların ayırımını zamanla ve tecrübeyle kazanacaksınız. Aşağıda referans olarak kullanabileceğiniz bir tablo gözükmektedir. Bu tabloyu kullandıkça artık aşina olup tekrar dönüp bakmayacağınızı da zannediyorum. Ama şimdi her birine birer birer göz atmanızda yarar var.</p>
<table border="1">
<tbody>
<tr>
<th>C / C++ Tipi</th>
<th>Delphi Karşılığı</th>
<th>Delphi 2009 Alternatifi</th>
</tr>
<tr>
<td>char</td>
<td>AnsiChar</td>
<td></td>
</tr>
<tr>
<td>char*</td>
<td>PAnsiChar</td>
<td></td>
</tr>
<tr>
<td>wchar_t</td>
<td>WideChar</td>
<td>Char</td>
</tr>
<tr>
<td>wchar_t*</td>
<td>PWideChar</td>
<td>PChar</td>
</tr>
<tr>
<td>signed char</td>
<td>ShortInt</td>
<td></td>
</tr>
<tr>
<td>unsigned char</td>
<td>Byte</td>
<td></td>
</tr>
<tr>
<td>LPSTR veya PSTR</td>
<td>PAnsiChar</td>
<td></td>
</tr>
<tr>
<td>LPWSTR veya PWSTR</td>
<td>PWideChar</td>
<td>PChar</td>
</tr>
<tr>
<td>void*</td>
<td>Pointer</td>
<td></td>
</tr>
<tr>
<td>bool</td>
<td>Boolean</td>
<td></td>
</tr>
<tr>
<td>float</td>
<td>Single</td>
<td></td>
</tr>
<tr>
<td>double</td>
<td>Double</td>
<td></td>
</tr>
<tr>
<td>long double</td>
<td>Extended</td>
<td></td>
</tr>
<tr>
<td>int</td>
<td>Integer</td>
<td></td>
</tr>
<tr>
<td>signed int</td>
<td>Integer</td>
<td></td>
</tr>
<tr>
<td>unsigned int</td>
<td>Cardinal</td>
<td></td>
</tr>
<tr>
<td>UINT</td>
<td>Cardinal</td>
<td></td>
</tr>
<tr>
<td>unsigned long</td>
<td>Cardinal</td>
<td></td>
</tr>
<tr>
<td>unsigned long int</td>
<td>Cardinal</td>
<td></td>
</tr>
<tr>
<td>unsigned</td>
<td>Cardinal</td>
<td></td>
</tr>
<tr>
<td>long</td>
<td>LongInt</td>
<td></td>
</tr>
<tr>
<td>signed long</td>
<td>LongInt</td>
<td></td>
</tr>
<tr>
<td>long int</td>
<td>LongInt</td>
<td></td>
</tr>
<tr>
<td>DWORD</td>
<td>Cardinal veya DWORD</td>
<td></td>
</tr>
<tr>
<td>QWORD</td>
<td>QWORD</td>
<td></td>
</tr>
<tr>
<td>WORD</td>
<td>Word</td>
<td></td>
</tr>
<tr>
<td>unsigned short</td>
<td>Word</td>
<td></td>
</tr>
<tr>
<td>unsigned short int</td>
<td>Word</td>
<td></td>
</tr>
<tr>
<td>short</td>
<td>SmallInt</td>
<td></td>
</tr>
<tr>
<td>short int</td>
<td>SmallInt</td>
<td></td>
</tr>
<tr>
<td>signed short</td>
<td>SmallInt</td>
<td></td>
</tr>
</tbody>
</table>
<p style="text-align: justify;">Tabloyu incelerseniz çoğu tipin birbirinin aynısı olduğunu anlarsınız. Bu yüzden tablo biraz uzunca oldu. Bunun nedeni C / C++&#8217;da bolca makro denilen yapıların kullanılmasıdır. Makrolar ile ilgili işlemleri ileride anlatacağımızdan şimdilik burada kısa kesiyoruz.</p>
<p style="text-align: justify;">Gördüğünüz gibi string yerine PChar yada PAnsiChar kullanılmakta. Tabloda ayrıca Delphi 2009 ve yukarısında kullanılmak üzere verdiğim alternetifler de mevcut.</p>
<table style="background-color: #e9eaeb; width: 100%;" border="0">
<caption>Bir Tüyo</caption>
<tbody>
<tr>
<td style="text-align: justify;">Tabi burada verilmeyen bir çok tip olabilir. Ama temel manada bunlar bize referans olacaktır. Diğer tipler için bir tüyomuz şöyle olacaktır. Mesela UINT gibi makro bir tipin ifade ettiği tipi,değeri bulabilmek için C++ Builder gibi herhangi bir derleyeci size kafi gelecektir. Tek yapmanız gereken aşağıdaki gibi bir yapıyı, derleyicinin kütüphane dosyalarında arattırıp bulmanızdır.
<pre class="brush: c++">#define UINT unsigned int</pre>
<p style="text-align: justify;">C++ Builder için, sadece Ctrl ile tipin üzerine tıklamınız kafidir. Yukarıdaki ifadede UINT makrosu, &#8220;unsigned int&#8221; olarak tanımlanmıştır. Geriye kalan sadece &#8220;unsigned int&#8221; için tablodan Delphi karşılığını öğrenmektir.</p>
</td>
</tr>
</tbody>
</table>
<p style="text-align: justify;">Veri tipleri ile ilgili zamanı geldikçe yine değineceğiz. Ama giriş için bu kadarlık bilgi yeterli.</p>
<h1>Makrolar</h1>
<p style="text-align: justify;">Diyeceksiniz ki, ne oluyor C dersine mi başladık? Eğer bir dilden diğer dile çevirim yapmak istiyorsanız her iki dilin de incelikleri bilmeniz gerekmektedir. Tabi biz burada bir C kitabı yazacak değiliz. Bunun yerine mümkün olduğunca kısa tutup püf noktalarını vermeye çalışıyorum. Fakat, yeri gelecek bir C kitabını karıştırmanız gerekebilecek veya C ile ilgili bir siteyi&#8230;</p>
<p style="text-align: justify;">Makrolar C / C++ derleyicilerine, derlemeden hemen önce yapılacak olan işlemleri bildirir. Bir nevi Delphi&#8217;deki {$ ..} derleyici direktiflerine benzeselerde bunlar daha karmaşıklardır. Önemli nokta, makroların derlemeden hemen önce çalıştırılmasıdır ve derlemeye dahil olmamasıdır. Bu yüzden C / C++ derleyicileri precompile denilen bir işleme tabi tutulurlar. Bu da C / C++ derleyicilerinin neden bu kadar yavaş derlediklerinin bir sebebidir. Eğer C / C++ ile büyük bir programı derlememiş iseniz hala, Delphi gibi 1-2 sn içinde derlediğini zannedersiniz. Halbuki C/C++&#8217;da ömrünüz yarısı derlemekle geçer <img src='http://www.diyezon.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p style="text-align: justify;">Aşağıda tanımlı basit bir makro görüyorusunuz:</p>
<pre class="brush: c++">#define BIRMAKRO MakronunDegeri</pre>
<p style="text-align: justify;">Makro burada gördüğünüz gibi #define ile tanımlanıyor. Delphide olduğu gibi #ifdef, #ifndef, #endif gibi ifadelerle bu makronun tanımlı olup olmadığı kontrol edilebilir.</p>
<p style="text-align: justify;">Peki bu makro delphi&#8217;ye geçirilecek mi? Belki evet belki hayır. Eğer makro bir fonksiyonu tanımlıyorsa bu durumda makroyu delphide tanımlamalısınız. Aksi taktirde makronun değeri sizin için yeterlidir. Makroyu geçrimenize gerek yok.</p>
<p style="text-align: justify;">Eğer önceki konudaki tüyoyu okudu iseniz makroların gerçek değerlerine nasıl ulaşacağımızı biliyoruz. Mesela aşağıdaki makroyu birebir delphi&#8217;ye geçirmeye luzum yoktur:</p>
<p>C:</p>
<pre>
<pre class="brush: c++">#define size_t unsigned int
__stdcall size_t getSize();</pre>
</pre>
<p>Delphi:</p>
<pre class="brush: delphi">function getSize: Cardinal; stdcall;</pre>
<p style="text-align: justify;">Ama isterseniz size_t makrosunu bir tip olarak tanımlayabilirsiniz de:</p>
<pre class="brush: delphi">size_t = Cardinal;</pre>
<p style="text-align: justify;">Ama lüzum yok. Peki hangi makroları delphi&#8217;ye çevirmemiz gerekli? Şimdiye kadarki tecrübeme göre genelde fonksiyon makrolarını geçrimek yeterli. Aşağıda örnek bir fonksyion makrosu mevcut ve devamında delphi kodlarını bulabilirsiniz:</p>
<pre class="brush: c++">#define Fonksiyon(Msg) MessageBox(NULL, Msg, NULL, NULL)</pre>
<pre>
<pre class="brush: delphi">procedure Fonksiyon(Msg: PChar); inline;
begin
  MessageBox(0, Msg, &#039;&#039;, 0);
end;</pre>
</pre>
<p style="text-align: justify;">Gördüğünüz gibi sadece &#8220;<strong>inline</strong>;&#8221; olarak tanımlamamız aynı etkiyi yapacaktır. Çünkü &#8220;inline&#8221; direktifi de makro gibi fonksiyonun çağrıldığı yerde çalıştırılır. Inline ve makro fonksiyonlar için internette bir çok makale bulabilirsiniz.</p>
<p style="text-align: justify;">Bunun dışında makrolar ile iligli olarak şunu söylemek istiyorum. Önceden de dediğim gibi her makronun bire bir Delphi&#8217;ye çevrilmesine gerek yoktur. Makro fonksiyonların haricinde bir de bazen makro sabitler de tanımlanabilir. Mesela:</p>
<pre>
<pre class="brush: c++">#define ei_NONE 0
#define ei_WRITE 1
#define ei_READ 2
#define ei_CREATE 3</pre>
</pre>
<p style="text-align: justify;">Bu tanımlamaları ister const ile tek tek eşitleyerek yaparsınız isterseniz enumerator tanımlarsınız. Biz ikisini de görelim:</p>
<pre>
<pre class="brush: delphi">//const ile
const
  ei_NONE = 0;
  ei_WRITE = 1;
  ei_READ = 2;
  ei_CREATE = 3;
//enumerator ile
type
  Tei = (ei_NONE = 0, ei_WRITE = 1, ei_READ = 2, ei_CREATE = 3);</pre>
</pre>
<p>Tercih size kalmış&#8230;</p>
<p style="text-align: justify;">Çok karmaşık makrolar geldiğinde internetten veya bir c kitabından yardım alabilirsiniz. Fakat bu kadarlık bilgi sizi fazlasıyla idare edecektir.</p>
<h1>Gelecek Makalede</h1>
<p style="text-align: justify;">Daha yapacağımız çok işler olacak. Şimdilik bu verdiğimiz bilgiler ile kendiniz çevirimler deneyebilirsiniz. Fakat, daha görememiz gereken çok önemli konular mevcut. Özellikle fonksiyon parametreleri başlı başına bir konu başlığı.</p>
<p style="text-align: justify;">Bununla beraber bu makale serisinde sadece DLL kullanımını görmeyeceğiz. Gelecek makalelerde DLL kullanmadan C obje dosyalarını direk olarak delphi&#8217;ye gömeceğiz. Böylece programınızın yanında ek bir DLL verme zorunluluğunuzda kalkacak. Tabi bu konu başlı başına bir makale bölümü olacaktır.</p>
<p style="text-align: justify;">Yine gelecek makalelerde hedear dosyaları haricindeki, implementation olarak ifade ettiğimiz kısımları da sıfırdan Delphi&#8217;ye nasıl çevrileceğinden bahsedeceğiz. Daha doğrusu yine bazı püf noktalara değineceğiz. Yoksa tüm gramerin nasıl çevrileceğini anlatmak biraz uzun ve sıkıcı olabilir.</p>
<p style="text-align: justify;">Bu bölümlük bu kadar.</p>
<p style="text-align: justify;">Fikir, eleştiri ve önerilerinizi bekliyorum.</p>
<p style="text-align: center;"><strong>Fatih Tolga Ata &#8211; 2009</strong></p>
]]></content:encoded>
			<wfw:commentRss>http://www.diyezon.com/2009/02/19/c-den-delphiye-kod-cevirme-klavuzu-1/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Stream&#8217;de Uzmanlaşalım&#8230; (Bölüm 2)</title>
		<link>http://www.diyezon.com/2009/02/17/streamde-uzmanlasalim-bolum-2/</link>
		<comments>http://www.diyezon.com/2009/02/17/streamde-uzmanlasalim-bolum-2/#comments</comments>
		<pubDate>Mon, 16 Feb 2009 22:19:27 +0000</pubDate>
		<dc:creator>Fatih Tolga Ata</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[BinaryReader]]></category>
		<category><![CDATA[BinaryWriter]]></category>
		<category><![CDATA[BufferedStream]]></category>
		<category><![CDATA[MemoryStream]]></category>
		<category><![CDATA[stream]]></category>
		<category><![CDATA[StreamReader]]></category>
		<category><![CDATA[StreamWriter]]></category>
		<category><![CDATA[TBlobStream]]></category>
		<category><![CDATA[TFileStream]]></category>
		<category><![CDATA[TMemoryStream]]></category>
		<category><![CDATA[TResourceStream]]></category>
		<category><![CDATA[TStream]]></category>

		<guid isPermaLink="false">http://www.diyezon.com/?p=99</guid>
		<description><![CDATA[Geçen bölümde stream&#8217;e giriş yaptık. Ve değişik veri tipleri ile stream üzerindeki okuma ve yazma işlemlerini gördük. Bu bölümde stream kullanımının sadece dosya okumak-yazmak olmadığını da göreceğiz. Ayrıca .net ve vcl.net ortamında stream kullanımından da bahsetmeye çalışacağız.

Hafıza Bölgesinde Stream Kullanımı
Takdir edersiniz ki en hızlı stream verisi hafızada bulunan stream verisidir. Yani bir hafıza stream nesnesi [...]]]></description>
			<content:encoded><![CDATA[<p align="justify">Geçen bölümde stream&#8217;e giriş yaptık. Ve değişik veri tipleri ile stream üzerindeki okuma ve yazma işlemlerini gördük. Bu bölümde stream kullanımının sadece dosya okumak-yazmak olmadığını da göreceğiz. Ayrıca .net ve vcl.net ortamında stream kullanımından da bahsetmeye çalışacağız.</p>
<p><span id="more-99"></span></p>
<h1>Hafıza Bölgesinde Stream Kullanımı</h1>
<p align="justify">Takdir edersiniz ki en hızlı stream verisi hafızada bulunan stream verisidir. Yani bir hafıza stream nesnesi her halukarda okuma-yazma işlemleri dosya ve veritabanından hızlı çalışacaktır. Bu durumda eğer bir stream ile çok fazla okuma-yazma işlemleri gerçekleştiriliyorsa, ilk başta bir hafıza streami kullanılmalı ve stream ile işimiz bitip son durumu kaydetmek istediğimizde bu hafıza streaminden başka bir stream nesnesine aktarım yapılması en doğru işlem olacaktır. Aynı şekilde stream verisi üzerinde çok miktarda Seek işlemi yani stream işaretçisinin pozisyonunu değiştirme işlemi gerçekleştiriyorsanız, bu durumda yine stream verisinin tamamını bir hafıza bölgesine aktarmalısınız.</p>
<p>Aşağıdaki örneğe göz atalım:</p>
<pre>
<pre class="brush: delphi">
procedure TForm1.Button1Click(Sender: TObject);
var
  Mem: TMemoryStream;
  writebuffer, readbuffer: AnsiString;
begin
  Mem := TMemoryStream.Create;
  try
    writebuffer := &#039;Diyezon&#039;;
    Mem.WriteBuffer(writebuffer[1], Length(writebuffer));
    Mem.Position := 0;
    SetLength(readbuffer, Length(writebuffer));
    Mem.ReadBuffer(readbuffer[1], Length(readbuffer));
    ShowMessage(readbuffer);
  finally
    Mem.Free;
  end;
end;
</pre>
</pre>
<p align="justify">Gördüğünüz gibi TMemoryStream&#8217;in Create oluşturucusu hiç bir parametre almıyor. Burada ekstradan yazma işleminden sonra Position := 0 ile stream işaretçisini başa getiriyoruz. &Ccedil;ünkü yazma işleminin sonunda işaretçi pozisyonu kayacaktır. Okuma işlemini yapabilmemiz için yine işaretçiyi başa getirip okutuyoruz. Aynı işlemi Seek metodu ile de yapabiliriz. Ek bir not olarak, Seek metodu Position&#8217;dan daha hızlı çalışmaktadır.  </p>
<p align="justify">Hafıza streami kullanmak, oluşturulması esnasında hiç bir parametre gerektirmediğinden, diğerlerine nazaran daha kolay ve pratiktir. Bu yüzden yapacağımız yüksek yoğunluktaki stream işlemlerini TMemoryStream ile yapmamız bizim yararımıza olacaktır. Özellikle Seek işlemlerinde yani pozisyon değiştirme işlemlerinde hafızadan faydalanmak önemli bir performans artışına sebep olacaktır.</p>
<h1>Stream&#8217;lerin Aktarımı ve Kopyalanması</h1>
<p align="justify">Stream verileri ile okuma-yazma işlemlerinden sonra yapılan en önemli işlem de bir stream&#8217;in başka bir stream verisi üzerine aktarılmasıdır. Mesela bir dosyayı hafızaya almak isteyebilirsiniz. Veya hafızadaki bir veriyi veritabanına aktarmak isteyebilirsiniz. Bu durumda her stream fonksiyonunda bulunan CopyFrom metodu kullanılır.  </p>
<p align="justify">Aktarım işleminde basit bir metodu kullanmaktan öte, bir noktaya dikkat edilmesi şarttır. CopyFrom metodu ve bunun gibi bir çok metod, stream işaretçisinin bulunduğu pozisyondan itibaren işlemeye başlarlar. Aşağıdaki örneğe bir göz atalım:</p>
<pre>
<pre class="brush: delphi">
procedure TForm1.Button1Click(Sender: TObject);
var
  Mem: TMemoryStream;
  F: TFileStream;
  //Mem, F: TStream; şeklinden de yazılabilirdi...
  buffer: AnsiString;
begin
  Mem := TMemoryStream.Create;
  F := TFileStream.Create(&#039;c:\deneme.txt&#039;, fmCreate or fmShareDenyNone);
  try
    buffer := &#039;Diyezon&#039;;
    Mem.WriteBuffer(buffer[1], Length(buffer));
    //karakter katarımızı hafızaya aldık.

    Mem.Position := 0; //Önemli!!!!

    F.CopyFrom(Mem, Mem.Size);
  finally
    Mem.Free;
    F.Free;
  end;
end;
</pre>
</pre>
<p align="justify">İlk yaptığımız işlem bir karakter dizisini hafızaya almak oldu. Ardında bu hafıza streamini dosya üzerine kopyaladık. Burada dikkat edilmesi gereken nokta Position := 0 kısmıdır. Hafızaya karakter dizisinin yazılmasından sonra stream işaretçisi sona kayar. Böylece sonrasında gelen CopyFrom gibi metodlar bu pozisyondan itibaren işlem yapmaya başlarlar. İşaretçi sonda olduğu için CopyFrom kopyalayamaz ve bir istisna hatası çıkarır. İşte bunu önlemek için işaretçiyi başa alıyoruz ve hafıza streaminin tamamını dosya üzerine aktarıyoruz.</p>
<p align="justify">Kısacası CopyFrom metodu, kopyalanacak olan streamin işaretçi pozisyonundan itibaren, sizin belirlediğiniz uzunluk kadar stream&#8217;den okur ve diğer streame aktarır. Tek dikkat etmemiz gereken nokta kopyalayacağımız kaynak stream&#8217;in mevcut işaretçi poziyonudur. Eğer işaretçi poziyonu sonda ise CopyFrom kaynak stream&#8217;den veri okuyamayacak ve hata meydana gelecektir. </p>
</p>
<h1>Nesneler ve Bileşenleri Okuyup Yazmak</h1>
<p align="justify">Nesneleri, stream üzerine yazmak denildiğinde ilk akla gelen record verileri gibi okuyup yazmaktır. Halbuki nesneler, her ne kadar benzese de, record verilerinden çok farklıdır ve karışıktır. Bu yüzden direk olarak bir sınıf ya da nesne stream üzerine aktarılamaz. Ancak sizin belirlediğiniz kısımları ayrı ayrı, önceden öğrendiğimiz okuma-yazma işlemleri ile halledebilirsiniz.</p>
<p align="justify">Fakat bileşenlerde durum biraz farklıdır. Daha doğrusu TPersistent sınıfından türeyen bütün sınıflarda durum biraz farklıdır. TStream sınıfında, bileşenlerin özelliklerini stream üzerine alabilmek için ReadComponent ve WriteComponent metodları mevcuttur. Bu metodlar sayesinde bileşenler stream verileri üzerine aktarılabilirler. Ki, Delphi IDE&#8217;si bunu bol bol kullanmaktadır. Eğer bileşen yazımı ile ilgili 3 bölümlük makaleyi okumuş iseniz bileşenlerde bir özelliğe kaydedebilme özelliği kazandırmıştık. Yani özelliğin kaydedebilme özelliği yoksa stream üzerine aktarılamazlar. Özelliklerin kaydedilebilme işlemine burada değinmeyeceğiz. Ama isterseniz, bileşen yazımı ile ilgili makaledeki kolleksiyon ve binary özellikler kısmına göz atabilirsiniz.  </p>
<p align="justify">Bileşenlere bu özelliği kazandıran TPersistent sınıfıdır. TPersistent sınıfı ile ilgili daha fazla bilgi alabilmek için yardım dosyalarına göz atabilirsiniz. </p>
<p>Yeni bir form açalım ve bir button ekleyelim. Buttonun OnClick olayına aşağıdaki kodları yerleştirelim:</p>
<pre>
<pre class="brush: delphi">
procedure TForm1.Button1Click(Sender: TObject);
var
  Mem: TMemoryStream;
  F: TFileStream;
begin
  Mem := TMemoryStream.Create;
  F := TFileStream.Create(&#039;c:\deneme.txt&#039;, fmCreate or fmShareDenyNone);
  try
    Mem.WriteComponent(Button1);

    Mem.Position := 0; //Önemli!!!!

    F.CopyFrom(Mem, Mem.Size);
  finally
    Mem.Free;
    F.Free;
  end;
end;
</pre>
</pre>
<p align="justify">Bu programı çalıştırıp button&#8217;a basarsanız, C: bölümde deneme.txt isimli bir dosya göreceksiniz. Bu dosyayı bir hex editor ile açıp kontrol edebilirsiniz. Ama dosyaya baktığınızda TButton&#8217;a ait tüm özelliklerin olmadığını göreceksiniz. &Ccedil;ünkü burada sadece bileşenin değişmiş olan özellikleri kaydedilir. Her özellik bir default değere sahiptir. Ve bu değer değişmiş ise, WriteComponent metodu bunu stream üzerine yazacaktır.  </p>
<p align="justify">Şimdi bu dosyadaki Button1 bileşenini okutalım ve form üzerinde oluşturalım. Bunun için yeni bir uygulama açalım ve içinde Button1 isimli bir button olmamasına özen gösterelim. &Ccedil;ünkü birazdan bu bileşeni dosyadan biz oluşturacağız. </p>
<p align="justify">Form üzerine bir button ekleyelim ve ismine Button2 diyelim ve Caption olarak &quot;bileşeni dosyadan oku&quot; gibi bir şey yazalım. Ardından buttonun OnClick olayına aşağıdaki kodları yerleştirilem:</p>
<pre>
<pre class="brush: delphi">
procedure TForm1.Button2Click(Sender: TObject);
var
  F: TFileStream;
  Button1: TButton;
begin
  F := TFileStream.Create(&#039;c:\deneme.txt&#039;, fmOpenRead or fmShareDenyNone);
  try
    Button1 := TButton.Create(Self);
    Button1.Parent := Self;
    F.ReadComponent(Button1);
  finally
    F.Free;
  end;
end;
</pre>
</pre>
<p align="justify">Programı çalıştırıp Button2&#8242;ye tıklarsanız form üzerinde önceden kaydettiğimiz Button1 bileşenini göreceksiniz.</p>
<p align="justify">Önceden de dediğimiz gibi TPersistent olmayan nesneleri direk olarak stream olarak yazmak ve okumak mümkün değildir. Fakat kendinizin tanımlayacağı formatta, stream&#8217;den okuma ve stream&#8217;e yazma gibi fonksiyonlar ile nesnelerinize bu özelliği kazandırabilirsiniz. Zaten TPersistent sınıfının da yaptığı işlem budur. TPersistent sınıfına fazla girmek istemiyorum, fakat merakı olanlar TPersistent ile beraber TFiler, TReader gibi sınıflara yardımdan göz atabilirler.</p>
<p align="justify">Ek olarak bu konuda söyleyebileceğim bir tecrübem de şudur. İster VCL içinde olsun, ister başka bir yerden bulacağınız bir bileşen veya bir sınıf olsun, eğer stream ile çalışıyorlarsa şu 4 metod bunlarda bulunacaktır. LoadFromStream, LoadFromFile, SaveToStream, SaveToFile.</p>
<p align="justify">Buna tecrübe dedim çünkü literatürde bu metodlar aynı isim ve parametrelerle bulunacak diye bir şart yoktur. Fakat VCL&#8217;de bulunan bileşenlerde bolca kullanılan bu metodlar, diğer 3. parti bileşen ve sınıflarda da kolaylık ve alışkanlık olması açısından isimlerini ve yapılarını korumuştur. Eğer bir sınıf stream ile işlem yapıyorsa bilinki bu sınıfta, yukarıda bahsettiğimiz 4 metod bulunacaktır.</p>
<p align="justify">Sizler de yazacağınız sınıflarda bu metodları aynı isim ve yapıda kullanırsanız, sınıfınızı ya da bileşeninizi kullanacak olan kişiler zorlanmayacaktır. Bu metodların tanımlamasını VCL&#8217;de herhangi bir sınıf ya da bileşenin tanımlamasından kopyala yapıştır ile alabilirsiniz. Sadece burada değişecek olan kısım LoadFromStream ve SaveToStream metodlarıdır. Bu metodların içeriğini kendinize göre yeniden oluşturmalısınız. </p>
<pre> </pre>
</p>
<h1>Resource Verilerini Okuyup Yazmak</h1>
<p align="justify">Resoruce verileri, kısaca açıklamak gerekirse, exe ya da dll dosyanız içine gömülen verilerdir. Bu veriler resim, yazı, icon, form olabileceği gibi herhangibir formatta stream verisi de olabilir.  </p>
<p align="justify">Resource dosyaları &quot;.RC&quot; uzantılı bir dosyadan herhangi bir resource compiler ile derlenir. Bu derleme sonucunda &quot;.RES&quot; uzantılı bir dosya oluşacaktır. RES dosyalarını istediğiniz bir resource derleyicisi ile oluşturabilirsiniz. &Ccedil;ünkü bu res dosyaları derleyiciden bağımsızdır. Fakat RC dosyasının grameri değişiklik gösterebilmektedir. Biz bu makalede resource verilerini derlemek için delphi ile beraber gelen brcc32 derleyicisini kullanacağız. İsterseniz Visual Studio veya benzeri bir başka resource manager ile RES dosyaları oluşturabilirsiniz. Eğer Delphi 2009&#8242;a sahip iseniz, yeni &quot;Resource Manager&quot; ile daha rahat resource dosyaları oluşturabilirsiniz.</p>
<p align="center"><img width="502" height="331" align="middle" alt="Delphi 2009 - Resource Manager" src="/wp-content/uploads/Image/post99/d2009_resource_manager.jpg" /> <font size="1"> Delphi 2009&#8242;da menüden Project/Resources&#8230; altından ulaşabilrsiniz.</font></p>
<p align="justify">Biz elimizde Delphi&#8217;den başka bir şey olmadığını düşünerek işe koyulalım ve resource dosyamızı oluşturalım.</p>
<p align="justify">Notepad ile yeni bir yazı dosyası oluşturalım ve içine bir yerlerden uzun bir paragraf bulup yapıştırılım. Bunu yazi.txt ismi ile kaydelim. Aynı klasöre başka bir yazı dosyası daha oluşturalım ve içine aşağıdakileri yazalım:</p>
<pre>yazi RCDATA yazi.txt </pre>
<p align="justify">Daha sonra bu dosyayı &quot;yazi.rc&quot; ismi ile aynı klasöre kaydedelim.  </p>
<p align="justify">Burada ilk yazdığımız kelime resource&#8217;a verdiğmiz isimdir. İleride bu isme göre resource verimizi kullanacağız. İkinci kısımda RCDATA ile verimizin tipini belirledik. Burada RCDATA&#8217;nın dışında BITMAP, CURSOR ve ICON gibi ayrı ayrı tipler kullanılabilir. Bunların tam listesi için internette araştırma yapabilirsiniz. (<a target="_blank" href="http://msdn.microsoft.com/en-us/library/aa381043(VS.85).aspx">http://msdn.microsoft.com/en-us/library/aa381043(VS.85).aspx</a>). En sondaki kısım ise resource olarak kullanacağımız verimizin dosyasını giriyoruz. </p>
<p align="justify">Başlat/&Ccedil;alıştır ile cmd yazıp entere basıp komut satırına geçelim. Buradan dosyaları kaydettiğimiz klasöre geçelim(msdos biliyorsunuzdur herhalde <img src='http://www.diyezon.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  ). Ardından aşağıdaki komutu yazıp entere basalım.</p>
<pre>brcc32 yazi.rc</pre>
<p align="justify">Bir problem oluşmadı ise aynı klasörede yazi.res isimli bir dosya göreceğiz.  </p>
<p align="justify">Bu işlemlerden sonra Delphi&#8217;ye geçip yeni bir uygulama açalım. Yeni uygulamamızı .res dosyalarını oluşturduğumuz klasöre kaydedelim. Şimdi az önce oluşturduğumuz res dosyasını programımız içine gömmek için gerekli işlemleri yapalım.  </p>
<p align="justify">Unit1&#8242;in kodlarını açalım ve uygun bir boşluğa aşağıdaki derleyici direktifini girelim:</p>
<pre>
<pre class="brush: delphi">{$R yazi.res}</pre>
</pre>
<p align="justify">Programızı derlediğimizde bu res dosyasında buluna bütün veriler, resource verisi olarak programımıza gömülecektir. İsterseniz bunu ResHack gibi herhangi bir resource yöneticisi ile kontrol edebilirsiniz.</p>
<p align="justify">Şimdi bu gömülmüş olan veriyi okumamız gerekiyor diyelim. Mesela az önce programımız içine gömdüğümüz yazi.txt dosyasını bir memo içine alalım ve gösterelim.</p>
<p align="justify">Form üzerine bir adet memo bileşeni ve bir adet button ekleyelim. Button&#8217;unun OnClick olayına şunları yazalım:</p>
<pre>
<pre class="brush: delphi">var
  Res: TResourceStream;
begin
  Res := TResourceStream.Create(hInstance, &#039;yazi&#039;, RT_RCDATA);
  Memo1.Lines.LoadFromStream(Res);
end;</pre>
</pre>
<p align="justify">Programı çalıştırıp button&#8217;a basalım. Bir sorun çıkmadı ise memo bileşeninde yazi.txt dosyasındaki yazılı olan paragrafı göreceğiz.</p>
<p align="justify">Bu yaptığımız örnek ile görüyoruz ki; bir TResourceStream nesnesi, herhangi bir exe veya dll içindeki resource verilerine erişebiliyor. Create oluşturucusunda gördüğümüz ilk parametre resource verisini nereden çekeceğimizi belirliyor. Buna daha sonra değineceğiz. İkinci parametre olarak erişeceğimiz resource verisinin ismidir. Bunu RC dosyasını hazırlarken görmüştük. Aynı şekilde son parametre de RC dosyasında belirlenmişti. Bu son parametre ile resource verisinin tipini belirliyoruz. Tek fark olarak baş tarafına RT_ ön ekini getiriyoruz.</p>
<p align="justify">Sonraki işlem ise memo bileşenin Lines özelliğine streamdeki veriyi aktarıyoruz.</p>
<p align="justify">Bu mantığı kullanarak istediğiniz türde veriyi resource olarak gömebilir ve program çalışması anında kullanabilirsiniz. Bu yaptığımız örnek sadece mevcut programdan verileri çekebilir. Fakat başka bir dll ya da exe&#8217;den veri çekmemiz gerektiğinde ne yapacağız?</p>
<p align="justify">İşte bu durumda hInstance olarak girdiğimiz parametre değişecektir. &Ccedil;ünkü burada girdiğimiz parametre veriyi nereden çekeceğimizi daha doğrusu resource verisinini nerede olduğunu belirler.</p>
<p align="justify">Her program(exe, dll, ocx, ..) çalıştırıldığında windows tarafından bir handle numarası ile ilişkilendirilir. Delphi&#8217;de bu handle numarasına erişebilmek için hInstance isimli global bir değişkeni kullanıyoruz. Bu yüzden Create oluşturucusunda mevcut çalışan programımızın handle numarasını buraya birinci parametre olarak girdik.</p>
<p align="justify">Peki başka bir exe veya bir dll&#8217;in handle numarasını nasıl alırız?</p>
<p align="justify">Bunun için cevabımız LoadLibrary fonksiyonudur. Bu fonksiyon ile çağırdığımız herhangi bir program geçici olarak hafızaya yüklenir ve windows tarafından bir handle atanır. Bu handle numarası ise çıkış olarak döner. Eğer yükleme işlemi başarısız ise handle numarası 0 olarak dönecektir. Mesela yukarıda yaptığımız işlemleri bir dll içinden almaya kalkarsak nasıl yapacaktık?</p>
<pre>
<pre class="brush: delphi">var
  Res: TResourceStream;
  DLLHandle: THandle;
begin
  DLLHandle := LoadLibrary(&#039;resource.dll&#039;);
  if DLLHandle &amp;lt;&amp;gt; 0 then
  begin
    Res := TResourceStream.Create(DLLHandle, &#039;yazi&#039;, RT_RCDATA);
    Memo1.Lines.LoadFromStream(Res);
  end;
end;</pre>
</pre>
<p align="justify">Aynı işlemi exe, ocx gibi diğer çalıştırılabilir formatlar içinde yapabilirsiniz.</p>
<p align="justify">Artık herhangi bir veriyi exe içine gömüp okumayı öğrendik. Şimdi de bu işlemin bir benzerini veritabanlarında yapalım. </p>
</p>
<h1>Veritabanında Stream Kullanımı</h1>
<p align="justify">Artık buraya kadar öğrendiklerimizle herhangi bir stream verisini okuyup yazabiliriz. Burada yapacağımız işlem de pek farklı olmayacak. Bu yüzden hemen konuya girelim ve kısaca veritabanlarında nasıl stream kullanıldığını görelim.</p>
<p align="justify">Veritabanlarında bulunan tablolarda stream verilerini saklamak için alanımız BLOB tipinde olmalıdır. BLOB tipindeki alanlar binary veriler tutabilir. Fakat indeksli ve üzerinde çok arama tarama işlemleri yapılan bir tablomuz var ise bu BLOB alanlar veritabanı işlemlerini yavaşlatacaktır. Bu yüzden bu BLOB alanları başka bir tablo üzerine taşımak performansı artıracaktır.</p>
<p align="justify">Tabloda bulunan bir BLOB alana veri yazmak ve bu alandan veri okumak için TBlobStream nesneleri kullanılır. Kullanımı aşağıdaki gibidir:</p>
<pre>
<pre class="brush: delphi">
var
  blob: TBlobStream;
begin
  blob := tblBirTablo.CreateBlobStream(tblBirTablo.FieldByName(&#039;BlobAlan&#039;), bmReadWrite);
  try
    blob.Position := 0;
    blob.ReadBuffer(...);
    blob.WriteBuffer(...);
    ......
  finally
    blob.Free;
  end;
end;
</pre>
</pre>
<p align="justify">Genel manada kullanım bu şekildedir fakat daha başka yöntemler ile de blob stream oluşturulabilir. Bu örnekte BlobAlan isimli bir alan hem okuma hem de yazma işlemleri için stream olarak bizim blob stream nesnemize atanmıştır. Burada sadece okuma işlemleri yapacaksak bmRead, sadece yazma işlemi yapacaksak bmWrite parametrelerini kullanmalıyız. </p>
<h1>TStream&#8217;i .NET ve Delphi 2009&#8242;da Kullanmak</h1>
<p align="justify">TStream sınıfı Delphi&#8217;nin bileşen mantığının temelini oluşturmaktadır. Delphi, .NET ortamına taşınırıken haliyle TStream sınıfı da ufak farklılıklar ile .NET ortamına taşınmıştır. Benim burada bahsedeceğim konu sadece bu küçük farklılıklar olacaktır. Geri kalan işlemlerde buraya kadar öğrendiğiniz bilgiler fazlasıyla yetecektir.</p>
<p align="justify">.NET ortamında Pointer işlemleri kullanılmadığından TStream sınıfı ve alt sınıfları için bir değişikliğe gidilmesi şarttı. Bu yüzden, TStream sınıfı .NET ortamına taşındığında Pointer işlemleri yerine, bu işlemlerin yerini tutan belirgin ve basit tipler için ayrı ayrı metodlar hazırlandı.</p>
<p align="justify">Mesela hatırlarsanız normalde TStream sınıfında bulunan ReadBuffer ile bir srting veri yazdığımızda string&#8217;i pointer olarak type cast yapıp parametre olarak giriyorduk. .NET&#8217;de pointer kullanmadığımız için ReadBuffer yerine artık aynı isimli ve sadece özel tipler için özdeşleştirilmiş bir metodumuz var. Bu metodu kullanarak rahat bir şekilde pointer kullanmadan işlemlerimizi aynı şekilde yapabiliriz. Tabi bu daha önce yaptığımız Win32 TStream sınıfına göre çok rahatlık getirmektedir. Bu da hiç şüphesiz .Net&#8217;in getiridiği rahatlıklardan birisi.</p>
<p align="justify">ReadBuffer ver WriteBuffer metodlarından bir kaçındaki değişlikliklere göz atalım:</p>
<pre>
<pre class="brush: delphi">
    //TBytes
    procedure ReadBuffer(Buffer: TBytes; Count: Longint); overload;
    procedure WriteBuffer(const Buffer: TBytes; Count: Longint); overload;

    //Boolean
    procedure ReadBuffer(var Buffer: Boolean); overload;
    procedure WriteBuffer(const Buffer: Boolean); overload;

    //Integer
    procedure ReadBuffer(var Buffer: Integer); overload;
    procedure WriteBuffer(const Buffer: Integer); overload;
    procedure WriteBuffer(const Buffer: Integer; Count: Longint); overload; platform;

    //Char
    procedure ReadBuffer(var Buffer: Char); overload;
    procedure ReadBuffer(var Buffer: Char; Count: Longint); overload; platform;
    procedure WriteBuffer(const Buffer: Char); overload;
    procedure WriteBuffer(const Buffer: Char; Count: Longint); overload; platform;

    // AnsiChar
    procedure ReadBuffer(var Buffer: AnsiChar); overload;
    procedure ReadBuffer(var Buffer: AnsiChar; Count: Longint); overload; platform;
    procedure WriteBuffer(const Buffer: AnsiChar); overload;
    procedure WriteBuffer(const Buffer: AnsiChar; Count: Longint); overload; platform;
</pre>
</pre>
<p align="justify">Tam liste için lütfen Borland.Vcl.Classes unitinde bulunan TStream sınıfına ya da yardıma göz atınız.</p>
<p align="justify">Gördüğünüz gibi aynı ReadBuffer metodu, overload yöntemi ile bir çok kez tanımlanmış. Biz bunlardan ihtiyacımız olanı seçip kullanacağız. Boolean ve Integer yazımı için tek bir adet parametre gerektirdiğine dikkat ediniz.</p>
<p align="justify">Diyelimki stream üzerine yazdıracağımız veya okuyacağımız veri çeşidine uygun bir metod bulunmamakta. Bu durumda verimizi bir byte dizisi gibi düşünüp ona göre davranacağız. Bu durumda verimizi TBytes&#8217;a yani &quot;array of Byte&quot; tipinieçevirmemiz gerekmektedir. Bunun için herhangi bir for döngüsü tanımlayarak bu işlememizi yapabiliriz.</p>
<p align="justify">Integter ile ilgili metodlara bakarsanız, en sonda fazladan Count parametresi alan bir metod yerleştirdim. Bütün metod çeşitlerinde alternatif olarak bu ikininci parametreye sahip olan metodlar mevcuttur. Bu parametreyi mesela Integer için normalde 4 byte ayrılmasına rağmen siz 10 byte yer ayırmak istediğinizde kullanablirsiniz.  </p>
<p align="justify">Bu metodlara baktığınızda string ile ilgili bir özel metodun olmadığını farkedeceksiniz. Peki string veriyi nasıl yazacağız?</p>
<p align="justify">Bu durumda veriyi hangi encode&#8217;da yazacağımız veya okuyacağımız önemli. Veriyi istersek UTF8 formatında istersek UTF16 veya ANSI olarak okuyabilir ve yazabiliriz. Bu durumda string verimizi byte dizisine çeviren veya byte dizisini string verisine çeviren bir yönteme ihtiyacımız var. Aşağıdaki örneğe göz atalım:</p>
<pre>
<pre class="brush: delphi">var
  Mem: TMemoryStream;
  s: string;
  bytes: TBytes;
begin
  Mem := TMemoryStream.Create;
  try
    s := &#039;diyezon&#039;;
    bytes := Encoding.UTF8.GetBytes(s); //uses&#039;a System.Text eklenmeli
    Mem.WriteBuffer(bytes, Length(bytes));
    Mem.Position := 0;
    Memo1.Lines.LoadFromStream(Mem);
  finally
    Mem.Free; //Yapmanıza gerek yok.
  end;
end;</pre>
</pre>
<p align="justify">Gördüğünüz gibi string verimizi utf-8 olarak stream üzerine attık ardından memo1 ile ekranda gösterdik. Bu işlemi yapabilmek için string verisini uygun bir byte dizisine çevirmemiz gerkiyordu. Bunun için System.Text alanında bulunan Encoding sınıfını kullanıyoruz. Bu sınıfı kullanabilmek için uses&#8217;a System.Text&#8217;i eklemelisiniz.</p>
<p align="justify">İsterseniz bu yazma işlemini Encoding.Unicode ile utf-16 ya da diğer adıyla unicode olarak, isterseniz Encoding.Default ile işletim sisteminin varsayılan ansi karakterleri olarak yazıp okuyabiliriz. Önemli olan string bir şekilde byte dizisine çevrilmiş olması gerekmektedir. Ayrıca yazma ve okuma işlemlerinde aynı tip encode formatını kullandığınızdan emin olun. Yani ya her ikisinde de Unicode ya da her ikisinde de UTF8 kullanın.  </p>
<p align="justify">Eğer TBytes&#8217;dan stringe çevirim yapmamız gerekirse bu durumda aşağıdaki metodu kullanıyoruz:</p>
<pre>
<pre class="brush: delphi">s := Encoding.UTF8.GetString(bytes)</pre>
</pre>
<p align="justify">Gördüğünüz gibi .net ile bu işlemleri yapmak çok basit. Ayrıca en sonda stream&#8217;i free yapmasanız da Garbage Collector sizin için bunu yapacaktır. Fakat TFileStream kullanıyor ve dosyaya fmShareExclusive gibi kilitleyici bir parametre ile ulaşıyorsanız, stream nesnesini öldürmeden veya program kapanmadan, başka bir program dosyaya ulaşamaz, dosya kilitli kalır. </p>
<p align="justify">Bu bölümde bahsetmek istediğim başka bir konu da bu işlemleri Delphi 2009 ve üstünde yapmak. Burada bahsediyorum, çünkü bahsedeceğimiz bu konu .net ile de ilgili bir konu.</p>
<p align="justify">Aslında daha önceden gördük ki; bir stringi stream&#8217;e yazmak için stream&#8217;in kaç karakter olduğundan çok ,boyutunu bilmemiz gerekiyor. Bu yüzden ansi ya da ascii stringlerde boyut olarak her zaman Length(string) şeklinde aldık. &Ccedil;ünkü ansi stringlerde boyut = karakter sayısıdır. Fakat unicode olan stringlerde bu durum biraz farklıdır. Unicode olan stringler 1 byte değil, 2 byte yer kaplarlar. Bu yüzden WideString gibi unicode stringler ile uğraştığımızda artık boyutumuz Length*2 olmaktadır. Bildiğiniz gibi Delphi.Net ve Delphi 2009 ve yukarısı derleyicilerde &quot;string&quot; ve &quot;Char&quot; tipi unicode formatındadır. Bu derleyicilerde ansi olarak veri tanımlamamız gerektiğinde AnsiString, AnsiChar, PAnsiChar gibi tipleri kullanmalıyız. Aksi durumda bu derleyicilerde bir Char tipi 2 byte yer kaplar. Bu konu ile ilgili daha önceden örnek verdiğimizden bu konuyu burada bitiriyorum. </p>
</p>
<h1>.NET Stream Nesnesinin Kullanımı</h1>
<p align="justify">TStream&#8217;e ek olarak, .net&#8217;de aynı mekanizma bulunmaktadır. Neden bilmiyorum ama .net&#8217;de bulununa bir çok teknoloji Delphi ile benzerlik göstermektedir. Belki de c# ve .net&#8217;in fikir babası olan Anders Hejlsberg&#8217;in delphi&#8217;nin ilk temellerini atmasından kaynaklanıyor olabilir. Her neyse biz konumuza dönelim.</p>
<p align="justify">Aslında bu konu çok uzun anlatılabilecek bir konu ve burada bahsetmemiz makaleyi bir üçüncü bölüme bile taşıyabilir. Ki bence buna gerek yok. Ben sadece TStream ile arasındaki benzerlikleri gösterip, nasıl kullanıldığına dair bir örnek vermek istiyorum.</p>
<p align="justify">Bu bölümü VCL.Net kullanmak istemeyip saf .net kodu oluşturmak isteyenler veya Delphi Prism kullananlar için yazmak istedim. Aksi takdirde TStream sınıfı çoğu kez işimizi rahat bir şekilde görmektedir. Fakat bazen kullandığımız .net sınıfları .net&#8217;in stream sınıflarına ihtiyaç duyabilmektedir.  </p>
<p align="justify">VCL&#8217;de bulunan bazı stream sınıfları yerine .net&#8217;de bulunan karşılıklarını şöyle sıralayabilriiz:</p>
<pre>TFileStream =&gt; FileStream
TMemoryStream =&gt; MemoryStream
TIdTCPClient =&gt; NetwrokStream</pre>
<p>vs..</p>
<p align="justify">Tabi bu stream sınıflarının metodları benzerlik gösterse de kullanımları farklıdır. Bu stream sınıflarını kullanabilemek için uses&#8217;a System.IO alanını eklemelisiniz. Biz buradan MemoryStream ile ilgili bir örnek verelim:</p>
<pre>
<pre class="brush: delphi">var
  Mem: MemoryStream;
  s: string;
  bytes: array of Byte;
begin
  Mem := MemoryStream.Create;
  s := &#039;diyezon&#039;;
  bytes := Encoding.UTF8.GetBytes(s);
  Mem.Write(bytes, 0, Length(bytes));</pre>
</pre>
<p align="justify">Gördüğünüz gibi kullanım olarak TStream&#8217;den farklı değil. Fakat Write metodunu TStream&#8217;de olduğu gibi bir çok çeşidi olduğunu zannetmeyin sakın. &Ccedil;ünkü MemoryStream sınıfında Write metodunun sadece iki adet varyasyonu mevcut. Bu metodda ilk parametre byte dizisi, ikinci parametre nereden itibaren yazılacağı yani ofset ve üçüncü parametre olarak yazdırılacak verinin uzunluğu giriliyor.</p>
<p align="justify">Bunun dışında bu string ve binary değerleri rahat bir şekilde okuyup yazabilmek için çeşitli ek sınıflar kullanılmaktadır. Mesela BinaryWriter ile binary değerleri stream üzerine yazabiliriz veya StreamReader ile streamden karakter verilerini okuyabiliriz. Aşağıdaki örneğe göz atalım:</p>
<pre>
<pre class="brush: delphi">var
  Mem: MemoryStream;
  textW: StreamWriter;
  s: string;
  bytes: array of Byte;
begin
  Mem := MemoryStream.Create;
  textW := StreamWriter.Create(Mem);
  s := &#039;diyezon&#039;;
  textW.Write(s);</pre>
</pre>
<p align="justify">Yukarıda yaptığımız işlemin aynısını burada StreamWriter sınıfı ile gerçekleştirdik. İşte burada StreamWriter sınıfının Write metodunda bir çok varyasyon mevcuttur. Bu varyasyonlara yardım dosyalarından ulaşabilirsiniz. Mesela bir Integer değer yazmak için yine StreamWriter&#8217;a ait Write metodunu kullanmamız kafidir.</p>
<p align="justify">Bunun dışında bir de kullanabileceğimiz BinaryWriter ve BinaryRead sınıfları mevcuttur. Bu sınıflar ise binary verileri okuyup yazmak için kullanılırlar. Ama kullanım aynı olduğu için bu konuya da girmeyeceğim.  </p>
<p align="justify">Burada en son olarak BufferedStream sınıfından bahsetmek istiyorum. Bu stream ile okuma ve yazma işlemlerini başka bir stream üzerinde buffer&#8217;layarak daha hızlı işlemler yaptırabilirsiniz. Hatırlarsanız TMemoryStream sınıfından bahsederken, bu sınıfı diğer stream sınıflardaki okuma yazma işlemleri yerine kullanılabileceğinden ve bunun performansı artırdığından bahsetmiştik. İşte burada TMemoryStream bir buffer vazifesi görmektedir.</p>
<p align="justify">Mesela bir dosyaya bir çok veri yazılacak, ve bu veriler parça parça yazılacak. Ayrıca bu dosya stream&#8217;i üzerinde bir çok seek işlemi yani ileri geri alma işlemleri yapılacak. İşte bu durumda direk olarak TFileStream gibi bir dosya stream nesnesi ile uğraşmak yerine hafıza stream&#8217;leri ile uğraşmamız işlemlerimizin performansını önemli derecede artıracaktır. Yapacağımız stream işlemlerini bir hafıza stream&#8217;i üzerinde gerçekleştirip, en son işlemimiz bittiğinde hafıza stream&#8217;ini ilgili stream nesnesine aktarmak en akıllıca yöntemdir. İşte bu yönteme buffering ya da tamponlama tekniği denir.  </p>
<p align="justify">İşte bu anlattığımız tekniği BufferedStream nesnesi gerçekleştirmektedir. Aynı işlemi ekstra bir memory stream tanımlayarak siz de oluşturabilirisiniz, hatta bu makalede buna benzer örnekleri çok gördük.</p>
<p align="justify">BufferedStream sınıfını kullanmak isteyenler, yardım dosyalarında ve msdn&#8217;de tanımlamasına bakabilirler. Artık stream mantığını kafanızda yerleştirdiğinize göre burada kullanımını vermek artık sıkacaktır.</p>
<h1>Sonuç</h1>
<p align="justify">Bir makalenin daha sonuna geldik. Bu makale serisinde VCL ve .net&#8217;de kullanılan stream sınıflarına değinmeye çalıştık ve bu sınıfların kullanımına dair çeşitli örnekler vermeye çalıştık.</p>
<p align="justify">Tabi ki burada verdiklerimiz stream için en temel konulardır. Bu makaleyi okuduktan sonra artık kafanızda stream&#8217;e ait somut düşüncelerin oluştuğunu zannediyorum. Ve bundan sonra önünüze gelecek olan bir çok stream işlemini rahat bir şekilde halledebileceğinizi umut ediyorum.</p>
<p align="justify">Fikir, eleştiri ve yorumlarınızı bekliyorum. Ayrıca makaledeki hataları bildirirseniz sevinirim. </p>
<p>Hayırlı çalışmalar.</p>
<p align="center"><strong>Fatih Tolga ATA, Şubat 2009</strong></p>
</p></p>
]]></content:encoded>
			<wfw:commentRss>http://www.diyezon.com/2009/02/17/streamde-uzmanlasalim-bolum-2/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Stream&#8217;de Uzmanlaşalım&#8230; (Bölüm 1)</title>
		<link>http://www.diyezon.com/2008/09/13/streamde-uzmanlasalim-bolum-1/</link>
		<comments>http://www.diyezon.com/2008/09/13/streamde-uzmanlasalim-bolum-1/#comments</comments>
		<pubDate>Sat, 13 Sep 2008 07:15:49 +0000</pubDate>
		<dc:creator>Fatih Tolga Ata</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[stream]]></category>
		<category><![CDATA[TBlobStream]]></category>
		<category><![CDATA[TFileStream]]></category>
		<category><![CDATA[TMemoryStream]]></category>
		<category><![CDATA[TResourceStream]]></category>
		<category><![CDATA[TStream]]></category>

		<guid isPermaLink="false">http://www.diyezon.com/?p=62</guid>
		<description><![CDATA[Diyezon&#8217;a yazmayalı baya bir süre olmuştu. Neredeyse diyezon, kendi işlerim yüzünden hayalet site olma yolunda ilerliyordu. Bundan önceki son yazımı yaklaşık bir sene önce yayınlamıştım. Her neyse&#8230; 
Stream&#8217;ler hakkında bir makale yazmak, yapacaklarım arasındaydı. En son gelen bir istek üzerine stream konusuna el atalım istedim. Bu makale serimizde hem VCL&#8217;de bulunan TStream, hem de .NET&#8217;de [...]]]></description>
			<content:encoded><![CDATA[<p align="justify">Diyezon&#8217;a yazmayalı baya bir süre olmuştu. Neredeyse diyezon, kendi işlerim yüzünden hayalet site olma yolunda ilerliyordu. Bundan önceki son yazımı yaklaşık bir sene önce yayınlamıştım. Her neyse&#8230; </p>
<p align="justify">Stream&#8217;ler hakkında bir makale yazmak, yapacaklarım arasındaydı. En son gelen bir istek üzerine stream konusuna el atalım istedim. Bu makale serimizde hem VCL&#8217;de bulunan TStream, hem de .NET&#8217;de bulunan Stream sınıfları ve kullanımlarından bahsetmeye çalışacağım.</p>
<p align="justify">Özellikle TStream üzerinde stringlerle uğraşmak gibi bir çok konu programcıların kafasını karıştırabilmektedir. Bu ve bunun gibi sorunların çözümlerine de çareler bulmaya çalışacağız. </p>
<p><span id="more-62"></span></p>
<h1>Stream Nedir?</h1>
<p align="justify">Bilgisayar ortamında yazma-okuma işlemlerinden bahsettiğimizde aklımıza bir çok şey gelir. Mesela disk üzerinde bir dosyaya erişip üzerinde yazma ve okuma işlemleri yapabiliriz. Veya bir hafıza bölgesinde yazma-okuma işlemleri yapabileceğimiz gibi bir veritabanındaki bir tablo alanında veya daha başka çeşit çeşit ortamlar üzerinde yazma-okuma işlemleri yapabiliriz. Bu bütün yazma-okuma işlemleri aslında her zaman yöntemi, işleyişi aynı olan bir fonksiyonlar dizisidir. Sadece alt planda yazma-okuma işleminin yapılacağı yüzeyde(disk, hafıza&#8230;) farklılıklar oluşmaktadır.</p>
<p align="justify">İşte bu işlemleri kolay bir şekilde, ortak bir sınıf üzerinden yapmak istediğimiz zaman ortaya Stream gibi bir yapı çıkıyor. Stream nesnelerini kullanarak, bir dosyayı yazıp okuyabilir, aynı fonksiyonları kullanarak bir hafıza bölgesine ulaşılabilir veya yine aynı fonksiyonlar ile bir veritabanı tablosunun alanına ulaşılabilir.</p>
<p align="justify">Stream, burada anlatılan basit özelliklerin yanında, stream verileri üzerinde bir çok işlemi yapabilmemize imkan sağlamaktadır. Bu makalemizde bunları ayrıntısı ile görmeye çalışacağız.</p>
<h2>Stream Verileri</h2>
<p align="justify">Stream nesneleri, stream verilerini kullanmamızı sağlamaktadır. Stream verileri, üzerinde çalışılan yere göre çeşitli veriler içermektedir. Bu bir resim dosyası olabileceği gibi kendimize özel çok farklı bir format da olabilir.</p>
<p align="justify">Buradan da anlaşılacağı gibi, stream verileri bir nevi bir byte dizisidir. Yani aynı veriyi &quot;<font face="Courier New"><strong>array of</strong> byte</font>&quot; şeklinde kullanarak da oluşturabilir ve üzerinde işlem yapabiliriz. Fakat bu, bize ek masraftan başka bir şeye yaramayacaktır. &Uuml;stelik yazacağımız fonksiyon ya da sınıf stream nesneleri gib esnek ve hızlı olmayacaktır.</p>
<h2>Stream Ne Zaman Kullanılmalı?</h2>
<p align="justify">Yapacağımız işlemerde stream mi, array mi kullanılmalı noktasında karar vermek istediğimizde bir kaç hususu göz önünde bulundurmamız gerekecektir.</p>
<p align="justify">İlk olarak bilmemiz gereken stream verileri array&#8217;de olduğu gibi indeksli <strong>değildir</strong>. Eğer kullanacağımız veriler, sabit veri tipi ise ve bir for döngüsüne girmek üzere oluşturulacak ise kullanacağımız nesne array olacaktır. Stream&#8217;de de istediğiniz veriye array&#8217;de olduğu gibi ulaşabilirsiniz. Fakat bu ileride de göreceğimiz gibi array indekslerinden farklı olarak çalışmaktadır. </p>
<p align="justify">Eğer bu veri dizimiz bir disk veya bir hafıza bölgesi üzerine yazılacaksa ve burada işlem yapılacaksa, bu durumda array yerine stream kullanılması bize bir çok kolaylık sunacaktır.  </p>
<p align="justify">Aynı şekilde veri dizimiz sabit veri tipleri içermiyorsa yani sadece <strong><font face="Courier New">array of string</font></strong> veya sadece <font face="Courier New"><strong>array of</strong> Integer</font> değilse bu durumda yine stream kullanmalıyız. Mesela veri dizimizin birinci elemanı 4 karakterlik bir <strong><font face="Courier New">string</font></strong>, ikinci elmanı bir <font face="Courier New">Integer</font>, üçüncü elemanı bir <font face="Courier New">Boolean</font>, vs&#8230; gibi. Bu şekilde kendi dosya formatımızı oluşturabileceğimiz gibi, doc, exe, zip gibi bir çok binary dosyasının yapısını bildiğimizde bu dosyaların verilerine de ulaşabiliriz.</p>
<h1>VCL&#8217; de Stream Kullanımı</h1>
<p align="justify">İlk başta VCL ile stream kullanımına değinelim. Ardında .Net üzerinde stream kullanımını göreceğiz.</p>
<p align="justify">VCL ile stream işlemleri yapabilmek için <font face="Courier New">TStream </font>sınıfından türeyen bir çok sınıf mevcuttur. <font face="Courier New">TStream </font>sınıfı <strong>abstract </strong>olarak tanımlanmış bir sınıftır ve direk olarak kullanılamaz. Abstract olarak oluşturulmuş olan bir sınıftan <strong>nesne oluşturulamaz.</strong> Ancak bu abstract sınıftan türemiş olan <strong>abstract olmayan sınıflardan</strong> nesne oluşturulabilir. Bu şekilde bir yapı olması, tüm stream sınıflarının aynı olan bir çok metod ve özelliğe sahip olmasını sağlar. Abstract sınıfların avantajları burada anlatılacak kadar kısa değil. Bu yüzden şimdilik bu kadar bilmemiz yeterli.</p>
<p align="justify">Help dosyasına göz attığımızda <font face="Courier New">TStream </font>sınıfından türemiş bir çok stream sınıfını görüyoruz.</p>
<p align="justify"><font face="Courier New">TFileStream</font>: Disk yani <strong>dosya sistemi</strong> üzerinde bulunan dosyalarla işlem yapmamızı sağlar.</p>
<p align="justify"><font face="Courier New">TMemoryStream</font>: Dosyalar üzerinde yapılan aynı işlemleri bir <strong>hafıza bölgesi</strong> üzerinde yapmamızı sağlar.</p>
<p align="justify"><font face="Courier New">TBlobStream</font>: Veritabanında bulunan bir tablodaki <font face="Courier New">BLOB</font> yani binary veri olarak tanımlanmış alanlar üzerinde okuma-yazma işlemleri yapmamızı sağlar.</p>
<p align="justify"><font face="Courier New">TResourceStream</font>: Resource dediğimiz, exe, dll gibi çalıştırılabilir dosyalar içine gömülü olan resim, yazı, dialog kutuları, versiyon bilgisi gibi çok çeşitli verileri okuyup yazmamıza imkan sağlamaktadır.</p>
<p align="justify"><font face="Courier New">TStringStream</font>: Aynı işlemleri bir string verisi üzerinde işlem yapabilmemizi sağlar. Böylece, sınırsız uzunlukta bir string üzerinde stream ile yapılabilecek her türlü işlemler yapılabilir.</p>
<p align="justify">Bunlar, temelde kullanılan stream çeşitleridir. Bunun gibi özel olarak tasarlanmış bir çok stream sınıfı da olabilir. Fakat hepsi TStream sınıfından türediği için, kullanımları da aynı olacaktır.  </p>
<h1>Dosyalar &Uuml;zerinde Stream İşlemleri</h1>
<p align="justify">Dosyalar ile uğraşırken <font face="Courier New">TFileStream </font>kullanıldığını söylemiştik. <font face="Courier New">TStream </font>sınıflarının nasıl kullanıldığını, sonucu kolay görme açısından ilk başta dosyalar üzerinde görelim. İleride diğer stream çeşitlerine de değineceğiz. </p>
<pre>
<pre class="brush: delphi">
var
  AFile: TFileStream;
  Buffer: String;
begin
  AFile := TFileStream.Create(&#039;C:\deneme.txt&#039;, fmCreate or fmShareDenyNone);
  try
    Buffer := &#039;Deneme&#039;;
    AFile.WriteBuffer(Pointer(Buffer)^, Length(Buffer));
  finally
    AFile.Free;
  end;
</pre>
</pre>
<p align="justify">Bu kodları bir button&#8217;nun <font face="Courier New">OnClick </font>olayına yerleştirip deneyelim. Kodlar çalıştırıldığında, <font face="Courier New">C: </font>üzerinde eğer bir erişim ihlali yoksa <em>deneme.txt</em> isminde bir dosya göreceğiz.</p>
<p align="justify">İlk bakışta bu kodlar ürkütücü gelebilir. Fakat konular ilerledikçe stream kullanmanın ne kadar rahat olduğunu göreceğiz.</p>
<p align="justify"><font face="Courier New">AFile</font>&#8216;ın oluşturulduğu satıra bakalım. Bir <font face="Courier New">TFileStream </font>nesnesi oluşturabilmeniz için iki adet parametreye ihtiyacınız var. Birincisi <strong>dosyanın yolu</strong>, ki bizim örneğimizde &quot;<em>deneme.txt</em>&quot; olarak verilmiş. İkinci parametremiz ise bu dosya üzerinde yapacağımız <strong>işlem kipini ve paylaşım kipini</strong> belirler. İşlem ve paylaşım kipleri &quot;<font face="Courier New"><strong>or</strong></font>&quot; kelimesi ile birleştirilerek kullanılır. Şimdi bu kiplerin ne olduğuna bakalım:</p>
<p><strong>İşlem Kipleri: </strong></p>
<p align="justify"><font face="Courier New">fmCreate</font>: Verilen dosya yolunda eğer dosya yoksa oluşturur. Eğer dosya var ise üzerine yazar.</p>
<p align="justify"><font face="Courier New">fmOpenRead</font>: Dosyayı sadece okuma amaçlı açar.</p>
<p align="justify"><font face="Courier New">fmOpenWrite</font>: Dosyayı sadece yazma amaçlı açar.</p>
<p align="justify"><font face="Courier New">fmOpenReadWrite</font>: Dosyayı hem okuma hem de yazma için açar.</p>
<p><strong>Paylaşım Kipleri:</strong></p>
<p align="justify"><font face="Courier New">fmShareExclusive</font>: Diğer uygulamaların, bizim üzerinde çalıştığımız dosya üzerinde hem <strong>okuma </strong>hem de <strong>yazma</strong> işlemlerini <strong>yasaklar</strong>.</p>
<p align="justify"><font face="Courier New">fmShareDenyWrite</font>: Diğer uygulamalar dosyayı okuyabilir fakat üzerinde <strong>yazma </strong>işlemi <strong>yapamazlar</strong>.</p>
<p align="justify"><font face="Courier New">fmShareDenyRead</font>: Diğer uygulamaların, dosya üzerinde <strong>okuma </strong>işlemi yapmasını <strong>yasaklar</strong>.</p>
<p align="justify"><font face="Courier New">fmShareDenyNone</font>: Ne okuma ne de yazma yasağı koyar. Yani diğer uygulamalar, dosya üzerinde yazma ve okuma yapabilirler. </p>
<p align="justify"><font face="Courier New">fmShareCompat</font>: Eskiden dos zamanlarında kullanılan <em>FCB(File Control Block)</em> ile uyumluluğu sağlamak için kullanılır. <em>Windows </em>üzerinde <font face="Courier New">fmShareExclusive</font>, <em>Linux </em>üzerinde ise <font face="Courier New">fmShareDenyNone </font>ile aynıdır. Bu yüzden bunu es geçebilirsiniz.</p>
<p align="justify">Yukarıda verdiğimiz örnekte olduğu gibi işlem kipleri ve paylaşım kipleri &quot;<font face="Courier New"><strong>or</strong></font>&quot; kelimesi ile birleştirilerek kullanılır. Örneğimizde dosya <font face="Courier New">fmCreate </font>ile eğer yoksa oluşturulacak ve eğer varsa üzerine yazılacaktır. Ve dosya <font face="Courier New">fmShareDenyNone</font> kipinde olduğu için üzerinde herhangi bir okuma yazma yasağı bulumamaktadır.</p>
<p align="justify">Ardından <strong><font face="Courier New">try..finally</font></strong> buloğunda, stream üzerine yazma işlemleri yapılmaktadır. Şimdilik bu kısmı anlatmayacağız. &Ccedil;ünkü okuma ve yazma işlemleri için ayrı bir konu başlığımız bulunmaktadır.</p>
<p align="justify"><font face="Courier New"><strong>finally</strong></font> bloğunda gördüğümüz <font face="Courier New">Free </font>metodu dosyalar üzerinde fazladan bir önem arz etmektedir. &Ccedil;ünkü bir kez <font face="Courier New">TFileStream </font>ile bir dosyaya erişildiğinde, bu stream nesnesi <font face="Courier New">Free </font>oluncaya yani yok edilinceye kadar, dosyanın handle&#8217;ı işletim sistemi tarafından tutulur. Dosya handle&#8217;ı aktif olduğu sürece dosyanın silinmesi taşınması işletim sistemi tarafından engellenir. Ve genelde herkesin karşılaştığı &quot;<em>Bu dosya bir uygulama tarafından kullanıyor.</em>&quot; gibi bir mesaj alırız. Bunun için <em>unlocker</em>, <em>process explorer </em>gibi harici programlar ile aktif kalan bu handle&#8217;lar kapatılarak dosyalar silinebilir. Bu yüzden okuma yazma işlemlerimiz biter bitmez stream nesnemizi <font face="Courier New">Free </font>yapmalıyız.  </p>
<p align="justify">Kısaca bir stream nesnesini oluşturmak için aşağıdaki <strong>şablon</strong>u kullanmalıyız:</p>
<pre>
<pre class="brush: delphi">
var
  AStream: TBirStreamSınıfı;
begin
  AStream := TBirStreamSınıfı.Create({Stream sınıfına ait parametreler...});
  try
    //Stream üzerinde okuma yazma işlemleri
  finally
    AStream.Free;
  end;
end;
</pre>
</pre>
<h1>Stream &Uuml;zerinde Okuma-Yazma İşlemleri</h1>
<p align="justify">Stream&#8217;ler üzerinde yazma işlemleri için başlıca <font face="Courier New">Write </font>ve <font face="Courier New">WriteBuffer </font>metodları ve okuma işlemleri için de <font face="Courier New">Read </font>ve <font face="Courier New">ReadBuffer </font>metodları tanımlanmıştır. Şimdi göreceğimiz işlemler <em>Win32</em>&#8216;ye göre anlatılacaktır. İleride ayrı bir başlık halinde <font face="Courier New">TStream </font>sınıfının <em>.NET</em> üzerinde nasıl yazma okuma yapabileceğiniz de göreceğiz. Ayrıca .NET içinde bulunan <font face="Courier New">Stream </font>sınıflarına değineceğiz. </p>
<p align="justify">Stream üzerinde yapılan okuma yazma işlemleri bir <strong>işaretçi </strong>vasıtası ile yapılır. Bu işaretçi hangi pozisyonda ise o pozisyonda okuma ve yazma işlemi yapılır. İlk stream nesnesi oluşturulduğunda işaretçinin pozisyonu <strong>sıfırdır</strong>. Bu metodlar çağrıldığı vakit, yani her okuma ve yazma işleminin <strong>sonunda </strong>stream işaretçisi <strong>bir sonraki</strong> pozisyona kayar. Stream işaretçisi, stream verisinin sonuna gelip gelmediğini test etmek için <font face="Courier New">Eof </font>metodu kullanılır.Bu paragraf streamlerin çalışma mantığının anlaşılması için önemlidir. Bu yüzden dönüp ikinci bir defa okumanızda fayda var. </p>
<p align="justify">Sonunda <font face="Courier New">Buffer </font>yazan metodların yazmayanlardan farkından söz etmek istiyorum. Tek fark, &quot;istisna&quot; (<em>Exception</em>) yakalama özelliğidir. Yani mesela <font face="Courier New">ReadBuffer </font>metodu yine alt planda <font face="Courier New">Read </font>metodunu çalıştırır. Fakat <font face="Courier New">Read</font> metodu bir sorunla karşılaşır ve okuyamaz ise, <font face="Courier New">ReadBuffer</font> metodu, <font face="Courier New">EReadError</font> istisnasını yollar. Aynı şekilde <font face="Courier New">WriteBuffer</font> da sorun yaşadığında <font face="Courier New">EWriteError</font> istisnasını yollar. Böylece okuma ve yazma işlemlerinde herhangi bir hata olup olmadığının tespiti için <font face="Courier New">ReadBuffer</font> ve <font face="Courier New">WriteBuffer</font> ile beraber istisnaların yaklanması kafidir.</p>
<p align="justify">Okuma ve yazma metodları, işlemlerini bir <strong>buffer</strong> aracılığı ile gerçekleştirir. Bu buffer bir <strong><font face="Courier New">string</font></strong> olabilceği gibi bir <font face="Courier New">Integer</font>, bir <strong><font face="Courier New">record</font></strong> dahi olabilir. Bu yüzden her veri tipi için okuma ve yazma işlemlerinin nasıl olduğunu ayrı ayrı incelemeye çalışalım.</p>
<h2>Sayısal Değelerin Okunması ve Yazılması</h2>
<p align="justify"><font face="Courier New">Integer</font>, <font face="Courier New">Byte</font>, <font face="Courier New">Double</font> gibi sayısal verileri okuyup yazmak diğerlerine nazaran en basit işlemdir. Ekstra bir çevirim yapmamıza gerek kalmamaktadır.</p>
<p align="justify">Bunu bir örnek üzerinde görelim. İki adet button yerleştirelim. Birinin <font face="Courier New">Caption</font> özelliği &quot;Yaz&quot;, diğerinin <font face="Courier New">Caption</font> özelliği de &quot;Oku&quot; olsun. &quot;Yaz&quot; butonuna çift tıklayalım ve şunları yazalım:</p>
<pre>
<pre class="brush: delphi">
procedure TForm1.btnYazClick(Sender: TObject);
var
  BirStream: TFileStream;
  BirInteger: Integer;
  BirDouble: Double;
  BirReal: Real;
  BirByte: Byte;
begin
  BirStream := TFileStream.Create(&#039;c:\deneme.bin&#039;, fmCreate or fmShareDenyNone);
  try
    BirInteger := 15000;
    BirDouble := 1.3;
    BirReal := 123.123123123;
    BirByte := 65;
    BirStream.WriteBuffer(BirInteger, SizeOf(Integer));
    BirStream.WriteBuffer(BirDouble, SizeOf(Double));
    BirStream.WriteBuffer(BirReal, SizeOf(Real));
    BirStream.WriteBuffer(BirByte, SizeOf(Byte));
  finally
    BirStream.Free;
  end;
end;
</pre>
</pre>
<p align="justify">Gördüğünüz gibi bir <font face="Courier New">TFileStream</font> nesnesi oluşturduk ve bu stream nesnesini kullanarak dosyaya bir takım sayısal değerler yazdırdık. Mesela ilk yazdırma örneğimizde <em>buffer</em>, <font face="Courier New">Integer</font> tipinde bir değişkendir. Bu <font face="Courier New">Integer</font> değeri yazdırırken ne kadar yazdırılması gerektiğini <font face="Courier New">WriteBuffer</font> metodunun <strong>ikinci</strong> parametresi olarak giriyoruz. Yani yazdıracağımız bu buffer kaç byte yer tutacak, onu ikinci parametre olarak giriyoruz. Örneğimizde, bu parametre olarak <font face="Courier New">Integer</font>&#8216;ın boyutunu giriyoruz. Bunun için <font face="Courier New">SizeOf</font> fonksiyonundan faydalanıyoruz. Help dosyasına baktığımızda, <font face="Courier New">Integer</font>&#8216;ın 4 byte yer kapladığını görürüz. Burada yaptığımız işlem dosyada 4 byte&#8217;lık bir alana <font face="Courier New">Integer</font> değeri yazdırmaktan ibarettir.</p>
<p align="justify">Ardından gelen yazma işlemleri de aynı mantık ile yapılmaktadır. Burada dikkat etmemiz gereken nokta ikinci parametredir. &Ccedil;ünkü bu parametre yanlış girilirse hem yazmada hem de okumada eksik ve yanlış verilerle karşılaşırız. Bu örnekten de anladığımız gibi sayısal veriler ile uğraşırken <font face="Courier New">SizeOf(<em>DeğişkeTipi</em>)</font> şeklinde ikinci parametreyi belirlememiz yeterlidir.</p>
<p align="justify">Şimdi bu programı çalıştırıp Yaz butonuna basalım. Diskin <font face="Courier New">C:</font> bölümünde <em>deneme.bin</em> isimli bir dosya oluşmuş olmalı. Herhangi bir hex editor ile (<a href="http://mh-nexus.de/en/" target="_blank">HxD</a>) bu dosyayı açıp inceleyebilirsiniz. Eğer ayrıntısına girmek isterseniz <font face="Courier New">Integer</font> ve diğer sayısal değerlerin nasıl yazıldığını inceleyebilirsiniz. Mesela demiştik ki dosyamızın ilk 4 byte&#8217;ında bir <font face="Courier New">Integer</font> bulunmaktadır. Dosyamızı bir hex editör ile açtığımızda ilk 4 byte&#8217;ında <font face="Courier New">98 3A 00 00</font> yazdığını görürüz. IBM<em> (x86)</em> tabanlı makinelerde bu veriler tersten yazılırlar. Bu yüzden bu ifadeyi <font face="Courier New">00 00 3A 98</font> olarak düşüneceğiz. Hesap makinesini açıp 16&#8242;lık <em>Hex</em> moda geçelim. Tabi bunu yapabilmek için bilimsel moda geçmeniz gerekmektedir. Hex modu seçili iken <font face="Courier New">3A98</font>&#8216;i yazalım. Ardından <em>Dec</em> yani ondalık olarak her zaman kullandığınız sayı formatına çevirelim. Ve karşımızda <font face="Courier New">15000</font> sayısını göreceğiz ki bu değer örneğimizde <font face="Courier New">Integer</font> olarak dosyanın baş tarafına yazdırılmıştı. Diğer veri tiplerinin nasıl kaydedildiğini bu şekilde inceleyerek bulabilirsiniz. </p>
<p align="justify">Şimdi gelelim bu değerleri okumaya&#8230; Oku butonumuza çift tıklayıp şu kodları girelim:</p>
<pre>
<pre class="brush: delphi">
procedure TForm1.btnOkuClick(Sender: TObject);
var
  DosyaOku: TFileStream;
  BirInteger: Integer;
  BirDouble: Double;
  BirReal: Real;
  BirByte: Byte;
begin
  DosyaOku := TFileStream.Create(&#039;c:\deneme.bin&#039;, fmOpenRead or fmShareDenyNone);
  try
    DosyaOku.ReadBuffer(BirInteger, SizeOf(Integer));
    DosyaOku.ReadBuffer(BirDouble, SizeOf(Double));
    DosyaOku.ReadBuffer(BirReal, SizeOf(Real));
    DosyaOku.ReadBuffer(BirByte, SizeOf(Byte));
    ShowMessage(&#039;Integer: &#039; + IntToStr(BirInteger) + #13 +
      &#039;Double: &#039; + FloatToStr(BirDouble) + #13 +
      &#039;Real: &#039; + FloatToStr(BirReal) + #13 +
      &#039;Byte: &#039; + IntToStr(BirByte)
    );
  finally
    DosyaOku.Free;
  end;
end;
</pre>
</pre>
<p align="justify">Şimdi bu programı çalıştıralım ve bu sefer oku butonuna basalım. Ve karşımızda dosyaya yazdırdığımız değerler&#8230;</p>
<p align="justify">Gördüğünüz gibi okuma işlemi yazma işleminden farklı değil. İhtiyacımız olan tek şey bir <strong>buffer</strong> ve bu buffer&#8217;ın tipinin <strong>boyutu</strong>. Eğer değişken tipinin boyutunu yanlış girersek veya okuma sırasını yer değiştirirsek, istediğimiz sonucu alamayız. &Ccedil;ünkü stream üzerine yazarken, <strong>bu sırada</strong> ve <strong>bu değişken tipleri</strong> ile yazılmıştır. Okurken de <strong>aynı sıra</strong> ve <strong>uzunlukta</strong> okunmalıdır. Bu yüzden, bu şekilde kendi dosya formatlarımızı oluştururken, bir plan üzerinde dosya formatında hangi pozisyonda hangi veri bulunacağı ve boyutu belirlenmelidir.</p>
<p align="justify">Gördüğünüz gibi sayısal verileri okumak ve yazmak hiç de zor değil. Şimdi dilerseniz karakter dizilerini nasıl okuyup yazacağımızı görelim.</p>
<h2>Karakter Dizilerinin Okunup Yazılması</h2>
<p align="justify">Karakter dizisi dediğimizde aklımıza gelen ilk değişken tipleri <font face="Courier New">string</font> ve <font face="Courier New">PChar</font>&#8216;dır. Eğer Delphi 2009&#8242;dan önceki bir sürüm kullanıyorsanız bu değişken tipleri <em>ANSI</em>&#8216;dir. Yani her bir karakter <strong>bir byte</strong> yer kaplar. D2009 ve sonrası için her bir karakter <em>Unicode</em> olduğu için <strong>2 byte</strong> yer kaplarlar.  </p>
<p align="justify">İlk önce <font face="Courier New">PChar</font> verilerini nasıl yazıyoruz ve okuyoruz onu görelim:</p>
<pre>
<pre class="brush: delphi">
procedure TForm1.btnYazClick(Sender: TObject);
var
  DosyaYaz: TFileStream;
  Karakterler: PChar;
begin
  DosyaYaz := TFileStream.Create(&#039;c:\deneme.txt&#039;, fmCreate or fmShareDenyNone);
  try
    Karakterler := &#039;http://www.diyezon.com&#039;;
    DosyaYaz.WriteBuffer(Karakterler^, Length(Karakterler));
  finally
    DosyaYaz.Free;
  end;
end;
</pre>
</pre>
<p align="justify">Karakter verililerini yazdırırken ve okurken dikkat etmemiz gereken nokta yine verinin <strong>kapladığı</strong> alandır. Karakter dizisinin kapladığı alanı hesaplayabilmek için <strong>bir karakterin</strong> ne kadar yer kapladığını bilmemiz gereklidir. Önceden de dediğim gibi, eğer karakterleriniz ANSI ise yani Unicode değilse, her bir karakter <strong>1 byte</strong> yer kaplar. Bu yüzden bu örneğimizde yazılacak uzunluk olarak karakter sayısını verdik. &Ccedil;ünkü ANSI karakter katarlarında karakter sayısı, karakter dizisinin boyutuna <strong>eşittir</strong>. Fakat <font face="Courier New">PChar</font> yerine <font face="Courier New">PWideChar</font> kullansa idik ya da bu kodları D2009 ve üzeri bir sürümde çalıştırsa idik her bir karakteri 2 byte olarak alacaktık. Bu durumda karakter sayısı karakter dizisinin boyutuna eşit değil, boyutun <strong>yarısı</strong> kadarı olacaktı. Yani Yukarıdaki kodda <font face="Courier New">PChar</font>&#8216;ı <font face="Courier New">PWideChar</font> yaparsak <font face="Courier New">WriteBuffer</font> metodunun ikinci paramteresine <font face="Courier New">Length(Karakterler) * 2</font> yazmamız gerekiyordu. Bu bahsettiğimizi örneğimizde değiştirip deneyin ve bir hex editör ile her bir karakterin 2 byte yer kapladığına şahid olun. Ama daha sonra tekrar <font face="Courier New">PChar</font> olarak değiştirip dosyayı tekrar yazın. &Ccedil;ünkü birazdan yapacağımız okuma işleminde <font face="Courier New">PChar</font> olarak okutacağız.</p>
<p align="justify">Okuma işlemine geçmeden önce <font face="Courier New">WriteBuffer</font> metodunda ilk parametreye bir göz atalım. Burada gördüğünüz gibi <font face="Courier New">Karakterler</font> değişkeninin sonuna <font face="Courier New">^</font> operatörünü ekledik. Bu operatör, işaretçilerle (<em>pointer</em>) birlikte kullanılan özel bir operatördür. Bir işaretçinin <strong>işaret ettiği esas değere</strong> erişmek için bu operatörü kullanıyoruz. Örneğimizde ise PChar tipindeki değişkenimizin işaret ettiği gerçek değeri buffer parametresi olarak girdik. &Ccedil;ünkü <font face="Courier New">PChar</font> da bir çeşit işaretçidir. Ve <font face="Courier New">Karakterler^</font> yazmak ile bu işaretçinin hafızada işaret ettiği gerçek karakter verisini buffer olarak girdik. </p>
<p align="justify">Şimdi yazdırdığımız bu stream verilerini okuyalım:</p>
<pre>
<pre class="brush: delphi">
procedure TForm1.btnOkuClick(Sender: TObject);
var
  DosyaOku: TFileStream;
  Karakterler: PChar;
begin
  DosyaOku := TFileStream.Create(&#039;c:\deneme.txt&#039;, fmOpenRead or fmShareDenyNone);
  try
    Karakterler := AllocMem(22);
    DosyaOku.ReadBuffer(Karakterler^, 22);
    ShowMessage(Karakterler);
  finally
    DosyaOku.Free;
  end;
end;
</pre>
</pre>
<p align="justify">İlk yaptığımız işlem <font face="Courier New">Karakterler</font> değişkeni için hafızadan yer ayırmaktır. Bunun için <font face="Courier New">AllocMem</font> fonksiyonu kullanılır. Tabi bunun için okuyacağımız verinin <strong>ne kadar uzunlukta</strong> olduğunu bilmemiz gerekiyor. Eğer okuyacağımız karakter verilerinin uzunluğu belli değilse ya da duruma göre boyut <strong>değişebiliyorsa</strong> bu durumda daha başka bir şeyler yapmamız gerekecek. Bununla ilgili ayrıntılı bilgiyi ilerleyen konularda bulacağız. Ama şimdilik veri uzunluğumuzu <strong>sabit</strong> kabul ederek işlemlerimizi yapalım. </p>
<p align="justify"><font face="Courier New">AllocMem</font> ile hafızadan yer ayırdıktan sonra bu hafıza bölgesine stream verilerini yerleştirebiliriz. Bunun için yine <font face="Courier New">ReadBuffer </font>ile stream&#8217;den veri okuyoruz ve buffer&#8217;a atıyoruz. Yine buffer olarak işaretçinin gerçek değerini ifade eden <font face="Courier New">^</font> operatörünü kullanıyoruz. İkinci parametrede de ne kadar uzunlukta okuyacağımızı belirliyoruz.</p>
<p align="justify"><font face="Courier New">PChar</font> ile veri okuma ve yazma bu şekilde olmakta. Peki <font face="Courier New">string</font> bir değişkeni nasıl yazıp okuyacağız? Aslında <font face="Courier New">PChar</font>&#8216;da kullandığımız yöntemden farklı değil. &Ccedil;ünkü <font face="Courier New">string</font> ile <font face="Courier New">PChar </font>arasında çok büyük bir fark bulunmamakta. Zaten çalışma anında bu iki tip arasında dönüşümler rahatlıkla yapılabilmektedir. Yazma için yazdığımız kodları aşağıdaki gibi değiştirelim:</p>
<pre>
<pre class="brush: delphi">
procedure TForm1.btnYazClick(Sender: TObject);
var
  DosyaYaz: TFileStream;
  Karakterler: string;
begin
  DosyaYaz := TFileStream.Create(&#039;c:\deneme.txt&#039;, fmCreate or fmShareDenyNone);
  try
    Karakterler := &#039;http://www.diyezon.com&#039;;
    DosyaYaz.WriteBuffer(Pointer(Karakterler)^, Length(Karakterler));
  finally
    DosyaYaz.Free;
  end;
end;
</pre>
</pre>
<p align="justify">Önceki yazma işleminde çok farklı olmadığını burada görüyoruz. Tek yaptığımız işlem derleyiciye, <font face="Courier New">string</font> değişkene  bir <strong>işaretçi</strong> gibi davranmasını söylüyoruz. Bu şekilde derleyici, <font face="Courier New">string</font> olan <font face="Courier New">Karakterler</font> değişkenini bir işaretçi gibi düşünecek ve <font face="Courier New">^</font> operatörü ile esas hafıza bölgesindeki veriyi buffer olarak kullanacak. Burada <font face="Courier New">Pointer</font> yerine <font face="Courier New">PChar</font> yazarak da <em>type casting</em> yapabilirsiniz.</p>
<p align="justify">Okuma kodları <font face="Courier New">PChar</font>&#8216;a göre fazla ya da eksik değil. Önceki okuma fonksiyonumuzu aşağıdaki gibi değiştirelim.</p>
<pre>
<pre class="brush: delphi">
procedure TForm1.btnOkuClick(Sender: TObject);
var
  DosyaOku: TFileStream;
  Karakterler: string;
begin
  DosyaOku := TFileStream.Create(&#039;c:\deneme.txt&#039;, fmOpenRead or fmShareDenyNone);
  try
    SetLength(Karakterler, 22);
    DosyaOku.ReadBuffer(Pointer(Karakterler)^, 22);
    ShowMessage(Karakterler);
  finally
    DosyaOku.Free;
  end;
end;
</pre>
</pre>
<p align="justify"><font face="Courier New">AllocMem</font> yerine <font face="Courier New">string</font> için <font face="Courier New">SetLength</font> fonksiyonunu kullanıyoruz. Böylece string olan değişkenimizin boyutunu belirliyoruz. <font face="Courier New">ReadBuffer</font>&#8216;da yapılanlar önceden anlatılmıştı.</p>
<p align="justify">String için yaptığımız bu işlemler biraz pointer yani işaretçi bilgisi gerektiren işlerdi. Halbuki bu pointer işlerine girmeden de string verileri yazıp okuyabiliriz. Ama ilk önce bu şekilde vermemin sebebi, esasında işin arkaplanda çalışma şeklini göstermekti.  </p>
<p align="justify">Aslında diziler ve işaretçi olan PChar gibi karakter katarları hafızada bulunan esas verilerdeki <strong>ilk elemana</strong> işaret ederler.  Derleyici bu değişken tipleri ile uğraşırken bunların uzunluğundan haberdardır ve ilk pozisyondan itibaren hafızada bu değişkenler üzerinde işlem yapabilir. Esasında <font face="Courier New">PChar</font> ve <font face="Courier New">string</font>&#8216;de ortak olarak yapılan işlem, karakter dizisinin <strong>ilk elemanını</strong> buffer olarak girmektir. Her iki durumda da <font face="Courier New">^</font> operatörü dizinin hafızadaki <strong>ilk karakterine</strong> işaret eder. Geri kalanında <font face="Courier New">Write</font> ya da <font face="Courier New">Read</font> metodları verilen uzunluk kadar bu ilk karakterden itibaren okumaya ya da yazmaya başlar. Yukarıda <font face="Courier New">string</font> için yazdığımız kodları şu şekilde daha kolay ve sade bir şekilde de yazabiliriz:</p>
<pre>
<pre class="brush: delphi">
procedure TForm1.btnOkuClick(Sender: TObject);
var
  DosyaOku: TFileStream;
  Karakterler: string;
begin
  DosyaOku := TFileStream.Create(&#039;c:\deneme.txt&#039;, fmOpenRead or fmShareDenyNone);
  try
    SetLength(Karakterler, 22);
    DosyaOku.ReadBuffer(Karakterler[1], 22);
    ShowMessage(Karakterler);
  finally
    DosyaOku.Free;
  end;
end;

procedure TForm1.btnYazClick(Sender: TObject);
var
  DosyaYaz: TFileStream;
  Karakterler: string;
begin
  DosyaYaz := TFileStream.Create(&#039;c:\deneme.txt&#039;, fmCreate or fmShareDenyNone);
  try
    Karakterler := &#039;http://www.diyezon.com&#039;;
    DosyaYaz.WriteBuffer(Karakterler[1], Length(Karakterler));
  finally
    DosyaYaz.Free;
  end;
end;
</pre>
</pre>
<p align="justify">Gördüğünüz gibi pointer&#8217;lardan kurtulup, bize yakın olan kodları kullandık ve aynı sonucu aldık. Ama arka planda derleyici bizim ilk yazdığımız ve işaretçileri kullandığımız kısımdaki gibi çalışır.  </p>
<p align="justify">Burada tek yaptığımız işlem buffer olarak <font face="Courier New">string</font> değişkeninin <strong>ilk karakterini</strong> girmek. Tabi okuma işleminde yine <font face="Courier New">SetLength</font>&#8216;i unutmuyoruz. &Ccedil;ünkü okuma işleminden önce hafızada yeterince yer ayrılması şarttır. </p>
<p align="justify">Önceden de dediğim gibi uzunluğu sabit uzunlukta olmayan karakter dizileri için ayrı bir konu başlığı ayırdık. O zamana kadar karakter dizimizin boyutunu sabit kabul edeceğiz.</p>
<h2>Record&#8217;ların Stream Verileri Olarak Kullanılması</h2>
<p align="justify">Genelde stream ile yapılmak istenen bir gurup veriyi yazmak ve okumaktır. Bu gurup verileri ise genelde <strong><font face="Courier New">record</font></strong>&#8216;lar ile tutarız. Mesela aşağıda bulunan record içinde bir çok değişik değişken tipi bulunmaktadır.</p>
<pre>
<pre class="brush: delphi">
TDosyaBasligi = record
  Imza: string[3];
  Versiyon: Double;
  IcerikBoyutu: Integer;
end;
</pre>
</pre>
<p align="justify">Record&#8217;ları streamler ile beraber kullanırken şuna dikkat etmemiz gerekiyor. Bir <font face="Courier New">record</font> verisini stream ile okuyup yazabilmek için, record içeriğinin <strong>sabit uzunluklu</strong> verilerden oluşuyor olmasıdır. Aksi halde bu record, özel işlemler yapılmadığı taktirde, streamlerde kullanılmak için elverişli değildir. Bu yüzden yukarıda <font face="Courier New">string</font> değişkenini 3 karakter ile sınırlandırdık.  </p>
<p align="justify">Okuma esnasında da aynı record verisini kullanarak rahat bir şekilde okuma işlemini yapabilriiz. Peki ama bu bize ne avantaj sağlayacak?</p>
<p align="justify">Önceki örneklerde gördüğümüz gibi, herbir <font face="Courier New">string</font>, <font face="Courier New">Integer</font>, <font face="Courier New">Double</font> gibi değişkenler için ayrı ayrı <font face="Courier New">Read</font> ve <font face="Courier New">Write</font> metodları kullandık. Halbuki bu değişkenler eğer sabit bir formda ise bunları bir paket halinde okuyup yazabilmeliyiz. İşte bu paket halinde okuyup yazma işlemi için recordları kullanıyoruz. Yani yukarıda verdiğimiz <font face="Courier New">TDosyaBasligi</font> isimli recordu yazabilmek için tek tek içinde bulunan 3 değişkeni yazdırmamız gerekmiyor. Sadece <font face="Courier New">TDosyaBasligi</font> isimli recordu yazdırmamız kafidir.</p>
<p align="justify">Şimdi yukarıda verdiğmiz recordu <font face="Courier New"><strong>interface</strong></font> kısmında, <font face="Courier New">TForm</font> tanımlamasının yukarısında ama <strong><font face="Courier New">type</font></strong>&#8216;ın altında yazalım. Ve Yaz butonunun <font face="Courier New">OnClick</font> olayına şunları yazalım:</p>
<pre>
<pre class="brush: delphi">
procedure TForm1.btnYazClick(Sender: TObject);
var
  DosyaYaz: TFileStream;
  Paket: TDosyaBasligi;
begin
  DosyaYaz := TFileStream.Create(&#039;c:\deneme.txt&#039;, fmCreate or fmShareDenyNone);
  try
    Paket.Imza := &#039;DYZ&#039;;
    Paket.Versiyon := 1.2;
    Paket.IcerikBoyutu := 1500;
    DosyaYaz.WriteBuffer(Paket, SizeOf(TDosyaBasligi));
  finally
    DosyaYaz.Free;
  end;
end;
</pre>
</pre>
<p align="justify">Gördüğünüz gibi herhangi bir ekstra işlem yapmadım. Sanki basit bir sayısal veriyi yazdırır gibi recordumuzu yazdırdık. Aynı şekilde okumasını da gerçekleştirelim:</p>
<pre>
<pre class="brush: delphi">
procedure TForm1.btnOkuClick(Sender: TObject);
var
  DosyaOku: TFileStream;
  Paket: TDosyaBasligi;
begin
  DosyaOku := TFileStream.Create(&#039;c:\deneme.txt&#039;, fmOpenRead or fmShareDenyNone);
  try
    DosyaOku.ReadBuffer(Paket, SizeOf(TDosyaBasligi));
    ShowMessage(Paket.Imza + #13 + FloatToStr(Paket.Versiyon) + #13 +
      IntToStr(Paket.IcerikBoyutu));
  finally
    DosyaOku.Free;
  end;
end;
</pre>
</pre>
<p align="justify">Bu programı çalıştıralım ve ilk önce Yaz sonra da Oku buttonlarına tıklayalım. Böylece bir recordu yazıp okuyabildik. Fakat disk üzerine yazılan dosyayı bir hex editör yardımı ile incelersek göreceğiz ki ilk kısımda bulunan 3 karakterlik stringten <strong>önce 1 byte&#8217;lık</strong> bir veri bulunmaktadır. Önceki string yazma işlemlerinden farklı olarak burada record içindeki stringlerin sadece verisi yazdırılmaz. <font face="Courier New">String</font> değişkeninin <strong>tamamı</strong> record ile beraber yazdırılır. <font face="Courier New">String</font> değişkenin tamamından kastımız stringi oluşturan parçalardır. Bir <font face="Courier New">string</font>, <font face="Courier New">PChar</font> gibi karakter dizilerinden farklı olarak ilk <font face="Courier New">0</font>. baytında stringin veri uzunluğunu tutar. Bu örneğimizde 3 karakter uzunluğunda olduğu için hex editörde ilk göreceğimiz değer <font face="Courier New">03</font> değeridir. Tabi bu tek bytelık alanda en fazla 255 değeri yani <font face="Courier New">FF</font> değerini tutlabilir. Bu yüzden sınırlı bir <font face="Courier New">string</font>(<em>literal string</em>) kullanabilmek için en fazla 255 karakter kullanabiliriz. Aksi halde daha başka yollara başvurmalıyız. Başta bulunan <font face="Courier New">03</font> ifadesini kaldırıp sadece string verisini yazdırmak istersek  <strong><font face="Courier New">array</font></strong> kullanmamız gerekmektedir. <font face="Courier New">Array</font> kullanımını bir sonraki konuda göreceğiz. </p>
<p align="justify">Sabit uzunluklu recordları kaydederken gördüğünüz gibi hiç zorlanmıyoruz. Eğer record içeriği burada olduğu gibi sabit uzunluklu değilse yani record içine <font face="Courier New">PChar</font> ya da limitsiz <font face="Courier New">string</font> girersek ne yapacağız. Bu durumda eğer yapabiliyorsak değişken uzunluklu olanları recorddan <font face="Courier New">çıkartıp</font> işlemlerimizi o şekilde yapmalıyız. Değişken uzunluklu verileri recorddan <strong>ayrı olarak</strong> okuyup yazmamız gerekmektedir. İleriki konularımızda değişken uzunluklu veriler ile nasıl çalışacağımızı da göreceğiz.</p>
<h2>Dizileri Stream Verisi Olarak Kullanmak</h2>
<p align="justify">Aslında dizileri stream üzerine yazmak için, buraya kadar öğrendiğimiz bilgiler yeterli. String&#8217;leri nasıl yazdırır ve okutuyorsak, aynı şekilde array&#8217;leri de yazdırıp okutabiliyoruz.  </p>
<pre>
<pre class="brush: delphi">
//Yazdırmak için
var
  FS: TFileStream;
  BirDizi: array[0..5] of Integer;
begin
  FS := TFileStream.Create(&#039;c:\deneme.bin&#039;, fmCreate or fmShareDenyNone);
  try
    BirDizi[0] := 153;
    BirDizi[1] := 1;
    BirDizi[2] := 3442;
    BirDizi[3] := 902;
    BirDizi[4] := 455;
    FS.WriteBuffer(BirDizi[0], SizeOf(BirDizi));
  finally
    FS.Free;
  end;
end;

//Okutmak için
var
  FS: TFileStream;
  BirDizi: array[0..5] of Integer;
begin
  FS := TFileStream.Create(&#039;c:\deneme.bin&#039;, fmOpenRead or fmShareDenyNone);
  try
    FS.ReadBuffer(BirDizi[0], SizeOf(BirDizi));
    ShowMessage(IntToStr(BirDizi[0]) + #13 +
      IntToStr(BirDizi[1]) + #13 +
      IntToStr(BirDizi[2]) + #13 +
      IntToStr(BirDizi[3]) + #13 +
      IntToStr(BirDizi[4]) + #13);
  finally
    FS.Free;
  end;
end;
</pre>
</pre>
<p align="justify">Gördüğünüz gibi <strong>ilk elemanını</strong> buffer olarak giriyoruz ve ikinci parametre olarak da <strong>dizinin boyutunu</strong> parametre olarak giriyoruz.</p>
<p align="justify">Eğer <strong>dinamik</strong> array kullanıyorsak okuma işlemini gerçekleştiren <font face="Courier New">ReadBuffer</font> metodundan önce mutlaka <font face="Courier New">SetLength</font> ile dizinin boyutunu belirlemiş olmalıyız. Ayrıca dizinin kapladığı alanı bulabilmek için dinamik dizinin eleman sayısı ile elaman tipinin boyutunu çarpmalıyız. Yani yukarıdaki örnekte dinamik dizi kullanmış olsa idik <font face="Courier New">Length(BirDizi) * SizeOf(Integer)</font> kazacaktık. </p>
<p align="justify">İki ve daha fazla boyuttaki diziler için ise, iç içe bir <strong><font face="Courier New">for</font></strong> döngüsü kurularak bir okuma ve yazma algoritması geliştirilebilir.</p>
<p align="justify">Bu kısım diziler için kolay kısımdı. &Ccedil;ünkü bunlar önceki öğrendiklermizden farksızdır. Fakat eğer ActiveX ile uğraşıyorsanız, Word, Excel gibi programların otomosyonu ile uğraşıyorsanız <strong>variant array</strong>&#8216;ler ile uğraşmışsınızdır. İşte bu durumda variant olan array&#8217;leri stream&#8217;e kaydetmek ve stream&#8217;den okutmak bu kadar kolay değildir. Ama zor da değildir. Eğer ActiveX ile uğraşmıyorsanız diğer başlığa atlayabilirsiniz, ya da nasıl yapıldığı hakkında fikir sahibi olmak için kalıp okuyabilirsiniz. </p>
<p align="justify">Bu örneğimizde otomosyon ya da ActiveX nesnelerine girmeyeceğim. Bir şekilde bu nesnelerden variant array verilerini aldığınızı düşünerek haraket edeceğim.</p>
<pre>
<pre class="brush: delphi">
var
  VariantArray: Variant;
  VArraySize: Integer;
  PArray: Pointer;
  FS: TFileStream;
begin
  VariantArray := ActiveXNesnesi.ArrayDonderenMetod; //Bu kısım size ait...
  VArraySize := VarArrayHighBound(VariantArray, 1) - VarArrayLowBound(VariantArray, 1) + 1;
  PArray := VarArrayLock(VaraintArray);
  FS := TFileStream.Create;
  try
    FS.WriteBuffer(PArray^, VArraySize);
  finally
    FS.Free;
  free;
  VarArrayUnlock(VaraintArray);
</pre>
</pre>
<p align="justify">Karışık gibi görünse de eğer ActiveX ile uğraşıyorsanız bu kısım size zor gelmeyecektir. İlk başta bir nesneden variant array değerini aldık. Bu değeri siz delphi içinde de oluşturabilirsiniz. Ardından bu array&#8217;in boyutunu hesapladık ve <font face="Courier New">VArraySize</font> isimli değişkene attık. Burada dikkat etmeniz gereken nokta, bu örneğimizde diziyi byte, char gibi her elemanı 1 byte olan bir diziye göre hesapladık. Eğer array&#8217;deki elemanların boyutu 1 byte&#8217;dan fazla ise bu durumda:</p>
<pre>
<pre class="brush: delphi">
VArraySize := (VarArrayHighBound(VariantArray, 1) -
  VarArrayLowBound(VariantArray, 1) + 1) * TVarData(VariantArray).VArray^.ElementSize;
</pre>
</pre>
<p align="justify">gibi bir şeyler yazmamız gerekiyordu. Yani eleman sayısı ile bir elemanın boyutunu çarpıyoruz. Daha sonraki gelen işlemlerde, variant array&#8217;in <strong>ilk elemanının</strong> işaretçisini alıyoruz. Bunun için <font face="Courier New">VarArrayLock</font> fonksiyonundan faydalanıyoruz. Bu işaretçiyi daha sonra buffer olarak <font face="Courier New">WriteBuffer</font>&#8216;da kullanıyoruz. Bu işaretçi ile işimiz bitince <font face="Courier New">VarArrayUnlock</font> yapmayı unutmuyoruz. </p>
<p align="justify">Variant dizisini stream&#8217;den okurken de <font face="Courier New">VarArrayCreate</font> fonksiyonunu kullanıyoruz ve yeni bir dizi oluşturuyoruz. Ardından aynı <font face="Courier New">VarArrayLock</font> ve <font face="Courier New">VarArrayUnlock</font> işlemleri arasında dizimizi stream&#8217;den okuyoruz. Bu kısmı size bırakıyorum&#8230; </p>
<h2>Değişken Uzunluktaki Verileri Okuyup Yazmak</h2>
<p align="justify">Buraya kadar öğrendiğimiz işlemlerde hep varsayılan bir uzunluktan bahsettik. Fakat her zaman durum bu şekilde olmuyor.</p>
<p align="justify">Mesela, kullanıcının bir <font face="Courier New">TMemo</font> bileşenini doldurduğu bir form var. Biz bu <font face="Courier New">TMemo</font>&#8216;da bulunan yazıyı ve başka verileri kendi dosya formatımızda saklamak istiyoruz. Ya da veritabanına kendi formatımızda kaydetmek istiyoruz. Bu durumda string&#8217;in ne uzunlukta olduğunu <strong>yazarken</strong> bilebiliriz fakat <strong>okurken bilemeyiz</strong>. &Ccedil;ünkü dosya&#8217;da ya da herhangi bir stream&#8217;de kayıtlı bulunan o string&#8217;e ait <strong>uzunluk</strong>, artık bizim bilgimizin <strong>dışına</strong> çıkmıştır. Buraya kadar yaptığımız örneklerde hep yazma ve okuma işlemlerini aynı programa koyduk ve aynı çalışma zamanında işlettirdik. Bu yüzden okuduğumuz verilerin boyutunu her zaman biliyorduk. Halbuki okuma fonksiyonu ayrı bir işlemde ise ya da programı açıp kapadığımızda da okuma işleminin çalışmasını istiyorsak bu durumda kaydedeceğimiz veriyi özel bir biçimde kaydetmeliyiz.</p>
<p align="justify">Bu ve bunun gibi durumlarda yapacağımız işlem çok basit ve her türlü <strong>değişken uzunluklu</strong> veri tipi için geçerlidir. Yapacağımız tek şey değişken uzunluklu olan verinin boyutunu <strong>sabit</strong> bir pozisyona yazmaktır. Ardından okuma esnasında bu sabit pozisyondaki değeri <strong>okutarak</strong> değişken olan verinin boyutu hakkında bilgi sahibi olabiliriz.</p>
<p>Mesela stream formatımız şu şekilde olsun:</p>
</p>
<table height="94" width="368" cellspacing="1" cellpadding="1" border="1" align="" summary="">
<tbody>
<tr>
<td>
<p><strong>Pozisyon</strong></p>
</td>
<td>
<p><strong>Boyut</strong></p>
</td>
<td>
<p><strong>Değişken Tipi</strong></p>
</td>
<td>
<p><strong>Açıklama</strong></p>
</td>
</tr>
<tr>
<td>0</td>
<td>3 byte</td>
<td>String[3]</td>
<td>Dosya İmzası</td>
</tr>
<tr>
<td>3</td>
<td>8 byte</td>
<td>Double</td>
<td>Versiyonu</td>
</tr>
<tr>
<td>15</td>
<td>4 byte</td>
<td>Integer</td>
<td>İçerik Boyutu</td>
</tr>
<tr>
<td>19</td>
<td>Değişken</td>
<td>String</td>
<td>İçerik</td>
</tr>
<tr>
<td>Bilinmeyen</td>
<td>4 byte</td>
<td>Integer</td>
<td>Rastgele</td>
</tr>
</tbody>
</table>
<p align="justify">Tabi bu çok basit bir format şekli. Normalde bir çok sabit içerikle beraber bir çok değişken içeriği iç içe kullanmamız gerekebiliyor.</p>
<p align="justify">Formatımıza göre ilk 3 byte&#8217;da dosya imzası bulunacak. Ardından gelen 8 byte dosyanın versiyonunu tutacak. Ve sonrasında gelen 4 byte&#8217;lık Integer değer ise bizim üzerinde uğraştığımız esas konudur. &Ccedil;ünkü bu Integer değer sonrasında gelen string içeriğin boyutunu tutmaktadır. Böylece okuma esnasında string verimizi ne kadar okuyacağımızı belirleyebiliyoruz.  </p>
<p align="justify">En sonda ise 4 byte&#8217;lık bir <font face="Courier New">Integer</font> daha ekledim. Bu değer aslında bu örneğimizi için gerekli değil. Fakat burada bir noktaya dikkatinizi çekmek istediğimden bu değeri de dosya formatına ekledim.  </p>
<p align="justify">Değişken uzunlukta olan <font face="Courier New">string</font>, <font face="Courier New">array</font> ya da herhangi bir değişkeni kaydederken normalde ilk akla gelen stream&#8217;in sonuna kadar okumak olacaktır. Ya da stream&#8217;in uzunluğundan diğer verilerin boytunu çıkarmak olacaktır. Halbuki işin içine iki veya daha fazla değişken uzunlukta veri girdiğinde ve buradaki örnekte olduğu gibi değişken uzunluktan sonra da bir başka veri geldiğinde bu mantık bizi idare etmeyecektir. Aynı şekilde dosya formatımızı sürekli olarak güncellenebilen bir format ise bu mantık yine çökecektir.</p>
<p align="justify">Bu durumda en mantıklı işlem, değişken verilerin <strong>başında</strong> bu verinin uzunluğunu <strong>saklamak</strong> olacaktır. Böylece sonrasında gelen değişken uzunluktaki veri rahatlıkla okunabilir.</p>
<p align="justify">Şimdi bu anlattıklarımızı pratiğe dökelim ve yukarıdaki stream formatımızı yine bir dosya üzerinde yazıp okuyalım. Bunun için yeni bir uygulama açın ve form üzerine bir adet button ve bir adet memo yerleştirin.  </p>
<p align="justify">Unit&#8217;in <strong><font face="Courier New">interface</font></strong> kısmında <font face="Courier New">TForm1</font> tanımlamasının hemen üstünde(type bloğu içinde) şunları yazalım:</p>
<pre>
<pre class="brush: delphi">
type
  TDosyaBasligi = record
    Imza: array[0..2] of Char;
    Versiyon: Double;
    IcerikBoyutu: Integer;
  end;
</pre>
</pre>
<p align="justify">Buttonnun <font face="Courier New">OnClick</font> olayına aşağıdakileri yazalım.  </p>
<pre>
<pre class="brush: delphi">
var
  FS: TFileStream;
  Baslik: TDosyaBasligi;
  Icerik: string;
  Rastgele: Integer;
begin
  FS := TFileStream.Create(&#039;c:\deneme.dyz&#039;, fmCreate or fmShareDenyNone);
  try
    Icerik := Memo1.Lines.Text;
    //Başlık bilgilerinin yazımı
    Baslik.Imza := &#039;DYZ&#039;;
    Baslik.Versiyon := 1.2;
    Baslik.IcerikBoyutu := Length(Icerik);
    FS.WriteBuffer(Baslik, SizeOf(TDosyaBasligi));

    //Değişken boyutlu içeriğin yazımı
    FS.WriteBuffer(Icerik[1], Length(Icerik)); //Unicode olduğunda Length * 2

    Randomize;
    Rastgele := Random(100);
    FS.WriteBuffer(Rastgele, SizeOf(Integer));
  finally
    FS.Free;
  end;
</pre>
</pre>
</p>
<p align="justify">Burada aslında yeni bir şeyler görmedik. Sadece öğrendiklerimizi bir problemin çözümünde kullandık.  </p>
<p align="justify"><font face="Courier New">Baslik</font> yazdırılmadan önce, değişken uzunluktaki içeriğimiz olan memo&#8217;nun <font face="Courier New">Text</font> bilgisinin uzunluğunu, <font face="Courier New">Baslik</font> record verisi içine alıyoruz. Ardından bu recordu stream&#8217;e yazdıktan sonra, değişken içeriğin uzunluğu da yazılmış oldu. Ardından normal bir stringi yazdırır gibi bütün karakterlerini stream üzerine yazdırdık. Ardından da rastgele bir sayıyı bu değişken uzunluktaki veriden sonra yazdırdık. Artık programı çalıştırıp Memo içine bir şeyler yazalım ve button&#8217;a basıp verileri stream üzerine kaydedilim. </p>
<p align="justify">Şimdi gelelim bu stream formatının okunmasına&#8230; Bunun için ayrı yeni bir uygulama açalım ve bir button ekleyelim. Ve bu buttonun <font face="Courier New">OnClick</font> olayına geçip şunları yazalım:</p>
<pre>
<pre class="brush: delphi">
var
  FS: TFileStream;
  Baslik: TDosyaBasligi;
  Icerik: string;
  Rastgele: Integer;
begin
  FS := TFileStream.Create(&#039;c:\deneme.dyz&#039;, fmOpenRead or fmShareDenyNone);
  try
    //Başlık bilgilerinin okunması
    FS.ReadBuffer(Baslik, SizeOf(TDosyaBasligi));

    if Baslik.Imza &amp;lt;&amp;gt; &#039;DYZ&#039; then
    begin
      ShowMessage(&#039;Desteklenmeyen format&#039;);
      Exit;
    end;

    if Baslik.Versiyon &amp;gt; 1.2 then
    begin
      ShowMessage(&#039;Bu format ancak üst versiyonlarda açılabilir.&#039;);
      Exit;
    end;

    //Değişken boyutlu içeriğin okunması
    SetLength(Icerik, Baslik.IcerikBoyutu);
    FS.ReadBuffer(Icerik[1], Baslik.IcerikBoyutu);
    FS.ReadBuffer(Rastgele, SizeOf(Integer));

    ShowMessage(Icerik + #13 + IntToStr(Rastgele));
  finally
    FS.Free;
  end;
end;
</pre>
</pre>
<p align="justify">Burada yaptığımız işlem sadece verilerin okunmasından ibaret. Sadece dikkat etmemiz gereken kısım, <font face="Courier New">ReadBuffer</font>&#8216; dan önce <font face="Courier New">SetLength</font> ile değişken uzunlukta olan verinin uzunluğunu belirlememiz gerekiyor. </p>
<p align="justify">Değişken uzunluktaki stringler bu şekilde okutulabilir.  </p>
<p align="justify">Bir küçük tüyo olarak, verileri yazdırdığımız kısımdaki şu kodlara bir daha göz atmanızı isteyeceğim:</p>
<pre>
<pre class="brush: delphi">
  Icerik := Memo1.Lines.Text;
  ......
  ......
  Baslik.IcerikBoyutu := Length(Icerik);
</pre>
</pre>
<p align="justify">Bu kodlar <font face="Courier New">Baslik</font> recordunun yazılmasından evvel doldurulmaktadır. Bazen öyle durumlar oluyorki, veri uzunluğunu verinin stream&#8217;e <strong>tamamen yazdırılmasına</strong> kadar <strong>bilemiyoruz</strong>. Mesela değişken uzunlukta bu stringimiz, stream&#8217;e yazdırılma esnasında belli bir algoritma ile <strong>dinamik</strong> olarak oluşturuluyor olabilir. Bu durumda string verisi tamamen stream üzerine yazdırılmadan, uzunluğunu bilemeyiz. Bu durumda <font face="Courier New">TStream</font>&#8216;e ait <font face="Courier New">Seek</font> metodunu kullanacağız. Bu metod stream üzerinde belli bir <strong>pozisyona atlamamızı</strong> sağlamaktadır. Bu durumda kodumuz şöyle bir şey olacaktı:</p>
<pre>
<pre class="brush: delphi">
  //Şimdilik yer ayırmak için 0 yazdırıyoruz.
  Baslik.IcerikBoyutu := 0;
  FS.WriteBuffer(Baslik, SizeOf(TDosyaBasligi));

  .....
  .....
  //Stream üzerine verilerin yazdırılması ve boyutunun hesaplanması...
  //Burada değişken uzunluktaki veri yazdırılmakla birlikte boyutu da hesaplanıyor.
  .....
  .....

  FS.Seek(15, soFromBeginning);
  FS.WriteBuffer(IcerikBoyutu, SizeOf(Integer));
</pre>
</pre>
<p align="justify">Aynı şeyi <font face="Courier New">Baslik</font> recordunu en sonda yazdırmakla da yapabilrdik. Veya daha başka yollar ile de bu dediklerimiz yapılabilir. Burada bilmemiz gereken <font face="Courier New">Seek</font> metodunun kullanımıdır. <font face="Courier New">Seek</font> metodunda ilk parametre olarak <font face="Courier New">15</font> girdik. &Ccedil;ünkü örneğimizdeki stream format tablomuza baktığımızda 15. byte pozisyonunda içerik boyutu bulunmaktadır. <font face="Courier New">Seek</font> ile stream&#8217;in <strong>yazma işaretçisini</strong> buraya taşıdık ve bu pozisyondan sonra 4 byte&#8217;lık bir <font face="Courier New">Integer</font> değer yazdırdık. <font face="Courier New">Seek</font> metodunda kullanılan ikinci parametre için help dosyalarını gezebilirsiniz.</p>
<p align="justify">Değişken uzunlukta olan diziler için, yani dinamik diziler için de aynı mantığı kullanmamız gerekmektedir. Değişken olan dizinin boyutunu da stream üzerine yazdırmamız ve okuma anında bu değeri okuyup diziyi <font face="Courier New">SetLength</font> ile tekrar oluşturmamız gerekmektedir. Yapacağımız işlemler aynı olacağı için bu kısmı size bırakıyorum. </p>
<p align="justify">Bu bölümde bu kadar bilgi yeterli. Gelecek bölümde stream verilerinin başka streamler üzerine aktarılması ve kopyalanması, streamler ile nesne ve bileşen kullanımı, hafıza, resource, veritabanı gibi ortamlarla stream kullanımı ve son olarak .NET ortamında stream kullanımından bahsedeceğiz. İkinci bölüme geçmeden burada anlatılanları iyi bir şekilde kavrayıp, kendi denemelerinizi yapmanız gerekmektedir. </p>
<p align="justify">Eleştiri ve yorumlarınızı bekliyorum&#8230;</p>
<p align="justify">Fatih Tolga Ata &#8211; 2008</p>
</p>
</p></p>
]]></content:encoded>
			<wfw:commentRss>http://www.diyezon.com/2008/09/13/streamde-uzmanlasalim-bolum-1/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Delphi ile Thread(Kanal) Kullanımı &#8211; Bölüm 2</title>
		<link>http://www.diyezon.com/2007/10/06/delphi-ile-threadkanal-kullanimi-bolum-2/</link>
		<comments>http://www.diyezon.com/2007/10/06/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 oldu. [...]]]></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/2007/10/06/delphi-ile-threadkanal-kullanimi-bolum-2/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>Delphi ile Thread(Kanal) Kullanımı &#8211; Bölüm 1</title>
		<link>http://www.diyezon.com/2007/10/02/delphi-ile-threadkanal-kullanimi-bolum-1/</link>
		<comments>http://www.diyezon.com/2007/10/02/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ş [...]]]></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/2007/10/02/delphi-ile-threadkanal-kullanimi-bolum-1/feed/</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
		<item>
		<title>Enine Boyuna Generics &#8211; Bölüm 2</title>
		<link>http://www.diyezon.com/2007/09/23/enine-boyuna-generics-bolum-2/</link>
		<comments>http://www.diyezon.com/2007/09/23/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)
Öyle olurki bazen, [...]]]></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&amp;lt;T: ICizebilen&amp;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&amp;lt;T: ISerializable; U: IColoneable&amp;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&amp;lt;T: ISerializable, IColoneable&amp;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&amp;lt;T, U: ISerializable&amp;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&amp;lt;T, U: ISerializable; Z&amp;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&amp;lt;T: IPrintable&amp;gt; = class ...
...
TDeneme = class(IPrintable)
  procedure Print;
end;

THata = class
end;
...
var
  Calisan: TPrintableCollect&amp;lt;TDeneme&amp;gt;;
  Hatali: TPrintableCollect&amp;lt;THata&amp;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&amp;lt;T: TBirSinif&amp;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&amp;lt;T: ISerializable, ICloneable&amp;gt; = class
  BirNesne: T;
  procedure Klonla;
end;

procedure TTest&amp;lt;T&amp;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&amp;lt;T&amp;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&amp;lt;T: record&amp;gt; = class...
TReferansli&amp;lt;T: class&amp;gt; = class...
TBirRecord = record
end;

var
  Hatali1: TReferansiz&amp;lt;string&amp;gt;; //Hatalı, çünkü string nullable,
  Hatali2: TReferansli&amp;lt;Integer&amp;gt;; //Hatalı, Integer referanslı bir tip değil
  Uygun1: TReferansiz&amp;lt;TBirRecord&amp;gt;;
  Uygun2: TReferansiz&amp;lt;Integer&amp;gt;;
  Uygun3: TReferansli&amp;lt;string&amp;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&amp;lt;T: constructor&amp;gt; = class...
...

var
  Uygun: TBirKolleksiyon&amp;lt;TBirSinif&amp;gt;;
  Hatali: TBirKolleksiyon&amp;lt;TBirBaskaSinif&amp;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/2007/09/23/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/2007/09/18/enine-boyuna-generics-bolum-1/</link>
		<comments>http://www.diyezon.com/2007/09/18/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&amp;lt;eleman&amp;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&amp;lt;eleman&amp;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&amp;lt;Integer&amp;gt;;
  YaziKuyruk: Queue&amp;lt;string&amp;gt;;
  Sayi: Integer;
  Yazi: string;
begin
  SayiKuyruk := Queue&amp;lt;Integer&amp;gt;.Create;
  YaziKuyruk := Queue&amp;lt;string&amp;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&amp;lt;T, K, L&amp;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&amp;lt;T&amp;gt; = procedure(Param1, Param2: T) of object;
  TNormalBirSinif = class
    procedure BirProsedur&amp;lt;X&amp;gt;(Param1, Param2: X);
  end;

procedure TNormalBirSinif.BirProsedur&amp;lt;X&amp;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&amp;lt;T&amp;gt; = class
    ...
    procedure BirProc&amp;lt;X&amp;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&amp;lt;T&amp;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&amp;lt;T&amp;gt; = class
  public
    procedure BirSeylerYap&amp;lt;X&amp;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&amp;lt;Integer&amp;gt;;
begin
  BirNesne := TBirSinif&amp;lt;Integer&amp;gt;.Create;
  BirNesne.BirSeylerYap&amp;lt;string&amp;gt;(&#039;Deneme bir string&#039;);
  BirNesne.BirSeylerYap&amp;lt;Integer&amp;gt;(123);
  BirNesne.BirSeylerYap&amp;lt;Boolean&amp;gt;(True);
  BirNesne.BirSeylerYap&amp;lt;TForm&amp;gt;(Form1);
  BirNesne.BirSeylerYap&amp;lt;TButton&amp;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/2007/09/18/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/2007/08/14/delphi-7-ile-simdiki-delphi-surumleri-arasindaki-farklar/</link>
		<comments>http://www.diyezon.com/2007/08/14/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[strict private]]></category>
		<category><![CDATA[strict protected]]></category>
		<category><![CDATA[sınıf]]></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 kısımlar [...]]]></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/2007/08/14/delphi-7-ile-simdiki-delphi-surumleri-arasindaki-farklar/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>
