Mendeteksi berlebihan #termasuk dalam C / C ++?


289

Saya sering menemukan bahwa bagian tajuk dari file menjadi lebih besar dan lebih besar setiap saat tetapi tidak pernah menjadi lebih kecil. Sepanjang kehidupan sebuah kelas file sumber mungkin telah dipindahkan dan telah di refactored dan sangat mungkin bahwa ada beberapa #includesyang tidak perlu ada di sana dan lagi. Membiarkannya di sana hanya memperpanjang waktu kompilasi dan menambahkan dependensi kompilasi yang tidak perlu. Mencoba mencari tahu mana yang masih dibutuhkan bisa sangat membosankan.

Apakah ada beberapa alat yang dapat mendeteksi arahan #incian yang berlebihan dan menyarankan yang bisa saya hapus dengan aman?
Apakah serat melakukan ini mungkin?



1
Pertanyaan yang ditautkan tampaknya hanya untuk mengatasi masalah pada Windows, menggunakan Visual Studio pada khususnya.
D'Nabre

7
Memilih untuk membuka kembali ini, karena duplikatnya adalah tentang menggunakan Visual Studio, khususnya.
Drew Dormann

Jawaban:


42

Ini tidak otomatis, tetapi doxygen akan menghasilkan diagram dependensi untuk #includedfile. Anda harus melalui mereka secara visual, tetapi mereka bisa sangat berguna untuk mendapatkan gambaran tentang apa yang menggunakan apa.


5
Ini adalah cara yang bagus untuk melihat rantai .. melihat A -> B -> C -> D dan A -> D segera mengungkapkan redundansi.
Tom

34
@ Tom: Itu adalah Ide yang mengerikan: Untuk satu Ini tidak menunjukkan apakah mereka termasuk diperlukan atau tidak dan kedua, daftar termasuk tidak boleh tergantung pada tidak langsung termasuk yang dapat berubah di masa depan (Redundant biasanya tidak termasuk pokoknya masalah besar, terima kasih untuk menyertakan sihir penjaga dan kompiler), tetapi di mana kelas / fungsi sebenarnya digunakan dalam file (Kompiler Anda tidak harus melalui ribuan baris kode templat yang bahkan tidak bisa dipakai)
MikeMB

@ albert, dapatkah Anda menyertakan tangkapan layar ini, dan jelaskan secara singkat di mana harus mengklik di keluaran doxygen?
Gabriel Staples

@GabrielStaples Ini bukan jawaban saya, jadi saya tidak ingin menambahkan informasi ke dalamnya. Saya hanya mengoreksi tautannya (sebagai tempat hosting yang disebut berhenti / disita untuk digunakan).
albert

177

Google cppclean (tautan ke: unduh , dokumentasi ) dapat menemukan beberapa kategori masalah C ++, dan sekarang dapat menemukan #termasuk berlebihan.

Ada juga alat berbasis dentang, termasuk apa yang Anda gunakan , yang dapat melakukan ini. include-what-you-use bahkan dapat menyarankan penerusan deklarasi (jadi Anda tidak perlu # mencantumkan terlalu banyak) dan secara opsional membersihkan #tidak termasuk untuk Anda.

Versi Eclipse CDT saat ini juga memiliki fungsi ini bawaan: masuk di bawah menu Sumber dan mengklik Susun Termasuk akan mengabjadkan # sertakan Anda, tambahkan setiap tajuk yang menurut Eclipse sedang Anda gunakan tanpa secara langsung memasukkannya, dan mengomentari tajuk apa pun yang tidak dikerjakannya. pikir kamu perlu. Namun, fitur ini tidak dapat diandalkan 100%.


2
Itu sekarang. Saya baru mulai menggunakannya. Lihat catatan saya di sini. stackoverflow.com/questions/1301850/…
Peluang

1
Repositori cppclean sedang down, sekarang Anda bisa mendapatkannya di sini: bitbucket.org/robertmassaioli/cppclean (situs asli masih berguna untuk beberapa contoh penggunaan)
Nick

3
Saya memperbarui tautan ke garpu cppclean yang dikelola: github.com/myint/cppclean
BenC

1
Perhatikan bahwa cppclean tampaknya menemukannya hanya di file header, bukan file cpp, dari dokumen: "Tidak perlu #tercakup dalam file header".
Zitrax

1
@wizurd - Saya belum mengikuti perkembangan terkini di Eclipse CDT, tapi saya rasa tidak. iwyu menyeluruh dan relatif lambat. Analisis Eclipse CDT cepat (interaktif) dan, ketika saya mengujinya, kurang akurat.
Josh Kelley

65

Juga periksa termasuk apa yang Anda gunakan , yang memecahkan masalah serupa.


6
IMHO jawaban ini membutuhkan lebih banyak upvotes, karena begitu ketegaran dikerjakan alat Google IWYU akan menjadi alat yang pasti untuk tugas ini.
Dan Olson

5
sudo apt-get install iwyu
Andrew Wagner

Tampak hebat - dengan dua cavaets 1) pembaruan terakhir Feb 2106 2) Gogole sendiri menggunakannya hanya untuk C ++, bukan C, yang diminta OP.
Mawg mengatakan mengembalikan Monica

