İstisnalar için sözleşmeleri nasıl oluşturabilir ve uygulayabilirim?


33

Takım isSuccessfulliderimi, hata kodu ile bir bool veya enum döndürmek yerine C ++ 'da istisnalar kullanmaya izin vermeye ikna etmeye çalışıyorum . Ancak onun bu eleştirisine karşı koyamam.

Bu kütüphaneyi düşünün:

class OpenFileException() : public std::runtime_error {
}

void B();
void C();

/** Does blah and blah. */
void B() {
    // The developer of B() either forgot to handle C()'s exception or
    // chooses not to handle it and let it go up the stack.
    C();
};

/** Does blah blah.
 *
 * @raise OpenFileException When we failed to open the file. */
void C() {
    throw new OpenFileException();
};
  1. B()Fonksiyonu çağıran bir geliştirici düşünün . Belgelerini kontrol eder ve istisnalar döndürmediğini görür, bu yüzden hiçbir şey yakalamaya çalışmaz. Bu kod programın üretimde çökmesine neden olabilir.

  2. C()Fonksiyonu çağıran bir geliştirici düşünün . Belgeleri kontrol etmiyor, bu yüzden istisnalar yakalamıyor. Çağrı güvenli değil ve programın üretimde çökmesine neden olabilir.

Ancak hataları bu şekilde kontrol edersek:

void old_C(myenum &return_code);

Bu işlevi kullanan bir geliştirici, bu argümanı sağlamazsa derleyici tarafından uyarılır ve "Aha, kontrol etmem gereken bir hata kodu döndürür" derdi.

Bir çeşit sözleşme yapılması için istisnaları nasıl güvenle kullanabilirim?


4
@Azarım Asıl avantajı, geliştiricinin derleyici tarafından geri dönüş kodunu saklama fonksiyonuna bir değişken sağlaması için zorlanmasıdır; bir hata olabileceğinin farkındadır ve bununla başa çıkması gerekir. Bu, hiç bir zaman derleme yapamayan istisnalardan kesinlikle daha iyidir.
DBedrenko

4
"O halletmeli" - Bunun da olup olmadığını kontrol etmiyoruz.
Sjoerd

4
@Sjoerd Gerekli değil. Geliştiricinin bir hatanın olabileceğinin ve onu kontrol etmesi gerektiğinin farkında olması yeterlidir. Bir hata olup olmadığını kontrol edip etmeme konusunda hakemlerin de görüşü vardır.
DBedrenko

5
Hatayı güvenli bir şekilde EitherResult
yazılabilir bir

5
@gardenhead Çünkü çok sayıda geliştirici doğru kullanmıyor, çirkin kod ile bitiyor ve kendileri yerine özelliği suçlamaya devam ediyorlar. Java’daki denetlenen istisna özelliği güzel bir şey, ancak kötü bir rap alıyor çünkü bazı insanlar bunu anlamıyor.
AxiomaticNexus

Yanıtlar:


47

Bu, istisnaların meşru bir eleştirisidir. Genellikle bir kod döndürme gibi basit hata işlemeden daha az görülürler. Ve bir “sözleşmeyi” yürürlüğe koymanın kolay bir yolu yoktur. Bunun amacı, istisnaların daha yüksek bir seviyede yakalanmasına izin vermektir (her istisnayı her seviyede yakalamak zorundaysanız, bir hata kodu döndürmekten ne kadar farklıdır?). Bu da kodunuzun, uygun şekilde işlemeyen başka bir kod tarafından çağrılabileceği anlamına gelir.

İstisnaların olumsuz yanları vardır; Fayda temelli bir dava açmak zorundasınız.
: Ben yararlı olan bu iki makale buldum istisnalar gerekliliğini ve istisnalar dışında her şey yanlış. Ayrıca, bu blog yazısı C ++ 'a odaklanan istisnalar hakkında birçok uzman görüşü sunmaktadır . Uzman görüşü istisnalar lehine görünmekle birlikte, net bir fikir birliğinden uzaktır.

Takım liderini ikna etmek gelince, bu seçim yapmak için doğru savaş olmayabilir. Özellikle eski kodlarla değil. Yukarıdaki ikinci linkte belirtildiği gibi:

