Try-catch veya C ++ 'da hata işleme için ifs


30

İstisnalar oyun motoru tasarımında yaygın olarak mı kullanılıyor, yoksa eğer if ifadeleri kullanılarak daha çok tercih edilir mi? Örneğin istisnalar dışında:

try {
    m_fpsTextId = m_statistics->createText( "FPS: 0", 16, 20, 20, 1.0f, 1.0f, 1.0f );
    m_cpuTextId = m_statistics->createText( "CPU: 0%", 16, 20, 40, 1.0f, 1.0f, 1.0f );
    m_frameTimeTextId = m_statistics->createText( "Frame time: 0", 20, 20, 60, 1.0f, 1.0f, 1.0f );
    m_mouseCoordTextId = m_statistics->createText( "Mouse: (0, 0)", 20, 20, 80, 1.0f, 1.0f, 1.0f );
    m_cameraPosTextId = m_statistics->createText( "Camera pos: (0, 0, 0)", 40, 20, 100, 1.0f, 1.0f, 1.0f );
    m_cameraRotTextId = m_statistics->createText( "Camera rot: (0, 0, 0)", 40, 20, 120, 1.0f, 1.0f, 1.0f );
} catch ... {
    // some info about error
}

ve ifs ile:

m_fpsTextId = m_statistics->createText( "FPS: 0", 16, 20, 20, 1.0f, 1.0f, 1.0f );
if( m_fpsTextId == -1 ) {
    // show error
}
m_cpuTextId = m_statistics->createText( "CPU: 0%", 16, 20, 40, 1.0f, 1.0f, 1.0f );
if( m_cpuTextId == -1 ) {
    // show error
}
m_frameTimeTextId = m_statistics->createText( "Frame time: 0", 20, 20, 60, 1.0f, 1.0f, 1.0f );
if( m_frameTimeTextId == -1 ) {
    // show error
}
m_mouseCoordTextId = m_statistics->createText( "Mouse: (0, 0)", 20, 20, 80, 1.0f, 1.0f, 1.0f );
if( m_mouseCoordTextId == -1 ) {
    // show error
}
m_cameraPosTextId = m_statistics->createText( "Camera pos: (0, 0, 0)", 40, 20, 100, 1.0f, 1.0f, 1.0f );
if( m_cameraPosTextId == -1 ) {
    // show error
}
m_cameraRotTextId = m_statistics->createText( "Camera rot: (0, 0, 0)", 40, 20, 120, 1.0f, 1.0f, 1.0f );
if( m_cameraRotTextId == -1 ) {
    // show error
}

İstisnaların ifs'ten biraz daha yavaş olduğunu duydum ve istisnaları if-check yöntemiyle karıştırmamalıyım. Ancak, istisnalar dışında, bence sadece bir yöntem için çok ağır olsalar bile, her initialize () yönteminden veya benzer bir şeyden sonra tonlarca ifs'ten daha fazla okunabilir kodum var. Oyun gelişimi için uygunlar mı yoksa basit ifs ile başa çıkmak daha mı iyi?


Birçok kişi Boost'un oyun geliştirme için kötü olduğunu iddia ediyor, ancak ["Boost.optional"] ( boost.org/doc/libs/1_52_0/libs/optional/doc/html/index.html ) 'e bakmanızı öneririm ifs örneğiniz biraz daha hoş
ManicQin

Andrei Alexandrescu'nun hata işlemesi hakkında hoş bir konuşma var, istisnasız olarak ona benzer bir yaklaşım kullandım.
Thelvyn

Yanıtlar:


48

Kısa cevap: Algılanabilir Hata Yönetimi 1 , Algılanabilir Hata Kullanımı 2 ve Mantıklı Hata Kullanımı 3'ü Niklas Frykholm tarafından okuyun. Aslında, sen blogdayken o blogdaki tüm makaleleri oku. Her şeye katılıyorum demeyeceğim ama çoğu altın.

İstisnaları kullanmayın. Bir sürü sebep var. En büyükleri listeleyeceğim.