Bisakah Anda jelaskan sedikit bagaimana pengguna harus menggunakannya? README tidak begitu jelas tentang apa yang mengandung output skrip python.
King's badge

Saya menggunakan ini, tetapi tidak selalu 100% benar. Mungkin 70% kali ini memberikan saran yang benar.
InQusitive

25

Masalah dengan mendeteksi termasuk berlebihan adalah bahwa itu tidak bisa hanya pemeriksa ketergantungan tipe. Sertakan berlebihan adalah file yang tidak memberikan nilai apa pun pada kompilasi dan tidak mengubah item lain yang bergantung pada file lain. Ada banyak cara file header dapat mengubah kompilasi, katakanlah dengan mendefinisikan konstanta, mendefinisikan ulang dan / atau menghapus makro yang digunakan, menambahkan namespace yang mengubah pencarian nama beberapa cara di baris. Untuk mendeteksi item seperti namespace yang Anda butuhkan lebih dari sekadar preprosesor, Anda sebenarnya hampir membutuhkan kompiler lengkap.

Lint lebih seperti pemeriksa gaya dan tentu saja tidak akan memiliki kemampuan penuh ini.

Saya pikir Anda akan menemukan satu-satunya cara untuk mendeteksi tambahan yang tidak berguna adalah dengan menghapus, mengkompilasi dan menjalankan suite.


8
Tak satu pun dari ini akan menjadi masalah jika file yang disertakan diletakkan dengan baik. Jika Anda perlu menyertakan file A sebelum file B, Anda salah melakukannya (dan saya telah mengerjakan proyek yang salah).
David Thornley

9
@ David, ya tapi itu tergantung pada tahun devs sebelum Anda melakukannya dengan benar. Saya dapat mengatakan dengan sangat yakin bahwa kemungkinan itu terjadi menguntungkan rumah, bukan Anda :(
JaredPar

Ya, tapi saya biasanya mengetahuinya saat memodifikasi program, dan tiba-tiba saya mendapat kesalahan kompilasi (jika saya beruntung) atau bug yang tidak jelas. Tampaknya menjaga #include file jujur, setidaknya dalam jangka panjang.
David Thornley

Saya akan mengatakan sebaliknya. Yang Anda butuhkan adalah pemeriksa ketergantungan tipe. Mungkin tidak dapat dikompilasi setelah Anda mengatur termasuk, tetapi ini adalah masalah yang harus ditangani.
Benoît

1
@Enoeno, maka Anda akan mengabaikan kelas masalah yang mengkompilasi tetapi secara semantik mengubah makna program Anda. Pertimbangkan bagaimana #define dalam satu file dapat mengubah cabang #jika yang lain. Menghapus tajuk masih memungkinkan ini untuk dikompilasi dengan hasil yang berbeda
JaredPar

15

Saya pikir PCLint akan melakukan ini, tetapi sudah beberapa tahun sejak saya melihatnya. Anda mungkin memeriksanya.

Saya melihat blog ini dan penulis berbicara sedikit tentang mengkonfigurasi PCLint untuk menemukan termasuk yang tidak digunakan. Mungkin patut dilihat.


Bagus temukan! Saya harus menggunakannya.
crashmstr

4
Saya menggunakan PCLint secara teratur dan itu memberitahu saya tentang header yang tidak digunakan. Saya berhati-hati untuk komentar pada # include header dan re-kompilasi untuk memastikan bahwa header benar-benar tidak terpakai ...
Harold Bamford

Terima kasih atas konfirmasinya, Harold.
itsmatt

5
terlalu mahal. bukan alat yang layak untuk massa.

7

The CScout refactoring Browser dapat mendeteksi berlebihan termasuk arahan di C (sayangnya tidak C ++) kode. Anda dapat menemukan penjelasan cara kerjanya dalam ini artikel jurnal.


5

Anda dapat menulis skrip cepat yang menghapus direktif #include tunggal, mengkompilasi proyek, dan mencatat nama di #include dan file itu dihapus dari dalam kasus jika tidak ada kesalahan kompilasi terjadi.

Biarkan berjalan pada malam hari, dan hari berikutnya Anda akan memiliki daftar file yang benar 100% yang dapat Anda hapus.

Terkadang brute-force hanya berfungsi :-)