İstisnalar, istisnai olarak güvenli olmayan herhangi bir kodla çoğaltılamaz. Bu nedenle istisnaların kullanılması, projedeki tüm kodların istisna güvenliğinde olması gerektiği anlamına gelir.

Ağırlıklı olarak muhtemelen edilir etmediğini bir projeye istisnalar kullanan kod biraz ekleme değil bir gelişme olacak. Aksi takdirde iyi yazılmış kodlarda istisnalar kullanmamak, feci bir sorundan uzaktır; Uygulamaya ve sorduğunuz uzmana bağlı olarak hiç sorun olmayabilir. Savaşlarını seçmelisin.

Bu muhtemelen üzerinde çalışacağım bir tartışma değil - en azından yeni bir proje başlayana kadar. Yeni bir projeniz olsa bile, herhangi bir eski kod tarafından kullanılacak mı veya kullanılacak mı?


1
Tavsiye ve bağlantılar için teşekkür ederiz. Bu yeni bir proje, eski değil. Kullanımlarını güvensiz yapan çok büyük dezavantajları olduğunda, istisnaların neden bu kadar yaygın olduğunu merak ediyorum. N düzeylerinde daha yüksek bir istisna yakalarsanız, daha sonra kodu değiştirip hareket ettirirsiniz, istisnanın hala yakalanıp işlendiğini garanti etmek için statik kontrol yoktur (ve hakemlerin derinlemesine tüm fonksiyonları kontrol etmelerini istemek için çok fazla ).
DBedrenko

3
@ İstisnalar haricindeki bazı argümanlar için yukarıdaki bağlantıları inceleyin (Daha fazla uzman görüşü ile ek bir bağlantı ekledim).

6
Bu cevap çok açık!
Doktor Brown,

1
Önceden varolan bir kod tabanına istisnalar eklemeye çalışmak gerçekten kötü bir fikirdir. Böyle bir kod temeli ile çalıştım ve GERÇEKTEN, GERÇEKTEN çalışmak zordu çünkü yüzünüze neyin patlayacağını bilmiyordunuz. İstisnalar SADECE onlar için önceden plan yaptığınız projelerde kullanılmalıdır.
Michael Kohne

26

Kelimenin tam anlamıyla bu konuda yazılmış tüm kitaplar var, bu yüzden herhangi bir cevap en iyi şekilde özet olacaktır. Sorunuza dayanarak yapmaya değer olduğunu düşündüğüm önemli noktalardan bazıları. Kapsamlı bir liste değil.


İstisnaların her yere yakalanmaması amaçlanmıştır.

Ana döngüde genel bir istisna işleyicisi olduğu sürece - uygulamanın türüne bağlı olarak (web sunucusu, yerel servis, komut satırı yardımcı programı ...) - genellikle ihtiyacınız olan tüm özel durum işleyicileri vardır.

Kodumda, ana döngünün dışında sadece birkaç tane yakalama cümlesi var (eğer varsa). Ve bu modern C ++ 'da ortak yaklaşım gibi görünüyor.


İstisnalar ve iade kodları karşılıklı olarak dışlanmaz.

Bunu bir ya hep ya hiç yaklaşımı yapmamalısınız. İstisnai durumlar için istisnalar kullanılmalıdır. "Config dosyası bulunamadı", "Disk Dolu" veya yerel olarak kullanılamayacak herhangi bir şey.

Kullanıcı tarafından sağlanan bir dosya adının geçerli olup olmadığını kontrol etmek gibi yaygın hatalar, istisnalar için bir kullanım örneği değildir; Bunun yerine bu durumlarda bir dönüş değeri kullanın.

Yukarıdaki örneklerden gördüğünüz gibi, "dosya bulunamadı" kullanım durumuna bağlı olarak bir istisna veya dönüş kodu olabilir: "kurulumun bir parçasıdır" karşı "kullanıcı yazım hatası yapabilir".

Yani mutlak bir kural yoktur. Kaba bir kılavuz: yerel olarak ele alınabiliyorsa, onu bir geri dönüş değeri yapın; Yerel olarak idare edemiyorsanız, bir istisna atın.