Gerçekten daha yavaş olabilirler, ancak bu daha yeni derleyicilerde biraz azaltılabilir. Bazı derleyiciler, istisnaları tetiklemeyen kod yolları için "sıfır yükü istisnalarını" destekler (bu biraz yalan olsa da, istisna işleme için çalıştırılabilir / dll boyutunuzu şişirmek için hala fazladan veri kullanımı gerekir). Sonuç olarak, evet, istisnaları kullanmak daha yavaş ve performans açısından kritik kod yollarında kesinlikle bunlardan kaçınmalısınız. Derleyicinize bağlı olarak, etkin olmalarını sağlamak ek yük ekleyebilir. Her zaman kod boyutunu şişirir, daha ziyade, çoğu durumda, günümüzün donanımında performansı ciddi şekilde etkileyebilen önemli ölçüde şişirir.

İstisnalar, kodu çok daha kırılgan hale getirir . Bir grafik üzerinde (şu anda ne yazık ki bulamıyorum) temelde bir grafikte istisna güvenli kodla istisna güvenli olmayan kod yazmanın zorluğunu gösteren bir grafik var ve eski önemli ölçüde daha büyük bir çubuktur. Sadece istisnalar dışında çok sayıda ufak tefek şey var ve istisnaları güvenli görünen ama gerçekten değil gibi kod yazmanın birçok yolu var . Tüm C ++ 11 komitesi bile buna şaşırdı ve std :: unique_ptr komutunu doğru kullanmak için önemli bir yardımcı işlev eklemeyi unuttu ve bu yardımcı işlevlerde bile, bunları kullanmak için kullanmayacağından ve çoğu programcının kazandığından daha fazla yazı yazdı. Yapmazlarsa neyin yanlış olduğunu bile anlama.

Daha belirgin olarak, oyun endüstrisi için, bazı konsolların satıcı tarafından tedarik edilen derleyiciler / çalışma zamanları tamamen istisnaları tam olarak desteklemiyor, hatta hiç desteklemiyor. Kodunuz istisnalar kullanıyorsa, yine de bugün ve yaşta, yeni platformlara taşımak için kodunuzun bölümlerini yeniden yazmak zorunda kalabilirsiniz. (Söz konusu konsolların piyasaya sürülmesinden sonraki 7 yıl içerisinde bunun değişip değişmediğinden emin değilsiniz; istisnalar kullanmıyoruz, derleyici ayarlarında bile devre dışı bıraktıklarından, bu yüzden konuştuğum birinin yakın zamanda kontrol ettiğini bile bilmiyorum.)

Genel düşünme çizgisi oldukça açık: istisnai durumlar için istisnalar kullanın . Programınıza girdiğinde bunları kullanın. "Ne yapacağımı bilemiyorum, belki de başkası yapar, bu yüzden bir istisna atacağım ve ne olacağını göreceğim." Başka hiçbir seçenek mantıklı olmadığında bunları kullanın. Yanlışlıkla küçük bir hafıza sızdırması veya bir kaynağı temizleyememeniz veya uygun akıllı tutamaçların kullanımını karıştırdığınızdan emin olmamanız durumunda bunları kullanın. Diğer tüm durumlarda, onları kullanmayın.

Örnekteki gibi bir kodla ilgili olarak, sorunu çözmenin birkaç başka yolu vardır. En sağlamlarından biri - basit örneğinizde mutlaka en ideal olmasa da - monadik hata türlerine yaslanmaktır. Yani, createText () bir tamsayı yerine özel bir tanıtıcı türü döndürür. Bu tutamaç türü, metni güncellemek veya kontrol etmek için erişim sağlayıcılara sahiptir. Tutamaç bir hata durumuna getirildiyse (createText () başarısız oldu), daha sonra tutamağa yapılan çağrılar sessizce başarısız olur. Ayrıca, hatalı olup olmadığını ve varsa son hatanın ne olduğunu görmek için tanıtıcıyı sorgulayabilirsiniz. Bu yaklaşım diğer seçeneklerden daha fazla ek yüke sahiptir, ancak oldukça sağlamdır. Tek bir işlemin üretimde başarısız olabileceği, ancak yapamadığınız / kazanamadığınız / kazanamadığınız bazı bağlamlarda uzun bir işlem dizisi uygulamanız gerektiğinde kullanın.