sunting: dan terkadang tidak :-). Berikut sedikit informasi dari komentar:

  1. Terkadang Anda dapat menghapus dua file header secara terpisah, tetapi tidak keduanya bersamaan. Solusinya adalah menghapus file header selama proses dan tidak mengembalikannya. Ini akan menemukan daftar file yang dapat Anda hapus dengan aman, meskipun mungkin ada solusi dengan lebih banyak file untuk dihapus yang tidak ditemukan algoritma ini. (ini adalah pencarian serakah atas ruang menyertakan file untuk menghapus. Ini hanya akan menemukan maksimum lokal)
  2. Mungkin ada perubahan perilaku yang halus jika Anda memiliki beberapa makro yang didefinisikan ulang secara berbeda tergantung pada beberapa # jika. Saya pikir ini adalah kasus yang sangat jarang, dan Tes Unit yang merupakan bagian dari pembangunan harus menangkap perubahan ini.

1
Hati-hati dengan ini - katakan ada dua file header yang keduanya menyertakan definisi sesuatu. Anda dapat menghapus keduanya, tetapi tidak keduanya. Anda harus sedikit lebih teliti dalam pendekatan brute force Anda.
Dominic Rodger

Mungkin ini yang Anda maksudkan, tetapi skrip yang menghapus satu menyertakan, dan meninggalkan yang terakhir dihapus termasuk jika berhasil dihapus akan melakukan trik.
Dominic Rodger

1
Ide buruk. Jika file header # mendefinisikan BLAH konstan dan file header lainnya memeriksa #ifdef BLAH, menghapus file header pertama mungkin masih berhasil dikompilasi tetapi perilaku Anda telah berubah.
Graeme Perrow

1
Ini juga dapat menyebabkan masalah dengan header sistem, karena implementasi yang berbeda mungkin memiliki hal-hal yang berbeda termasuk dalam #include <vector>. Bahkan jika Anda tetap menggunakan satu kompiler, tajuk dapat berubah dari versi yang berbeda.
David Thornley

2
Ini tidak akan menemukan kasus di mana Anda menyertakan tajuk yang menyertakan tajuk yang benar-benar Anda butuhkan.
bk1e

5

Maaf untuk (kembali) memposting di sini, orang sering tidak memperluas komentar.

Periksa komentar saya untuk crashmstr, FlexeLint / PC-Lint akan melakukan ini untuk Anda. Pesan informasi 766. Bagian 11.8.1 manual saya (versi 8.0) membahas hal ini.

Juga, dan ini penting, tetap iterasi sampai pesannya hilang . Dengan kata lain, setelah menghapus tajuk yang tidak digunakan, jalankan kembali serat, lebih banyak file tajuk mungkin menjadi "tidak dibutuhkan" setelah Anda menghapus beberapa tajuk yang tidak dibutuhkan. (Itu mungkin terdengar konyol, baca perlahan & parsing, itu masuk akal.)


Saya tahu persis apa yang Anda maksud, dan reaksi saya adalah "Ewwww". Saya benci kode seperti itu.
David Thornley

5

Saya tidak pernah menemukan alat lengkap yang memenuhi apa yang Anda minta. Hal terdekat yang saya gunakan adalah IncludeManager , yang membuat grafik pohon inklusi header Anda sehingga Anda dapat melihat secara visual hal-hal seperti header yang disertakan hanya dalam satu file dan inklusi header melingkar.


4