İstisnaların statik kontrolü uygun değildir.

İstisnalar yine de yerel olarak ele alınmadığından, hangi istisnaların atılabileceği genellikle önemli değildir. Tek yararlı bilgi, herhangi bir istisnanın atılıp atılamayacağıdır.

Java'nın statik denetimi vardır, ancak genellikle başarısız bir deneme olarak kabul edilir ve çoğu dilde - özellikle C # - bu tür statik denetim olmadığından. Bu C # neden yok nedenleri hakkında iyi bir okuma .

Bu nedenlerden dolayı, C ++ throw(exceptionA, exceptionB)lehine itirazda bulundu noexcept(true). Varsayılan, bir fonksiyonun atabileceğidir, bu nedenle programcılar, belgeler açıkça belirtilmediği sürece bunu beklemelidir.


İstisna güvenli kodunu yazmak, istisna işleyicileri yazmakla hiçbir ilgisi yoktur.

İstisna güvenli kod yazmanın tamamen istisna işleyicileri yazmaktan nasıl kaçınılması gerektiğini söylerdim!

En iyi uygulamaların çoğu istisna işleyicilerinin sayısını azaltmayı amaçlamaktadır. Bir kez kod yazıp otomatik olarak çağırmak - örneğin RAII aracılığıyla - aynı kodu her yere kopyalayıp yapıştırmaktan daha az hatayla sonuçlanır.


3
Genel bir istisna işleyicisi olduğu sürece ... zaten zaten iyisiniz. ” Bu, istisna güvenli kod yazmanın çok zor olduğunu vurgulayan çoğu kaynağa zıtlık getirir.

12
@ dan1111 "İstisna güvenli kodu yazmak" istisna işleyicileri yazmakla çok az ilgili. İstisna güvenli kod yazmak, kaynakları kapsüllemek için RAII kullanmak, "önce yerel olarak tüm işleri, sonra takas / taşı kullan" ve diğer en iyi uygulamaları kullanmakla ilgilidir. Alışık olmadıklarında bunlar zor. Ancak "istisna işleyicileri yazmak" bu en iyi uygulamaların bir parçası değildir.
Sjoerd

4
@AxiomaticNexus Bir düzeyde, bir özelliğin her başarısız kullanımını "kötü geliştiriciler" olarak açıklayabilirsiniz. En iyi fikirler, kötü kullanımı azaltan fikirlerdir, çünkü doğru şeyi yapmayı kolaylaştırırlar. Statik olarak kontrol edilen istisnalar bu kategoriye girmez. Kendi değerleriyle orantısız iş yaratırlar, çoğu zaman kodun yazılmasının basitçe onları umursamaya gerek duymadığı yerlerde. Kullanıcılar derleyicinin onları rahatsız etmesini engellemek için yanlış yollarla susturmaya çalışırlar. Doğru yapılsalar bile çok fazla karışıklığa yol açarlar.
jpmc26

4
@AxiomaticNexus Orantısız olmasının nedeni, bunun kod tabanınızın tamamındaki hemen hemen her fonksiyona kolayca yayılabilmesidir . Sınıflarımın yarısındaki tüm işlevler veritabanına eriştiğinde, her birinin açıkça SQLException verebileceğini beyan etmesi gerekmiyor; onları çağıran her denetleyici yöntemi de yok. Bu kadar gürültü aslında işin ne kadar küçük olduğuna bakılmaksızın negatif bir değerdir. Sjoerd kafasına çiviyi vuruyor: kodunuzun çoğunun istisnalar ile ilgilenmesine gerek yok çünkü zaten onlar hakkında hiçbir şey yapamıyor .
jpmc26

3
@AxiomaticNexus Evet, çünkü en iyi geliştiriciler mükemmeldirler, kodları her zaman mümkün olan her kullanıcı girişini mükemmel bir şekilde idare eder ve bu istisnaları asla prodede asla görmezsiniz. Ve eğer yaparsan, bu hayatta başarısız olduğun anlamına gelir. Cidden, bu çöpü bana verme. Kimse mükemmel değil, en iyisi bile değil. Uygulamanız, "sanely" "dur ve yarım düzgün bir hata sayfası / mesajı döndürse" olsa bile, bu hatalar karşısında bile akılcı davranmalıdır. Ve tahmin et ne oldu: s de % 99 ile aynı şeyi yapıyorIOException . Kontrol edilen bölüm keyfi ve işe yaramaz.
jpmc26

