Büyük bir C ++ projesinde gereksiz #include dosyalarını nasıl tespit etmeliyim?


96

Visual Studio 2008'de büyük bir C ++ projesi üzerinde çalışıyorum ve gereksiz #includeyönergelere sahip bir çok dosya var . Bazen #includes'ler sadece yapaydır ve her şey, onlarla birlikte düzgün bir şekilde derlenir ve diğer durumlarda sınıflar ileri bildirilebilir ve #include .cppdosyaya taşınabilir . Bu durumların ikisini de tespit etmek için iyi araçlar var mı?

Yanıtlar:


50

Gereksiz içerme dosyalarını açığa çıkarmayacak olsa da, Visual Studio'nun derleme zamanında dahil edilen tüm dosyaların bir ağacını çıkaracak bir ayarı vardır /showIncludes(bir .cppdosyaya sağ tıklama Properties->C/C++->Advanced). Bu, dahil edilmesi gerekmeyen dosyaların tanımlanmasına yardımcı olabilir.

Ayrıca pimpl deyimine de bir göz atarak daha az başlık dosyası bağımlılığından kurtulabilir ve kaldırabileceğiniz püf noktasını görmeyi kolaylaştırabilirsiniz.


1
/ showincludes harika. Bunu manuel olarak yapmak, onsuz göz korkutucuydu.
shambolic

30

PC Lint bunun için oldukça iyi çalışıyor ve sizin için de her türlü saçma problemi buluyor. Visual Studio'da Harici Araçlar oluşturmak için kullanılabilecek komut satırı seçeneklerine sahiptir, ancak Visual Lint eklentisiyle çalışmanın daha kolay olduğunu buldum . Visual Lint'in ücretsiz sürümü bile yardımcı olur. Ama PC-Lint'e bir şans verin. Size çok fazla uyarı vermeyecek şekilde yapılandırmak biraz zaman alır, ancak ortaya çıkan sonuçlara şaşıracaksınız.


3
Bunun pc-lint ile nasıl yapılacağına dair bazı talimatlar riverblade.co.uk/…
David Sykes


26

!! SORUMLULUK REDDİ !! Ticari bir statik analiz aracı üzerinde çalışıyorum (PC Lint değil). !! SORUMLULUK REDDİ !!

Ayrıştırmayan basit bir yaklaşımla ilgili birkaç sorun vardır:

1) Aşırı Yük Setleri:

Aşırı yüklenmiş bir işlevin farklı dosyalardan gelen bildirimleri olması mümkündür. Bir başlık dosyasının kaldırılması, derleme hatası yerine farklı bir aşırı yükleme seçilmesine neden olabilir! Sonuç, daha sonra izini sürmek çok zor olabilecek, anlambilimde sessiz bir değişiklik olacaktır.

2) Şablon uzmanlıkları:

Aşırı yükleme örneğine benzer şekilde, bir şablon için kısmi veya açık uzmanlıklarınız varsa, şablon kullanıldığında hepsinin görünür olmasını istersiniz. Birincil şablonun uzmanlıkları farklı başlık dosyalarında olabilir. Uzmanlık içeren üstbilginin kaldırılması bir derleme hatasına neden olmaz, ancak bu uzmanlık seçilmiş olsaydı tanımlanmamış davranışla sonuçlanabilir. (Bakınız: C ++ fonksiyonunun şablon uzmanlığının görünürlüğü )

'Msalters' tarafından belirtildiği gibi, kodun tam bir analizini gerçekleştirmek, sınıf kullanımının analizine de izin verir. Bir sınıfın belirli bir dosya yolu üzerinden nasıl kullanıldığını kontrol ederek, sınıfın tanımının (ve dolayısıyla tüm bağımlılıklarının) tamamen kaldırılabilmesi veya en azından içerme içindeki ana kaynağa daha yakın bir düzeye taşınması mümkündür. ağaç.


@RichardCorden: Yazılımınız (QA C ++) çok pahalı.
Xander Tulip