Monadik hata işlemenin uygulanmasına bir alternatif, özel tanıtıcı nesneler kullanmak yerine, bağlamsal nesnedeki yöntemleri geçersiz tanıtıcı kimlikleriyle incelikle ele almasıdır. Örneğin, createText () başarısız olduğunda -1 döndürürse, o zaman bu tutamaçlardan birini alan m_statistics işlevine yapılan çağrılar, -1 iletilirse dikkatlice çıkmalıdır.

Aynı şekilde, hata basma işlemini gerçekten başarısız olan işlevin içine de koyabilirsiniz. Örneğinizde, createText () muhtemelen neyin yanlış gittiği hakkında daha fazla bilgiye sahip olduğundan, günlüğe daha anlamlı bir hata verebilir. Bu durumda, hatayı işleme / yazdırma işleminin arayanlara iletilmesinin çok az yararı vardır. Arayanların kullanımı özelleştirmesi (veya bağımlılık enjeksiyonunu kullanması) gerektiğinde bunu yapın. Bir hata günlüğe kaydedildiğinde ortaya çıkabilecek bir oyun içi konsola sahip olmanın iyi bir fikir olduğunu ve burada da yardımcı olacağını unutmayın.

Herhangi bir aklı başında bir ortamda başarısız olmasını beklemeyeceğiniz aramalar için en iyi seçenek (yukarıdaki bağlantılı makalelerde sunulan) - bir istatistik sistemi için metin blobları oluşturma eylemi gibi - sadece fonksiyona sahip olmaktır. bu başarısız oldu (örneğinizdeki createText) iptal edildi. Bir şey tamamen kazanılmadığı sürece createText () 'in üretimde başarısız olmayacağından emin olabilirsiniz (örneğin, kullanıcı yazı tipi veri dosyalarını silmiş veya bir nedenden dolayı sadece 256 MB belleğe sahip vs.). Bu vakaların çoğunda, başarısızlık gerçekleştiğinde yapılacak iyi bir şey bile yoktur. Bellek yetersiz? Kullanıcıya OOM hatasını göstermek için hoş bir GUI paneli oluşturmak için gerekli bir tahsis bile yapamayabilirsiniz. Eksik fontlar? Kullanıcıya hataları göstermeyi zorlaştırır. Neyin yanlışsa,

Siz sadece (a) hatayı bir günlük dosyasına kaydettiğiniz ve (b) sadece normal kullanıcı eylemlerinden kaynaklanmayan hatalar üzerine yaptığınız sürece, kilitlenme tamamen iyidir.

Aynı şeyi pek çok sunucu uygulaması için söyleyemem, kullanılabilirliğin kritik olduğu ve bekçi izleme sisteminin yeterli olmadığı, ancak oyun istemcisi geliştirmeden oldukça farklı olduğu söylenebilir. Ayrıca, diğer dillerin istisna işleme olanakları, yönetilen ortamlar olduğundan ve C ++ 'nın istisna güvenliği sorunlarına sahip olmadığından C ++ C gibi ısırma eğilimi göstermediğinden, sizi C / C ++ kullanmaya zorla yönlendiririm. Sunucular, oyun müşterileri gibi asgari gecikme garantilerinden çok paralellik ve verim üzerinde yoğunlaşmaya meyilli olduğu için performans kaygıları da hafifletilir. Atıcılar ve benzerleri için aksiyon oyunu sunucuları bile, C # ile yazıldığında oldukça iyi işleyebilir, çünkü, örneğin, FPS istemcilerin yapma eğiliminde olduğu gibi donanımı nadiren zorluyorlar.


1
+1. Ek olarak, warn_unused_resultele alınmayan hata kodunu yakalamaya izin veren bazı derleyicilerde işlev görecek özellik ekleme olasılığı da vardır .
Maciej Piechotka

Burada C ++ 'ı görmeye geldik ve monadik hata yönetimi zaten burada olmayacaktı. İyi şeyler!
Adam