8

C ++ programcıları istisna spesifikasyonlarını aramazlar. İstisna garantileri ararlar.

Bir kod parçasının bir istisna attığını varsayalım. Programcı bunun hala geçerli olacağını hangi varsayımlarla yapabilir? Kodun yazıldığı şekliyle kod, bir istisna sonrasında ne garanti ediyor?

Veya, belirli bir kod parçasının asla atılmamasını garanti edebileceği (yani, işletim sistemi işleminden başka hiçbir şeyin sonlandırılmadığı) mümkün mü?

İstisnalarla ilgili tartışmalarda "geri alma" kelimesi sıklıkla ortaya çıkıyor. Geçerli bir duruma (açıkça belgelenmiştir) geri dönebilmek bir istisna garantisi örneğidir. İstisna garantisi yoksa, bir program derhal sona ermelidir, çünkü daha sonra çalıştırılan herhangi bir kodun amaçlandığı gibi çalışacağı garanti edilmez - örneğin, eğer bellek bozulursa, başka bir işlem teknik olarak tanımsız davranıştır.

Çeşitli C ++ programlama teknikleri istisna garantileri sunar. RAII (kapsam tabanlı kaynak yönetimi), temizleme kodunu yürütmek ve kaynakların hem normal durumlarda hem de istisnai durumlarda serbest bırakılmasını sağlamak için bir mekanizma sağlar. Nesnelerde değişiklik yapmadan önce verilerin bir kopyasını almak, işlemin başarısız olması durumunda kişinin bu nesnenin durumunu geri yüklemesine izin verir. Ve bunun gibi.

Bu StackOverflow sorusuna verilen cevaplar, C ++ programcılarının kodlarında olabilecek tüm olası arıza modlarını anlamaya ve başarısızlıklarına rağmen program durumunun geçerliliğini korumaya çalışmasına büyük ölçüde bir fikir verir. C ++ kodunun satır bazında analizi alışkanlık haline gelir.

C ++ 'da (üretim kullanımı için) geliştirilirken, detaylardan bahsedemezsiniz. Ayrıca, ikili blob (açık kaynaklı olmayan) C ++ programcılarının ağzıdır. İkili blob çağırmam gerekiyorsa ve blob başarısız olursa, o zaman tersine mühendislik C ++ programcısının yapacağı şeydir.

Referans: http://en.cppreference.com/w/cpp/language/exceptions#Exception_safety - İstisna Güvenliği bölümüne bakın.

C ++ istisna spesifikasyonlarını uygulamada başarısız oldu. Diğer dillerde daha sonra yapılan analizler istisna şartnamelerinin pratik olmadığını söylüyor.

Neden başarısız bir girişim: kesinlikle zorla uygulamak için tip sisteminin bir parçası olmak zorunda. Ama öyle değil. Derleyici özel durum belirtimi için denetleme yapmaz.

Neden C ++ bunu seçti ve neden diğer dillerden (Java) elde edilen deneyimler istisna şartnamesinin suçlu olduğunu kanıtladı: Birinin bir işlevinin uygulanmasını değiştirdiği gibi (örneğin, yeni bir tür atabilecek başka bir işlevi çağırması gerekir) istisna), katı bir istisna şartname uygulaması, bu şartnameyi de güncellemeniz gerektiği anlamına gelir. Bu yayılır - basit bir değişiklik olan düzinelerce veya yüzlerce fonksiyon için istisna şartnamesini güncellemeniz gerekebilir. Soyut temel sınıflar için (işlerin C ++ eşdeğeri) durumları kötüye gider. Arayüzlerde istisna şartnamesi uygulanırsa, arayüz uygulamalarının farklı istisna türleri atan fonksiyonları çağırmasına izin verilmez.