Saya sudah mencoba menggunakan Flexelint (versi unix PC-Lint) dan hasilnya agak beragam. Ini mungkin karena saya sedang mengerjakan basis kode yang sangat besar dan rumit. Saya merekomendasikan untuk memeriksa setiap file dengan hati-hati yang dilaporkan tidak digunakan.

Kekhawatiran utama adalah positif palsu. Beberapa menyertakan header yang sama dilaporkan sebagai header yang tidak dibutuhkan. Ini buruk karena Flexelint tidak memberi tahu Anda pada baris apa header tersebut disertakan atau di mana ia dimasukkan sebelumnya.

Salah satu cara alat otomatis dapat melakukan ini:

Di A.hpp:

class A { 
  // ...
};

Dalam B.hpp:

#include "A.hpp

class B {
    public:
        A foo;
};

Dalam C.cpp:

#include "C.hpp"  

#include "B.hpp"  // <-- Unneeded, but lint reports it as needed
#include "A.hpp"  // <-- Needed, but lint reports it as unneeded

Jika Anda mengikuti secara membabi buta pesan dari Flexelint Anda akan menghapus dependensi #include Anda. Ada lebih banyak kasus patologis, tetapi pada dasarnya Anda perlu memeriksa sendiri header untuk hasil terbaik.

Saya sangat merekomendasikan artikel ini tentang Struktur Fisik dan C ++ dari blog Games dari dalam. Mereka merekomendasikan pendekatan komprehensif untuk membersihkan #include mess:

Pedoman

Berikut adalah seperangkat pedoman yang disaring dari buku Lakos yang meminimalkan jumlah ketergantungan fisik antara file. Saya telah menggunakannya selama bertahun-tahun dan saya selalu sangat senang dengan hasilnya.

  1. Setiap file cpp menyertakan file header sendiri terlebih dahulu. [menggunting]
  2. File header harus menyertakan semua file header yang diperlukan untuk menguraikannya. [menggunting]
  3. File header harus memiliki jumlah minimum file header yang diperlukan untuk menguraikannya. [menggunting]

Buku Lakos sangat bagus untuk pendidikan - selain dari pengamatannya yang ketinggalan zaman tentang teknologi kompiler.
Tom

4

Jika Anda menggunakan Eclipse CDT, Anda dapat mencoba http://includator.com yang gratis untuk penguji beta (pada saat penulisan ini) dan secara otomatis menghapus #includes berlebihan atau menambahkan yang hilang. Bagi pengguna yang memiliki FlexeLint atau PC-Lint dan menggunakan Elicpse CDT, http://linticator.com mungkin menjadi pilihan (juga gratis untuk pengujian beta). Meskipun menggunakan analisis Lint, ini memberikan perbaikan cepat untuk secara otomatis menghapus pernyataan #incul yang berlebihan.


Alasannya adalah karena departemen pembukuan kami tidak dapat menagih jumlah yang lebih rendah. Jika Anda menghitung waktu Anda dapat menghemat itu tidak masuk akal. Sekali, kita punya kemampuan untuk mendapatkan pembayaran kartu kredit, kita bisa menurunkan harganya secara signifikan. Opsi lain akan menjadi sponsor untuk upaya pengembangan kami. Model pembiayaan kami mengharuskan kami untuk mendapatkan keuntungan untuk membiayai pekerjaan penelitian kami. Saya akan senang menjual lisensi jauh lebih murah, tetapi tidak bisa. Mungkin kami akan berkontribusi untuk CDT dan Anda mendapatkannya secara gratis, tetapi entah bagaimana saya harus membiayai. Saya lupa, Anda dapat mencoba secara gratis!
PeterSom

2

Artikel ini menjelaskan teknik menghapus #include dengan menggunakan parsing Doxygen. Itu hanya skrip perl, jadi sangat mudah digunakan.


1
Script menemukan beberapa menyertakan untuk dihapus tetapi juga memberikan banyak menyertakan yang tidak dapat dihapus. Tampaknya tidak mendukung kelas enum, tampaknya juga memiliki waktu yang buruk dengan makro dan kadang-kadang dengan namespace.
Baptiste Wicht



1

Ada dua jenis file #incul yang tidak berguna:

  1. File header sebenarnya tidak diperlukan oleh modul (.c, .cpp) sama sekali
  2. File header diperlukan oleh modul tetapi dimasukkan lebih dari satu kali, secara langsung, atau tidak langsung.