13
@XanderTulip: Bir satış konuşmasına girmeden buna yanıt vermek zor - bu yüzden şimdiden özür dilerim. IMHO, göz önünde bulundurmanız gereken şey, herhangi bir makul boyutlu projede iyi bir mühendisin buna benzer şeyleri (ve diğer birçok dil / kontrol akış hatası) bulmasının ne kadar süreceğidir. Yazılım değiştikçe aynı görevin tekrar tekrar tekrarlanması gerekir. Dolayısıyla, kazanılan zamanı hesapladığınızda, aracın maliyeti muhtemelen önemli değildir.
Richard Corden

10

Böyle bir araç bilmiyorum ve geçmişte bir tane yazmayı düşündüm, ancak bunun çözülmesi zor bir problem olduğu ortaya çıktı.

Kaynak dosyanızın ah ve bh içerdiğini varsayalım; ah içerir #define USE_FEATURE_Xve bh kullanır #ifdef USE_FEATURE_X. Eğer #include "a.h"iptal edilmiştir, dosya hala derlemek olabilir, ancak beklediğiniz yapamazsınız. Bunu programlı olarak tespit etmek önemsiz değildir.

Bunun hangi aracı yaparsa yapsın, inşa ortamınızı da bilmesi gerekir. Eğer ah şöyle görünüyorsa:

#if defined( WINNT )
   #define USE_FEATURE_X
#endif

Daha sonra USE_FEATURE_Xyalnızca tanımlanmışsa WINNTtanımlanır, bu nedenle aracın derleyicinin kendisi tarafından hangi yönergelerin üretildiğini ve hangilerinin başlık dosyası yerine derleme komutunda belirtildiğini bilmesi gerekir.


9

Timmermans gibi, bunun için herhangi bir araca aşina değilim. Ama bir Perl (veya Python) betiği yazan ve her bir dahil satırını birer birer yorumlamayı deneyen ve ardından her dosyayı derleyen tanıdık programcılar var.


Görünüşe göre Eric Raymond'ın bunun için bir aracı var .

Google'ın cpplint.py'sinin bir "kullandığınızı dahil et" kuralı vardır (diğerlerinin yanı sıra), ancak anlayabildiğim kadarıyla " yalnızca kullandığınız şeyi dahil et" kuralı yoktur . Öyle bile olsa faydalı olabilir.


Bunu okuduğumda gülmek zorunda kaldım. Patronum bunu geçen ay projelerimizden birinde yaptı. Azaltılmış başlık, birkaç faktör içerir.
Don Wakefield

2
mac üzerinde codewarrior önceden bunu yapmak için yerleşik bir betiğe sahipti, yorum yapmak, derlemek, hata durumunda yorumdan çıkarma, #includes'in sonuna kadar devam ediyordu. Yalnızca dosyanın en üstünde #includes için çalıştı, ancak genellikle bulundukları yer orasıdır. Mükemmel değil, ama işleri makul ölçüde mantıklı tutuyor.
slycrel

5

Genel olarak bu konuyla ilgileniyorsanız, Lakos'un Büyük Ölçekli C ++ Yazılım Tasarımına göz atmak isteyebilirsiniz . Biraz eski, ancak dahil edilmesi gereken mutlak minimum başlıkları bulmak gibi birçok "fiziksel tasarım" sorununa giriyor. Gerçekten başka hiçbir yerde tartışılan bu tür şeyleri görmedim.


4

Ver Yöneticisi dahil bir deneyin. Visual Studio ile kolayca bütünleşir ve gereksiz şeyleri bulmanıza yardımcı olan dahil etme yollarınızı görselleştirir. Dahili olarak Graphviz kullanıyor ancak daha birçok harika özellik var. Ticari bir ürün olmasına rağmen fiyatı çok düşüktür.



3

Başlık dosyalarınız genellikle şununla başlıyorsa:

#ifndef __SOMEHEADER_H__
#define __SOMEHEADER_H__
// header contents
#endif

