19 Ocak 2016 Salı

Static Yerel (Local) Değişken

static local değişken metod ilk kez çağırıldığında ilklendirilir - non-trivial initialization
C++'ta bir nesnenin storage duration ve lifetime süreleri farklı şeylerdir. static local değişken için storage alanı uygulama ilk defa başlatılırken ayrılır.

Ancak ilklendirme yani lifetime'ın başlaması değişebilir. Eğer değişken trivial bir tipse tipse, hem storage hem lifetime uygulama açılırken başlatılabilir. Şöyle bir metodumuz olsun
{static int i = 0;}
i değişkenine 0 değeri en başta atanabilir. Metodun ilk kez çağrılmasını beklemeye gerek yoktur.

Eğer static local değişken constructor'a  sahip bir nesne ise, ilklendirme işlemi metod ilk kez çağrıldığında yapılır.Şöyle bir metodumuz olsun
void func1()
{
  static X i;
  ...
}
Bu metod ilk kez çağrıldığı zaman, X nesnesinin constructor metodu çağrılır.

static local değişken guard ile korunur
X nesnesi multi-threaded kod tarfından kullanılıyorsa, yaratılmasında problem olabilir. Bu yüzden derleyici bizim için X değişkeninden önce guard koyar.
void func1()
{
  static X i;
  static guard x_is_initialized;
    if ( __cxa_guard_acquire(x_is_initialized) ) {
        X::X();
        x_is_initialized = true;
        __cxa_guard_release(x_is_initialized);
    }
  ...
}
Gömülü bir platformda bir kez
Undefined reference to __cxa_guard_acquire
Undefined reference to __cxa_guard_release
hatasını bu yüzden almıştım.

static local değişken'in özyinelemeli kod kullanılması
Secure Coding için şöyle bir paragrah var.
Do not reenter a function during the initialization of a static variable declaration. If a function is reentered during the constant initialization of a static object inside that function, the behavior of the program is undefined. Infinite recursion is not required to trigger undefined behavior, the function need only recur once as part of the initialization.
Bu şu anlama geliyor. Static değişken kendini ilklendirmek için özyinelemeli kod kullanmamalı.
int foo(int i) {
  static int s = foo(2*i); // recursive call - undefined
  return i+1;
}
Böyle bir kod şu exception'a sebep olur.
terminate called after throwing an instance of '__gnu_cxx::recursive_init_error'
  what():  std::exception
static local değişken ve heap
Static local değişkeni heap'te yaratabiliriz.

class B {
  public:
   static const B* GetInstance() {
     static B* b = new B ();
     return b;
   }
};
Ancak uygulama sonlanırken önce static local değişken yok olacağı için heap'teki nesneyi silme şansımız olmayacaktır. Bu gibi durumlarda std::unique_ptr kullanmak iyi bir fikir.

class B {
  public:
   static const B* GetInstance() {
     static std::unique_ptr<B> b( new B() );
     return b.get();
   }
};

static local değişkenin yok edilmesi
static local değişkenlere program kapanırken dokunmamak gerekir.
If a function contains a block-scope object of static or thread storage duration that has been destroyed and the function is called during the destruction of an object with static or thread storage duration, the program has undefined behavior if the flow of control passes through the definition of the previously destroyed block-scope object.

Aşağıdaki kod tanımsız iş yapar.
void theFunction()
{
  static std::unique_ptr<int> foo { new int(42) };
}

struct Creator
{
  Creator() { theFunction(); }
};


struct Destroyer
{
  ~Destroyer() { theFunction(); }
};

Destroyer d;
Creator c;

int main()
{}

16 Ocak 2016 Cumartesi

feof - End Of File

Giriş
Dosyanın sonuna gelindiği kontrolü (EOF) hata yapmaya açık bir konu. İnternette "EOF kontrolü yapan kodum neden çalışmıyor" şeklinde bir çok soru görülebilir.

EOF Karakteri Nedir
EOF karakteri diye bir karakter aslında yok. İşletim sistemi bir şekilde dosyanın boyunu biliyor ve sonuna geldiğimizde bize EOF - genelde -1 olarak tanımlıdır - dönüyor. Burada küçük bir açıklama yapmak lazım. Windows C Runtime ASCII 26 karakterini EOF olarak yorumluyor. Bu sadece Windows'a mahsus. Linux'ta böyle bir kural yok. Dolayısıyla Windows'ta binary bir dosyayı text olarak açarsak ve içinde tesadüfen ASCII 26 karakteri varsa henüz dosyanın sonuna gelmeden EOF alabiliriz. Aşağıdaki örnekte binary tar.gz dosyasını text modunda açınca bu durum görülebilir.
int oneChar;
iFile = fopen("myFile.tar.gz", "r");
while ((oneChar = fgetc(iFile)) != EOF) {
        printf("%d ", oneChar);
}
Aslında dosya şöyle açılmalıydı.
fopen("myFile.tar.gz", "rb");

feof nasıl çalışır?
Benim için akımların (stream) nasıl bu kontrolü yaptığını anlatan bu soruyu görmek ufuk açıcı bir deneyim oldu.