Referans: http://www.gotw.ca/publications/mill22.htm

C ++ 17 ile başlayarak, [[nodiscard]]öznitelik, işlev dönüş değerlerinde kullanılabilir (bkz. Https://stackoverflow.com/questions/39327028/can-ac-function-be-declared-such-that-the-return-value göz ardı edilemez .

Öyleyse, bir kod değişikliği yaparsam ve bu yeni bir tür arıza koşulu ortaya çıkarırsa (örneğin yeni bir istisna türü), bu bir değişiklik mi? Arayan kişiyi kodu güncellemeye mi zorlamalıydı yoksa en azından değişiklik konusunda uyarılmalı mıydı?

C ++ programcılarının istisna spesifikasyonları yerine istisna garantileri aradığı argümanlarını kabul ederseniz, cevap, yeni tür bir arıza koşulu istisnaların hiçbirini bozmazsa, daha önce vaat ettiğiniz kodu garanti etmez, bunun kesin bir değişiklik olmadığıdır.


"Bir işlevin uygulanmasını değiştirdiğinde (örneğin, yeni bir istisna ortaya koyan farklı bir işleve çağrı yapması gerekir), katı bir özel durum belirtimi uygulaması bu belirtimi de güncellemeniz gerektiği anlamına gelir" - İyi , yapman gerektiği gibi. Alternatif ne olurdu? Yeni sunulan istisna dikkate alınmıyor mu?
AxiomaticNexus

@AxiomaticNexus Bu, istisna garantisiyle de yanıtlanır. Aslında, şartnamenin asla tamamlanamayacağını savunuyorum; Garanti aradığımız şey. Genellikle, hangi garantinin geçerli olduğunu tahmin etmek için hangi garantinin kırıldığını bulmak için istisna türünü karşılaştırırız. İstisna özellikleri, kontrol etmeniz gereken türlerden bazılarını listeler, ancak herhangi bir pratik sistemde listenin muhtemelen tamamlanamayacağını unutmayın. Çoğu zaman, bu durum tip sert denetimi yapar başka istisna, sararak, istisna tipini "yeme" nin kestirmeden insanları zorlar
rwong

@AxiomaticNexus İstisna kullanımına karşı ya da buna karşı çıkmıyorum. Tipik istisna kullanımı, temel istisna sınıfına ve bazı türetilmiş istisna sınıflarına sahip olmayı teşvik eder. Bu arada, bir başka istisna türlerinin de mümkün olduğunu, örneğin C ++ STL'den veya diğer kütüphanelerden atılanların mümkün olduğunu hatırlamak zorundasınız. Bu durumda çalışmak için istisna şartnamesinin yapılabileceği iddia edilebilir: standart istisnaların ( std::exception) ve kendi esas istisnanızın atılacağını belirtin . Ancak, bütün bir projeye uygulandığında, basitçe her bir işlevin aynı şartnameye sahip olacağı anlamına gelir: gürültü.
rwong

İstisnalar söz konusu olduğunda, C ++ ve Java / C # 'nın farklı diller olduğunu belirtmek isterim. Öncelikle C ++, isteğe bağlı kod yürütülmesine veya verilerin üzerine yazılmasına izin vermek açısından güvenli değildir, ikincisi C ++ hoş bir yığın izlemesi yazmaz. Bu farklılıklar, C ++ programcılarını hata ve istisnaların ele alınması açısından farklı seçimler yapmaya zorladı. Ayrıca, bazı projelerin yasal olarak C ++ 'nın onlar için uygun bir dil olmadığını iddia edebileceği anlamına da geliyor. (Örneğin, şahsen TIFF görüntü kod
çözmenin

1
@ rwong: C ++ modelini izleyen dillerde istisna işleme ile ilgili büyük bir sorun , istisna nesnesinin türünecatch dayanmasıdır , çoğu durumda önemli olan şey istisnanın doğrudan sebebi değil, ne anlama geldiğidir. sistem durumu. Maalesef, bir istisna türü genellikle, kodun kısmen güncellenen (ve dolayısıyla geçersiz) bir durumda bir nesneyi bırakacak şekilde rahatsız edici bir şekilde bir kod parçasından çıkmasına neden olup olmadığı hakkında hiçbir şey söylemez.
Süperkat

4

C () işlevini çağıran bir geliştirici düşünün. Belgeleri kontrol etmiyor, bu yüzden istisnalar yakalamıyor. Çağrı güvenli değil

Tamamen güvenli. Her yerdeki istisnaları yakalaması gerekmiyor, sadece gerçekten yararlı bir şeyler yapabileceği bir yerde deneyebilir / deneyebilir. Sadece diş ipinden sızmasına izin veriyorsa güvenli değildir, ancak bunun olmasını önlemek genellikle zor değildir.


İstisnai bir durumun çıkmasını beklememesi C()ve sonuçta aramanın atlanmasından sonraki koda karşı güvence almaması nedeniyle güvensizdir . Bu kod bazı değişmezliklerin doğru kalmasını garanti etmek için gerekliyse, bu garanti, ortaya çıkan ilk istisnada geçerli olur C(). Mükemmel istisna güvenli yazılım diye bir şey yoktur ve kesinlikle istisnaların hiçbir zaman beklenmediği bir yer değildir.
cmaster

1
@cmaster O'ndan bir istisna çıkmasını beklememekC() büyük bir hatadır. C ++ 'daki varsayılan değer, herhangi bir fonksiyonun atabileceği şeydir - noexcept(true)derleyiciye aksi belirtmek için açık bir gereksinim vardır . Bu vaadi yoksa, bir programcı bir işlevin atabileceğini varsaymalıdır.
Sjoerd

1
@cmaster Bu kendi aptal hatası ve C () 'nin yazarı ile ilgisi yok. İstisna güvenliği, bir şey öğrenmeli ve onu takip etmeli. Eğer bilmediği bir işlevi çağırırsa, istisnasızdır, fırlarsa ne olacağını çok iyi idare etmelidir. İstisnasız olması gerekiyorsa, olan bir uygulama bulması gerekir.
DeadMG

@Sjoerd ve DeadMG: Standart herhangi bir işlevin istisna atmasına izin verirken, bu, projelerin kodlarında istisnalara izin vermesi gerektiği anlamına gelmez. Kodunuzdaki istisnaları yasaklarsanız, OP'nin ekip liderinin yaptığı gibi, istisna güvenli programlama yapmak zorunda kalmazsınız. Bir takım liderini istisnalar için daha önce istisnai olarak ücretsiz olan bir kod tabanına izin vermeye ikna etmeye çalışırsanız C(), istisnaların varlığını bozan bir ton işlev olacaktır . Bu gerçekten çok akıllıca bir şey, bir sürü soruna yol açmaya mahkum.
cmaster

@cmaster Bu yeni bir proje hakkında - kabul edilen cevapla ilgili ilk yorumu görün. Bu nedenle, mevcut işlevler olmadığı için kırılacak hiçbir işlev yoktur.
Sjoerd

3

Kritik bir sistem inşa ediyorsanız, takım liderinizin tavsiyelerine uymayı ve istisnalar kullanmamayı düşünün. Bu Lockheed Martin'in Ortak Vurucu Avcı Uçağı C ++ Kodlama Standartlarındaki AV Kuralı 208'dir . Öte yandan, MISRA C ++ yönergelerinin, bir MISRA uyumlu yazılım sistemi oluştururken istisnaların ne zaman kullanılabileceği ve kullanılamayacağına ilişkin çok özel kuralları vardır.

Kritik sistemler inşa ediyorsanız, olasılıklar statik analiz araçlarını da çalıştırıyor olmanızdır. Birçok statik analiz aracı, bir yöntemin dönüş değerini kontrol etmezseniz, hata işleme durumlarının kolayca ortaya çıkması durumunda sizi uyaracaktır. Bildiğim kadarıyla, uygun istisnaların ele alınmasının tespiti için benzer araç desteği o kadar güçlü değil.

Nihayetinde, sözleşmenin ve savunma programlamasının tasarımın statik analizle birleştiğinde, kritik yazılım sistemleri için istisnalardan daha güvenli olduğunu savunuyorum .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.