performansın kritik olmadığı ve çoğunlukla hızlı bir uygulama hedeflediğiniz yerlere ne dersiniz? Mesela oyun mantığını uygularken?
Ali1S232

Ayrıca istisna işleme sadece hata ayıklama amacıyla kullanma fikri ne olacak? Oyunu bıraktığınız gibi, her şeyin sorunsuz bir şekilde ilerlemesini beklersiniz. bu nedenle, geliştirme sırasında hataları bulmak ve düzeltmek için istisnalar kullanırsınız ve daha sonra serbest bırakma modunda tüm bu istisnaları kaldırın.
Ali1S232

1
@ Gaboo: Oyun mantığı için, istisnalar sadece mantığı takip etmeyi zorlaştırır (tüm kodları takip etmeyi zorlaştırır). Pythnn ve C # istisnalarında bile benim deneyimime göre oyun mantığı çok nadirdir. hata ayıklama için, sert iddia genellikle yol daha uygundur. Kesin anda şu anda bir şeyler ters gider, istifin büyük kısımlarını çözdükten ve istisna atma anlamından dolayı her türlü bağlamsal bilgiyi kaybettikten sonra değil. Hata ayıklama mantığı için, araçlar ve bilgi / GUI'leri düzenlemek istersiniz, böylece tasarımcılar denetleyebilir ve ince ayar yapabilir.
Sean Middleditch

13

Donald Knuth’un eski bilgeliği:

“Küçük verimsizlikleri unutmalıyız, zamanın% 97'sini söyleyelim: erken optimizasyon tüm kötülüklerin kaynağıdır.”

Bunu büyük bir poster olarak yazdırın ve ciddi programlama yaptığınız her yere asın.

Yani bile eğer deneme / yakalama biraz daha yavaş olduğu zaman ben varsayılan olarak kullanmak olacaktır:

  • Kod mümkün olduğu kadar okunaklı ve anlaşılabilir olmalıdır. Sen belki bir kez kod yazmak ama olacak iyileştirilmesi için, anlamak için, diğer kod hata ayıklama için, bu kod hata ayıklama için bunu birçok defa daha okumak, ...

  • Performansa göre doğruluk. İf / else işlemlerini doğru yapmak önemsiz değildir. Örneğinizde yanlış yapıldı, çünkü bir hata gösterilemiyor, birden fazla gösteriliyor. Eğer / / / / sonra basamaklı kullanmanız gerekir.

  • Tutarlı kullanımı daha kolay: Deneyin / yakalayın, her satırda hataları kontrol etmeniz gerekmeyen bir stili benimseyin. Diğer yandan: One else / if ve kod kudreti batık tahribat eksik. Tabii ki sadece yeniden üretilemeyen koşullarda.

Yani son nokta:

İstisnaların ifşaya göre biraz daha yavaş olduğunu duydum

Bunu 15 yıl önce duydum, son zamanlarda güvenilir kaynaklardan duymadım. Derleyiciler iyileşmiş olabilir veya her neyse.

Ana nokta şudur: Erken optimizasyon yapmayın. Yalnızca, eldeki kodun, yoğun olarak kullanılan bazı kodların sıkı iç halkası olduğunu ve anahtarlama stilinin performansı önemli ölçüde iyileştirdiğini kanıtlamak için bunu yapabilirsiniz . Bu% 3 durumdur.


22
Bunu sonsuza kadar -1 alacağım. Bu Knuth alıntılarının geliştirici beyinlerine verdiği zararı anlayamıyorum. İstisnaların kullanılıp kullanılmayacağını göz önünde bulundurarak erken optimizasyon değil, bir tasarım kararıdır, performansın çok ötesinde sonuçları olan önemli bir tasarım kararıdır.
sam hocevar