C dilindeki bu metod sadece ve sadece bir akımın eof bayrağının atanıp atanmadığını kontrol eder. Yani bu metod okuma işlemi yapmaz!
Bu bayrağın kaldırılması için, akımın en son karakterini de okuduktan sonra, bir tane daha okuma işlemi yapmamız gerekir! Aynı kural C++ için de geçerlidir.
Konuyu açıklayan küçük bir örnek
std::istringstream stream("a");
char ch;
if (stream >> ch) {
   std::cout << "At eof? " << std::boolalpha << stream.eof() << "\n";
   std::cout << "good? " << std::boolalpha << stream.good() << "\n";
}
Bu kod şunu döner
false //Aslında dosya sonuna geldik ama henüz geçmedik. EOF false
true //Aslında dosya sonuna geldik ama henüz geçmedik. Good true

Döngü Koşulu Olarak feof Kullanmak
Yukarıdaki açıklamayı okuduktan sonra döngü koşulu olarak feof kullanmak istiyorsa, bir tane read işleminin hata döndüreceğini kabul etmesi gerektiğini kabul ederek işe başlamak gerekir. Dolayısıyla her read işleminin sonucunu kontrol etmemiz lazım gelir. Yani mantra şu
Don't use good() or eof() to detect eof before you try to read any further
Peki bu zahmete neden gireriz? Aklıma gelen tek örnek dosyada istenilen formata uymayan geçersiz bir satır varsa, geçersiz satır ile EOF arasındaki farkı ayırt etmek için kullanımı.

Yanlış Kullanım Örneği - Yapmayın!
Örnek'te dosyanın sonuna gelinceye kadar sayılar okunuyor. Son sayılar da okunduktan sonra feof bir kere daha çağırılıyor. Ancak henüz akımın sonunu geçmedik. Yani Bir read işlemi akımın eof bayrağını atamadı. Dolayısıyla feof true döndürür ve fscanf bir kere daha çalışır. Bu sefer fscanf akımın eof bayrağını atar. Ancak fscanf için döndürdüğü sonuç kontrolü yapılmadığı için printf çalıştırılır. Ve son satır iki kere gösterilir!
#include<stdio.h>

int main()
{

  int i;
  FILE * f;
  f=fopen("inputA.txt","r");

  while (!feof(f)){
    fscanf(f, "%i", &i);
    printf("%i\t", i);
  }
  printf("\n");

}
C++ için Yanlış Kullanım Örneği - Yapmayın!
Bu örnek mantrayı ihlal eder.
ifstream foo("foo.txt");

while (foo.good()){
    string bar;
    getline(foo, bar);
    cout << bar << endl;
}
Bu örnek te mantrayı ihlal eder.
while(!inStream.eof()){
  int data;
   inStream >> data;
}

Doğru Kullanım Örneği
İllaki feof ile çalışmak istiyorsak read işleminden sonra okumanın başarılı olduğunu kontrol etmek gerekir.
while (!feof(fp)) {
  ++lineno;
  while (nfields == (nret = fscanf(fp, "%f,", &vec[0]))) {
    // do something with the vector read
    ++lineno;
  }

  if (ferror(fp)) {
    // handle the error, usually exit/return
  } else if (nret != EOF) {
    fprintf(stderr, "warning: ignoring malformed line %zu\n", lineno);
    fscanf(fp, "%*[^\n]");
  }
}
C++'ta şöyle yapılır
while( !in.eof() ) 
{  
   int data;
   in >> data;
   if ( in.fail() ) /* handle with break or throw */; 
   // now use data
}

Döngü Koşulu Olarak Read İşlemi Kullanmak
Bu örneklerde dosyada hatalı satır olması ile ilgilenmeyiz. Sadece tüm satırları okumaya çalışırız. Aşağıdaki örneklerin hepsi doğru çalışır.

fgetc kullanımı
while döngüsünden çıktından sonra ferror kontrolü bence isteğe bağlı. EOF'tan dolayı mı yoksa okuma hatasından dolayı mı çıktığımızı anlamaya yarar.
/*
 * ch MUST be an int. Otherwise you can't distinguish between
 * (unsigned char)255 and EOF, and this won't work.
 */
int ch;

while ((ch = fgetc(fp)) != EOF) {
    // do something with ch
}
if (ferror(fp)) {
    // handle the error, usually exit or return
} else {
    // continue execution
}
fgets kullanımı
while döngüsünden çıktından sonra ferror kontrolü bence isteğe bağlı. EOF'tan dolayı mı yoksa okuma hatasından dolayı mı çıktığımızı anlamaya yarar.
while (fgets(buffer, buffer_size, fp)) {
    // do something with buffer
}
if (ferror(fp)) {
    // handle the error, usually exit or return
} else {
    // continue execution
}
C++ ile getline
C++ ile getline kullanarak bir satırı okumak mümkün. Döngüden EOF yüzünden mi yoksa satır sonu geldiği için mi çıkıldığını anlamak kolay.
while(getline(file,line) && !file.fail()) {}
C++ ile stream
Örnek
int data;
while(inStream >> data){
 
}
fread kullanımı
fread yazısına taşıdım.