Ada 2 cara dalam pengalaman saya yang berfungsi baik untuk mendeteksinya:

  • gcc -H atau cl.exe / showincludes (menyelesaikan masalah 2)

    Di dunia nyata, Anda dapat mengekspor CFLAGS = -H sebelum membuat, jika semua Makefile tidak menggantikan opsi CFLAGS. Atau seperti yang saya gunakan, Anda dapat membuat pembungkus cc / g ++ untuk menambahkan opsi -H secara paksa untuk setiap pemanggilan $ (CC) dan $ (CXX). dan tambahkan direktori wrapper ke variabel $ PATH, maka make Anda akan menggunakan perintah wrapper. Tentu saja pembungkus Anda harus memanggil kompiler gcc nyata. Trik ini perlu diubah jika Makefile Anda menggunakan gcc secara langsung. bukannya $ (CC) atau $ (CXX) atau dengan aturan tersirat.

    Anda juga dapat mengkompilasi satu file dengan tweaker dengan baris perintah. Tetapi jika Anda ingin membersihkan tajuk untuk keseluruhan proyek. Anda dapat menangkap semua output dengan:

    bersihkan

    buat 2> & 1 | tee result.txt

  • PC-Lint / FlexeLint (menyelesaikan masalah 1 dan 2)

    pastikan menambahkan opsi + e766, peringatan ini tentang: file header yang tidak digunakan.

    pclint / flint -vf ...

    Ini akan menyebabkan output pclint termasuk file header, file header bersarang akan diindentasi dengan tepat.


1

Untuk mengakhiri diskusi ini: c + + preprocessor turing selesai. Ini adalah properti semantik, apakah sebuah menyertakan tidak berguna. Oleh karena itu, mengikuti dari teorema Rice bahwa tidak dapat diputuskan apakah suatu menyertakan berlebihan atau tidak. Tidak BISA ada program, yang (selalu benar) mendeteksi apakah sebuah menyertakan berlebihan.


5
Apakah saya meminta solusi "selalu benar"? Jawaban ini tidak terlalu produktif untuk diskusi.
shoosh

1
Ada banyak posting yang membahas masalah yang harus dihadapi oleh program semacam itu. Posting saya memberikan jawaban yang konklusif dan benar untuk bagian diskusi itu. Dan saya tidak akan menyukainya, jika sebuah program memberi tahu saya, saya dapat dengan aman menghapus #include dan kemudian kode saya tidak dapat dikompilasi lagi. (atau lebih buruk - masih mengkompilasi tetapi melakukan sesuatu yang berbeda). SETIAP program semacam itu menanggung risiko ini.
Algoman

4
Di antara semua SPEKULASI tentang betapa sulitnya itu dan bagaimana Anda MUNGKIN memecahkan satu hambatan atau yang lain, saya memberi Anda satu-satunya jawaban yang benar 100%. Saya merasa kurang sopan untuk mengatakan bahwa ini tidak produktif ...
Algoman

1
Saya ingat bahwa teorema Rice menyatakan "Tidak mungkin ada program yang selalu dapat memeriksa apakah program yang diberikan memecahkan masalah yang berlebihan-termasuk masalah ini". Mungkin ada beberapa program yang memecahkan masalah yang termasuk berlebihan.
Zhe Yang

1
secara pribadi saya menemukan input @ Algoman sangat membantu. membuat saya sadar betapa sulitnya masalah ini.
bogardon


0

Lint PC Perangkat Lunak Gimpel dapat melaporkan kapan file sertakan telah dimasukkan lebih dari satu kali dalam unit kompilasi , tetapi tidak dapat menemukan file sertakan yang tidak diperlukan dalam cara yang Anda cari.

Sunting: Itu bisa. Lihat jawabannya


Apakah Anda yakin akan hal itu? Saya belum pernah menggunakan FlexeLint (sama seperti PCL) dalam beberapa tahun pada kode C ++, tetapi bahkan baru-baru ini pada kode C, saya bisa bersumpah saya melihat beberapa pesan (saya pikir itu kode 766?) Tentang file header yang tidak digunakan. Baru saja diperiksa (v8.0), lihat bagian 11.8.1. manual.
Dan

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.