19 Ocak 2018 Cuma

C++ İle Exception

C++ İle Exception
metod imzasında throw
exception specification C++11 ile kullanımdan kaldırıldı. metodun imzasına throw ile atabileceği exceptionları yazmanın anlamı yok. Yeni kodlarda throw yazmak yerine açıklama yazılmalı.
/**
 * Some function description.
 *
 * @throws SomeException In this or that situation this function will thrown an 
 * exception of type SomeException.
 */
void Foo();

metod imzasında noexcept kullanılmaya devam edilebilir.

Exception nerede yaşar
Standarttaki açıklama şöyle.
[except.throw] 15.1/4: The memory for the exception object is allocated in an unspecified way, except as noted in 3.7.4.1. The exception object is destroyed after either the last remaining active handler for the exception exits by any means other than rethrowing, or the last object of type std::exception_ptr (18.8.5) that refers to the exception object is destroyed, whichever is later.
Yani exception nesnesi fırlatıldıktan sonra runtime'da özel bir alanda yaşar.

Stackteki exception bu özel alana nasıl taşınır
Bu açıklama sanırım pointer olarak fırlatılmayan exception nesneleri için geçerli. Kendi kodumuzdaki exception nesnesinin, bu özel alandaki Exception nesnesine atanması tamamen derleyicinin nasıl kod ürettiğine bağlı. Kopyalama (gerekiyorsa) işleminin olabilmesi için bazı metodların erişilebiliyor olması lazım.
15.1/5 When the thrown object is a class object, the constructor selected for the copy-initialization and the destructor shall be accessible, even if the copy/move operation is elided (12.8).

Mesela Visual Studio 2013 kopyalama yöntemini kullanıyor. Dolayısıyla kendi exception nesnemizin destructor metodunun iki defa çağrıldığını görebiliriz. GCC ise başka bir kod üretiyor. 
// throw MyError("hi"); expands to:
auto tmp1 = MyError("hi");
auto exceptionObject = std::move(tmp1);
tmp1.~MyError(); //1. Destructor
goto catch;

// catch expands to:
MyError& exc = exceptionObject;
cout << exc.what() << endl;

// } of catch handler expands to:
exceptionObject.~MyError(); //2. Destructor

Static exception nesnesi
more effective c++ Item no -13'te metod içindeki static exception nesnesi örneği var.
void someFunction()
{
    static exception ex;            // exception object
    ...
    throw &ex;                      // throw a pointer to ex
    ...
}
Kendi exception sınıfımız
Kendi Exception Sınıfımız yazısına taşıdm.

STL İçinde Tanımlı exceptionlar
C++ ile gelen tanımlı exceptionları şunlar
exceptions hierarchy

std::exception sınıfı
std::exception Sınıfı yazısına taşıdım.

std::runtime_error
std::runtime_error Sınıfı yazısına taşıdım.

std::logic_error
std::logic_error Sınıfı yazısına taşıdım.

Exception Slicing
Exception hiyerarşimiz varsa, fırlatılan exception'ı referans olarak yakalamak gerekir. Örnek:
catch(const Exception& e)
Aksi takdirde exception slicing'e maruz kalırız. Bu tabir exception içindeki virtual metodların kullanılamaması anlamına gelir.
Aşağıdak kod parçası çıktı olarak "NullPointerException" yerine "Exception" yazar.

#include <iostream>
class Exception{
public:
    virtual void print(){
        std::cerr << "Exception\n";
    }
};
class NullPointerException : public Exception{
public:
    void print(){
        std::cerr << "NullPointerException\n";
    }
};
int main(){
    try{
        throw NullPointerException();
    }catch(Exception e){
        e.print();
    }
    return 0;
}

Exception ve finally
Java, C# gibi dillerde exception try/catch/finally şeklinde kullanılıyor. Bu kullanım şekli eğer dilin içinde deterministic destructor yoksa faydalı. C++'ta RAII - yani constructor da kaynağı al, destructor da bırak - olduğu için pek tercih edilmiyor

Exception ve macro
Macro sadece exception mesajı içine dosya ve satır numarası konulacaksa kullanılmalı.
Ancak function-like macro şeklinde olmamalı. Yanlış bir örnek
#define __EXCEPTION(aMessage) \
{ \
  std::ostringstream stream; \
  stream << "EXCEPTION: "<<aMessage<< ", file "<<__FILE__<< " line "<< __LINE__; \
  throw ExceptionImpl(stream.str()); \
}
Inline metod kullanan daha iyi bir örnek
#define MY_EXCEPTION(aMessage) MyException(aMessage, __FILE__, __LINE__) 

inline void MyException(const std::string aMessage,
                        const char* fileName,
                        const std::size_t lineNumber)
{
  std::ostringstream stream;
  stream << "EXCEPTION:"<<aMessage <<",file"<< fileName << " line " << lineNumber;
  throw ExceptionImpl(stream.str());
}
Exception'ı Tekrar Fırlatma
throw ile Exception'ı Tekrar Fırlatma yazısına taşıdım.

Exception ve Catch Sırası
Açıklaması şöyle.
The handlers for a try block are tried in order of appearance. That makes it possible to write handlers that can never be executed, for example by placing a handler for a derived class after a handler for a corresponding base class.
Exception ve Catch(...)
gcc'de exception üç nokta ile yakalansa bile ismine erişme imkanı var. cxxabi.h ile abi namespace içindeki __cxa_exception_type()->name() ile exception ismi alınır.
#include <cxxabi.h>

// more code here 
} catch (...) {
    std::string exName(abi::__cxa_current_exception_type()->name());
    std::cout<<"unknown exception: "<< exName <<std::endl;
    throw;
}



Hiç yorum yok:

Yorum Gönder