24 Şubat 2015 Salı

C++ Streamleri

Stream Sınıfları Hiyerarşisi
Yazıya başlamadan önce STL ile gelen stream sınıflarının hiyerarşisini bilmek gerekir.

Stream nesneleri de kendi içinde zengin bir hiyerarşiye sahiptir. Herb Sutter'ın More Exceptional C++ kitabından bu zengin hiyerarşi yüzünden iosfwd header dosyası ile daha basit forward declaration yapmanın önemi anlatılıyor.
Never #include a header when a forward declaration will suffice. Prefer to #include only <iosfwd> when the complete definition of a stream is not needed.

Örneğin aşağıdaki şekli buradan aldım ve stream nesneleri içindeki hiyerarşiyi kolayca görebiliyoruz. Ancak bizim için esas önemli olan her stream ile bir buffer nesnesinin ilişki olması mevzusu.




Bu hiyerarşi bizim C++ kodlarında kullandığımız sınıflar isimlerini içermiyor çünkü birçok sınıf aslında typedef olarak tanımlı. Örnek :

typedef basic_istringstream<char> istringstream;

Stream Sınıflarının Temel Özellikleri

Stream Sınıflarının ve Copy Constructor Metodu Çağırılamaz
Stream sınıflarının copy constructor metodları private tanımlıdır. Stream'leri metodlar referans veya pointer olarak geçebiliriz. Ya da std::shared_ptr ile kullanabiliriz.

Stream Sınıfları ve Assignment Operator Metodu Çağırılamaz
Yukarıdaki açıklamadaki gibi stream metodlarının assignment operator'leri dışarıya açık değildir.

Stream Sınıfları ve Associativity (Birleşme)
Okuma ve yazma işlemlerinde her zaman stream sınıfı ilk önce kullanılır. Eğer
int a, b ;
a << b << cin
şeklinde olsaydı associativity yüzünden derleyici bu ifadeyi
(a << b) << cin
gibi bit kaydırma şeklinde algılardı.

inputstream sınıfları
okuma işlemi bool döner
Örneğin bir akımdan okumak için aşağıdaki gibi yapılır.
while (in >> value) {
}
okuma işleminde hata olursa
okumada hata olursa stream'i temizlemek gerekir. Örnekte int'ten daha büyük bir değer girilirse stream hatalı durumda kalıyor. Tekrar okuma işlemi denense bile çalışmaz. clear () metodu ile failbit bayrağının temizlenmesi gerekir.
int num = 0;
cout << "Enter a number: << endl;
cin >> num;
Temizleme örneği
cin.clear();
cin >> num;

fstream sınıfları
ifstream ve is_open
ifstream ile text dosyanın tüm satırlarını okumak için örnek
ifstream myfile ("filename.txt");
if (myfile.is_open()) {
while ( getline (myfile,line) ) {
    a_file << line << '\n';
}

ofstream
ostream yazısına taşıdım.

sstream sınıfları

stringstream
stringstream'i temizleme için str("") metodu çağırılır. clear() metodu ise strema içindeki flag ve kapasiteyi temizler.
stringstream ss;
ss.str("");
ss.clear();

Tüm Sınıfları İçin StreamBuf

basic_ios ve basic_streambuf (stream buffer nesnesi)

basic_ios sınıfı input ve output stream sınıflarının türediği sınıf olup herkesin kullandığı tüm ortak özellikleri içerir. İçerilen ortak özelliklerin başında ise stream'den okuma yazma işlemleri için soyut bir tampon sınıfı olarak kullanılan basic_streambuf gelir. Dolayısıyla STL ile gelen IOStream kütüphanesinde kullanılan tüm streamlerin içinde bir buffer nesnesi bulunur.

Bu durumu gösteren örnek bir şekli buradan aldım.




Bu buffer nesnesi dolunca nereye boşaltması gerektiğini (sink) veya boşalınca nereden okuması gerektiğini bilir.

Aslında buffer nesnesi ile stream'in birbirinden ayrılması bir strateji tasarım şablonudur.

basic_streambuf yerine sadece streambuf

C++ kodunda yukarıdaki şekilde görülen basic_XXX isimli sınıflar yerine std::streambuf, std::istream gibi sınıflar karşımıza çıkar. Bunun sebebi bir çok sınıfın aslında basic_XXX isimli sınıflar için tanımlanmış typedef'ler olmasıdır. Bu konu ile ilgili bir örnek vermek gerekirse :

std::streambuf aslında std::basic_streambuf sınıfı için tanımlamış bir template ismidir. Yani aşağıdaki gibidir:
typedef basic_streambuf< char > std::streambuf
 Dolayısıyla STL'de kullanılan buffer hiyerarşisi aşağıdaki gibi gösterilirse okuması daha kolay oluyor.






Görüldüğü gibi streambuf olarak kullanılabilecek iki tane nesne bulunmakta. Bunlardan ilki olan filebuf dosyaları okuma/yazma işlevini yerine getirirken, stringbuf ise stringleri okuyup yazma işlevini yerine getirir.

Buffer nesnesi hem input hem de output için kullanılabildiğinden her iki işlev için de metotlar sağlamaktadır.

Aşağıdaki output buffer şeklini buradan aldım ve buffer'ın başlangıç, bitiş ve en son yazılabilecek pozisyonunu veren metotları gösteriyor. Yani buffer'a soldan sağa doğru yazıyoruz gibi düşünülebilir.

Yine aynı şekilde input için kullanılan buffer'ın başlangıç, bitiş ve en son okunabilecek pozisyonunu veren metotları gösteren şekil ise aşağıda. Buffer'ın bu sefer solda sağa doğru okunduğu düşünülebilir.


basic_streambuf'tan türeyen sınıflar
Bu sınıftan türetilen bazı örnek sınıfları görmek için Boost I/O kütüphanesi başlıklı yazıya göz atabilirsiniz.

basic_streambuf'tan kendimizin türetmesi
Buradaki soruda ise streambuf sınıfından bir sınıf türetilerek elimizde sadece file descriptor'ı olan bir stream'e yazma örneği gösterilmiş.overflow ve xsputn sınıfları override edilerek işlem yapılıyor. Kendi türettiğimiz nesneyi stream nesnesine, stream'in constructor'ını kullanarak geçiyoruz.




Hiç yorum yok:

Yorum Gönder