(#pragma'yı bir kez kullanmak yerine) bunu şu şekilde değiştirebilirsiniz:

#ifndef __SOMEHEADER_H__
#define __SOMEHEADER_H__
// header contents
#else 
#pragma message("Someheader.h superfluously included")
#endif

Ve derleyici, derlenmekte olan cpp dosyasının adını çıktıladığından, bu, en azından hangi cpp dosyasının başlığın birden çok kez getirilmesine neden olduğunu bilmenizi sağlar.


12
Başlıkları birden çok kez eklemenin sorun olmadığını düşünüyorum. Kullandıklarınızı eklemek iyidir ve bunu yapmak için içerme dosyalarınıza bağlı kalmazsınız. Bence OP'nin istediği, gerçekte kullanılmayan #içerenler bulmak.
Ryan Ginstrom

12
IMO aktif olarak yapılacak yanlış bir şey. Başlıklar, onlar olmadan çalışmayacakları zaman diğer başlıkları da içermelidir. Ve buna sahip olduğunuzda A.hve B.hbu her ikisine de bağlı C.hve dahil ettiğinizde A.hve B.hikisine de ihtiyacınız olduğu için iki kez dahil edeceksinizC.h , ama sorun değil, çünkü derleyici ikinci kez atlayacak ve yapmadıysanız, hatırlamanız gerekecek her zaman için dahil C.hönce A.hveya B.hçok daha yararsız kapanım kadar biten.
Jan Hudec

5
İçerik doğrudur, bu, birden çok kez eklenen başlıkları bulmak için iyi bir çözümdür. Ancak, asıl soru bununla cevaplanmıyor ve bunun ne zaman iyi bir fikir olacağını hayal edemiyorum. Cpp dosyaları, başlık başka bir yerden önce dahil edilmiş olsa bile, bağlı oldukları tüm başlıkları içermelidir. Projenizin derleme sırasına özel olmasını istemezsiniz veya farklı bir başlığın ihtiyacınız olanı içereceğini varsayarsınız.
jaypb

3

PC-Lint bunu gerçekten yapabilir. Bunu yapmanın kolay bir yolu, yalnızca kullanılmayan içerme dosyalarını algılayacak ve diğer tüm sorunları yok sayacak şekilde yapılandırmaktır. Bu oldukça basittir - sadece mesaj 766'yı ("Başlık dosyası modülde kullanılmıyor") etkinleştirmek için komut satırına -w0 + e766 seçeneklerini ekleyin.

Aynı yaklaşım, 964 ("Modülde doğrudan kullanılmayan başlık dosyası") ve 966 ("Modülde kullanılmayan dolaylı olarak dahil edilmiş başlık dosyası") gibi ilgili mesajlarla da kullanılabilir.

FWIW Bu konuyu geçen hafta http://www.riverblade.co.uk/blog.php?archive=2008_09_01_archive.xml#3575027665614976318 adresindeki bir blog gönderisinde daha ayrıntılı olarak yazdım .


2

#includeDerleme sürelerini azaltmak için gereksiz dosyaları kaldırmak istiyorsanız, zamanınız ve paranız derleme sürecinizi cl.exe / MP , make -j , Xoreax IncrediBuild , distcc / icecream vb. Kullanarak paralel hale getirmek için daha iyi harcanabilir .

Elbette, zaten paralel bir derleme süreciniz varsa ve bunu hala hızlandırmaya çalışıyorsanız, o zaman kesinlikle #includeyönergelerinizi temizleyin ve bu gereksiz bağımlılıkları kaldırın.


2

Her bir içerme dosyasıyla başlayın ve her bir içerme dosyasının yalnızca kendisini derlemek için gerekli olanı içerdiğinden emin olun. Daha sonra C ++ dosyaları için eksik olan tüm include dosyaları, C ++ dosyalarının kendilerine eklenebilir.

Her bir içerme ve kaynak dosyası için, her bir içerme dosyasını birer birer yorumlayın ve derlenip derlenmediğine bakın.

İçerme dosyalarını alfabetik olarak sıralamak da iyi bir fikirdir ve bunun mümkün olmadığı durumlarda bir yorum ekleyin.


2
Çok fazla sayıda uygulama dosyası varsa, bu yorumun ne kadar pratik olduğundan emin değilim.
Sonny

1

Aşağıdaki # tanımlamalardan birini veya her ikisini eklemek genellikle gereksiz başlık dosyalarını hariç tutar ve özellikle Windows API işlevlerini kullanmayan kod varsa derleme sürelerini önemli ölçüde iyileştirebilir.

#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN

Bkz. Http://support.microsoft.com/kb/166474


1
İkisine de gerek yok - VC_EXTRALEAN, WIN32_LEAN_AND_MEAN'ı tanımlar
Aidan Ryan

1

Henüz yapmadıysanız, değiştirmeyeceğiniz her şeyi (platform başlıkları, harici SDK başlıkları veya projenizin statik halihazırda tamamlanmış parçaları) eklemek için önceden derlenmiş bir başlık kullanmak, derleme sürelerinde büyük bir fark yaratacaktır.

http://msdn.microsoft.com/en-us/library/szfdksca(VS.71).aspx

Ayrıca, projeniz için çok geç olsa da, projenizi bölümler halinde organize etmek ve tüm yerel başlıkları tek bir büyük ana başlıkta toplamamak iyi bir uygulamadır, ancak biraz fazladan çalışma gerektirir.


Önceden derlenmiş başlıkların harika açıklaması: cygnus-software.com/papers/precompiledheaders.html (VisualStudio'nun son sürümlerinde önceden derlenmiş üstbilgilerin otomatik olarak oluşturulup oluşturulmadığından emin değilim, ancak kontrol etmeye değer.)
idbrii

1

Eclipse CDT ile çalışacaksanız , içerme yapınızı optimize etmek için http://includator.com adresini deneyebilirsiniz . Bununla birlikte, Includator, VC ++ 'nın önceden tanımlanmış içeriği hakkında yeterince bilgi sahibi olmayabilir ve doğru dahil etme ile VC ++ kullanmak için CDT'nin ayarlanması henüz CDT'de yerleşik değildir.


1

En son Jetbrains IDE, CLion, geçerli dosyada kullanılmayan öğeleri otomatik olarak (gri renkte) gösterir.

IDE'den kullanılmayan tüm içeriklerin (ve ayrıca işlevlerin, yöntemlerin, vb.) Listesine sahip olmak da mümkündür.


0

Mevcut cevaplardan bazıları bunun zor olduğunu belirtiyor. Bu gerçekten doğru, çünkü ileri bildirimin uygun olacağı durumları tespit etmek için tam bir derleyiciye ihtiyacınız var. Sembollerin ne anlama geldiğini bilmeden C ++ 'ı ayrıştıramazsınız; dilbilgisi bunun için çok belirsiz. Belirli bir adın bir sınıfı mı (ileri bildirilmiş olabilir) yoksa bir değişkeni mi (yapılamaz) adlandıracağını bilmelisiniz. Ayrıca, ad alanına duyarlı olmanız gerekir.


Sadece "Hangi #için gerektiğine karar vermek, durdurma problemini çözmekle eşdeğerdir. İyi şanslar :)" diyebilirsiniz. Elbette, buluşsal yöntemler kullanabilirsiniz, ancak bunu yapan herhangi bir özgür yazılım bilmiyorum.
porges


0

Artık gerekli olmadığını düşündüğünüz belirli bir başlık varsa (örneğin string.h), dahil etmek için yorum yapabilir ve ardından bunu tüm içeriklerin altına yazabilirsiniz:

#ifdef _STRING_H_
#  error string.h is included indirectly
#endif

Elbette arayüz başlıklarınız CPP belleğine dahil edilmelerini kaydetmek için farklı bir #define kuralı kullanabilir. Ya da kongre yok, bu durumda bu yaklaşım işe yaramaz.

Sonra yeniden inşa edin. Üç olasılık vardır:

  • İyi inşa ediyor. string.h derleme açısından kritik değildi ve bunun için içerme kaldırılabilir.

  • #Error gezileri. string.g bir şekilde dolaylı olarak dahil edilmişti string.h'nin gerekli olup olmadığını hala bilmiyorsunuz. Gerekirse, doğrudan #include etmelisiniz (aşağıya bakın).

  • Başka bir derleme hatası alıyorsunuz. string.h gerekliydi ve dolaylı olarak dahil edilmediğinden, include ile başlamak doğruydu.

.H veya .c dosyanızın doğrudan başka bir .h kullanması durumunda dolaylı dahil etmeye bağlı olarak, neredeyse kesinlikle bir hata olduğunu unutmayın: Aslında, kodunuzun yalnızca kullandığınız başka bir başlık gerektirdiği sürece bu başlığı gerektireceğini vaat ediyorsunuz. Muhtemelen demek istediğin bu değil.

Yapı hatalarına neden olan şeyleri bildirmek yerine davranışı değiştiren başlıklarla ilgili diğer yanıtlarda belirtilen uyarılar burada da geçerlidir.

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.