3
@SamHocevar: Üzgünüm, fikrimi anlamadım: Soru , istisnalar kullanılırken ortaya çıkabilecek olası performansla ilgili. Demek istediğim: Düşünme, isabet o kadar da kötü değil (eğer varsa) ve diğer şeyler çok daha önemli. Burada listeme "tasarım kararı" ekleyerek aynı fikirde görünüyorsunuz. TAMAM. Öte yandan, Knuth'un teklifinin kötü olduğunu, erken optimizasyonun fena olmadığını ima ettiğini söylüyorsunuz. Ancak tam olarak burada olan budur IMO: Q mimarlık, tasarım veya farklı algoritmalar hakkında düşünmez, sadece istisnalar ve bunların performans üzerindeki etkileri hakkında düşünür.
AH,

2
C ++ ile netlik konusunda aynı fikirde olmazdım. Bazı derleyiciler kontrol edilmiş iade değerleri uygularken, C ++ 'nın işaretli istisnaları yoktur. Sonuç olarak daha net görünen ancak gizli bellek sızıntısı olmuş veya tanımsız duruma kod koyan kodunuz var. Ayrıca üçüncü taraf kodu istisna güvenli olmayabilir. (Durum, istisnaları kontrol eden Java / C # 'da farklıdır, GC, ...). Tasarım kararından itibaren - eğer API noktalarını geçmezlerse, her bir stile / stilden yeniden yapılanma perl tek gömleklerle yarı otomatik olarak yapılabilir.
Maciej Piechotka

1
@SamHocevar: " Soru, neyin daha hızlı olduğu değil, neyin daha iyi olduğu ile ilgilidir. " Sorunun son paragrafını tekrar okuyun. Sadece o da yavaş olabileceğini düşünüyor çünkü o bile yaklaşık istisnalar kullanmayan düşünen nedenidir. Şimdi, OP'nin göz önünde bulundurmadığı başka kaygılar olduğunu düşünüyorsanız, bunları yayınlamaktan ya da kaygılanmaktan çekinmeyin. Ancak OP, istisnaların performansına çok net bir şekilde odaklanmıştır.
Nicol Bolas

3
@AH - Knuth'dan alıntı yapacaksan, geri kalanını (ve IMO'nun en önemli kısmı) unutma: “ Ancak bu kritik% 3'teki fırsatlarımızı kaçırmamalıyız. İyi bir programcı olmayacak. Böyle bir nedenden ötürü gönül rahatlığı yaşamadan, kritik koda dikkatlice bakmak akıllıca olacaktır, ancak bu kod tanımlandıktan sonra . " Çok sık bir kişi bunu, hiç de optimizasyon yapmamak veya performansın optimizasyon olmadığı ama aslında temel bir gereksinim olduğu durumlarda, yavaş davranan kodun gerekçeli olduğunu doğrulamak için bir bahane olarak görür.
Maximus Minimus

10

Bu soruya gerçekten çok iyi bir cevap verdim, ancak özel durumunuzda kod okunabilirliği ve hata kurtarma hakkında bazı düşünceler eklemek istiyorum.

Kodunuzun gerçekten böyle görünebileceğine inanıyorum:

m_fpsTextId = m_statistics->createText( "FPS: 0", 16, 20, 20, 1.0f, 1.0f, 1.0f );
m_cpuTextId = m_statistics->createText( "CPU: 0%", 16, 20, 40, 1.0f, 1.0f, 1.0f );
m_frameTimeTextId = m_statistics->createText( "Frame time: 0", 20, 20, 60, 1.0f, 1.0f, 1.0f );
m_mouseCoordTextId = m_statistics->createText( "Mouse: (0, 0)", 20, 20, 80, 1.0f, 1.0f, 1.0f );
m_cameraPosTextId = m_statistics->createText( "Camera pos: (0, 0, 0)", 40, 20, 100, 1.0f, 1.0f, 1.0f );
m_cameraRotTextId = m_statistics->createText( "Camera rot: (0, 0, 0)", 40, 20, 120, 1.0f, 1.0f, 1.0f );

İstisna yok, eğer yok. Hata raporlaması yapılabilir createText. Ve createTextgeri dönüş değerini kontrol etmenizi gerektirmeyen varsayılan bir doku kimliği döndürebilir, böylece kodun geri kalanı da aynı şekilde çalışır.

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.