Wykrywanie zbędnego # zawiera w C / C ++?

289

Często stwierdzam, że sekcja nagłówków pliku stale się powiększa, ale nigdy się nie zmniejsza. Przez cały okres istnienia pliku źródłowego klasy mogły się przenosić i były refaktoryzowane, i jest bardzo możliwe, że istnieje wiele takich #includes, które nie muszą już tam być. Pozostawienie ich tam tylko przedłuża czas kompilacji i dodaje niepotrzebne zależności kompilacji. Próba ustalenia, które są nadal potrzebne, może być dość nużąca.

Czy istnieje jakieś narzędzie, które może wykrywać zbędne dyrektywy #include i sugerować, które mogę bezpiecznie usunąć?
Czy może to robić?

buta
źródło
1
Zobacz także: stackoverflow.com/questions/74326/…
Eclipse
1
Wydaje się, że połączone pytanie dotyczy tylko tego problemu w systemie Windows, szczególnie w programie Visual Studio.
D'Nabre,
7
Głosowanie w celu ponownego otwarcia tego, ponieważ duplikat dotyczy w szczególności programu Visual Studio.
Drew Dormann

Odpowiedzi:

42

Nie jest to automatyczne, ale doxygen wytworzy diagramy zależności dla #includedplików. Będziesz musiał przejrzeć je wizualnie, ale mogą one być bardzo przydatne do uzyskania obrazu tego, czego używa.

Albert
źródło
5
Jest to świetny sposób, aby zobaczyć łańcuchy. Widzenie A -> B -> C -> D i A -> D natychmiast ujawnia redundancję.
Tom
34
@Tom: To okropny pomysł: dla jednego Nie pokazuje, czy te dołączenia są potrzebne, czy nie, a po drugie, lista włączeń nie powinna zależeć od pośrednich włączeń, które mogą ulec zmianie w przyszłości (Nadmiarowe dołączenia zwykle nie są takie Dużym problemem w każdym razie, dzięki m.in. strażników i kompilator magii), lecz na które klasy / funkcje są rzeczywiście używane w pliku (kompilator nie powinien przejść przez tysiące linii kodu szablonu, że nawet nie dostać instancja)
MikeMB
@albert, czy możesz dołączyć zrzuty ekranu z tego i krótko opisać, gdzie należy kliknąć wyjście doksygen?
Gabriel Staples
@GabrielStaples To nie jest moja odpowiedź, więc nie chcę dodawać do niej informacji. Poprawiłem tylko link (jako miejsce hostingu, do którego się odwoływał, zatrzymane / zajęte).
Albert
177

Google cppclean (linki do: pobierania , dokumentacji ) może znaleźć kilka kategorii problemów w C ++, a teraz może znaleźć zbędne #includes.

Istnieje również narzędzie oparte na Clang, które zawiera to, czego używasz , które może to zrobić. include-what-you-use może nawet sugerować przekazywanie deklaracji (więc nie musisz #include tak dużo) i opcjonalnie wyczyść #include dla ciebie.

Obecne wersje Eclipse CDT mają również wbudowaną tę funkcję: przejście do menu Źródło i kliknięcie opcji Organizuj dołączenia spowoduje alfabetyczność twoich # włączników, doda wszystkie nagłówki, które według Eclipse są używane bez ich bezpośredniego uwzględnienia, i skomentuje wszystkie nagłówki, których nie robi nie sądzę, że potrzebujesz. Ta funkcja nie jest jednak w 100% niezawodna.

Josh Kelley
źródło
2
To robi teraz. Właśnie zaczynam go używać. Zobacz moją notatkę tutaj. stackoverflow.com/questions/1301850/…
Szansa
1
Repozytorium cppclean jest wyłączone, możesz je teraz pobrać tutaj: bitbucket.org/robertmassaioli/cppclean (oryginalna strona jest nadal przydatna w niektórych przykładowych zastosowaniach)
Nick
3
Zaktualizowałem link do utrzymanego widelca cppclean
BenC
1
Zauważ, że cppclean wydaje się znajdować je tylko w plikach nagłówkowych, a nie w plikach CPP z dokumentu: „Niepotrzebne # zawiera w plikach nagłówkowych”.
Zitrax,
1
@wizurd - Nie nadążałem za najnowszymi osiągnięciami w Eclipse CDT, ale nie sądzę. iwyu jest dokładny i stosunkowo wolny. Analiza Eclipse CDT jest szybka (interaktywna), a kiedy ją przetestowałem, mniej dokładna.
Josh Kelley,
65

Sprawdź także opcję „włącz to, czego używasz” , która rozwiązuje podobny problem.

Tzafrir
źródło
6
IMHO ta odpowiedź wymaga znacznie więcej pozytywnych opinii, ponieważ po opracowaniu załamań narzędzie Google IWYU będzie ostatecznym narzędziem do tego zadania.
Dan Olson,
5
sudo apt-get install iwyu
Andrew Wagner
Wydaje się świetny - z dwoma cavaets 1) ostatnia aktualizacja 2106 lutego 2) Sami Gogole używają go tylko dla C ++, a nie C, o co poprosił OP.
Mawg mówi o przywróceniu Moniki
Czy możesz wyjaśnić trochę, w jaki sposób użytkownik powinien z niego korzystać? Plik README nie jest zbyt jasny o tym, co zawiera dane wyjściowe skryptu python.
Błazen króla,
Korzystam z tego, ale nie zawsze jest to w 100% poprawne. Może 70% razy daje prawidłowe sugestie.
Niewymagany
25

Problem z wykrywaniem zbędnych treści polega na tym, że nie może to być tylko sprawdzanie zależności typu. Zbędne dołączanie to plik, który nie zapewnia nic wartościowego w kompilacji i nie zmienia innego elementu, od którego zależą inne pliki. Plik nagłówka może zmienić kompilację na wiele sposobów, na przykład poprzez zdefiniowanie stałej, ponowne zdefiniowanie i / lub usunięcie używanego makra, dodanie przestrzeni nazw, która zmienia sposób wyszukiwania nazwy w pewnym stopniu wzdłuż linii. Aby wykryć elementy takie jak przestrzeń nazw, potrzebujesz znacznie więcej niż preprocesora, w rzeczywistości potrzebujesz prawie pełnego kompilatora.

Lint jest raczej sprawdzianem stylu i na pewno nie będzie miał takich możliwości.

Myślę, że jedynym sposobem na wykrycie zbędnego włączenia jest usunięcie, kompilacja i uruchomienie pakietów.

JaredPar
źródło
8
Nic z tego nie będzie stanowiło problemu, jeśli pliki dołączeń są dobrze ułożone. Jeśli kiedykolwiek musisz dołączyć plik A przed plikiem B, robisz to źle (a ja pracowałem nad projektami, w których źle to zrobili).
David Thornley,
9
@David, tak, ale to zależy od lat deweloperów, zanim zrobisz to poprawnie. Mogę powiedzieć z wielką pewnością, że szanse na to wydarzenie sprzyjają domowi, a nie tobie :(
JaredPar,
Tak, ale generalnie dowiaduję się o tym podczas modyfikowania programu, i nagle mam błąd kompilacji (jeśli mam szczęście) lub niejasny błąd. Wydaje się, że pozwala to zachować uczciwość plików #include, przynajmniej na dłuższą metę.
David Thornley,
Powiedziałbym dokładnie przeciwnie. Wszystko czego potrzebujesz to sprawdzanie zależności typu. Może się nie kompilować po odpowiednim skonfigurowaniu dołączeń, ale są to problemy, które należy rozwiązać.
Benoît
1
@Benoit, zignorowałbyś klasę problemów, które się kompilują, ale semantycznie zmieniają znaczenie twojego programu. Zastanów się, jak #define w jednym pliku może zmienić gałąź #if w innym. Usunięcie nagłówka nadal pozwala na kompilację z różnymi wynikami
JaredPar
15

Myślałem, że PCLint to zrobi, ale minęło kilka lat, odkąd na to spojrzałem. Możesz to sprawdzić.

Spojrzałem na tego bloga, a autor mówił trochę o konfigurowaniu PCLint, aby znaleźć nieużywane załączniki. Może warto rzucić na to okiem.

itsmatt
źródło
Dobre znalezisko! Będę musiał tego użyć.
crashmstr
4
Regularnie używam PCLint i mówi mi o nieużywanych nagłówkach. Jestem ostrożny zakomentuj #include nagłówka i ponownie skompilować, aby mieć pewność, że nagłówek jest naprawdę nieużywany ...
Harold Bamford
Dzięki za potwierdzenie, Harold.
itsmatt
5
za drogie. nie jest realnym narzędziem dla mas.
7

CScout przeglądarka refaktoring może wykryć zbędny obejmują dyrektyw w C (niestety nie C ++) kodu. Opis tego, jak to działa, można znaleźć w tym artykule w czasopiśmie.

Diomidis Spinellis
źródło
5

Możesz napisać szybki skrypt, który usuwa pojedynczą dyrektywę #include, kompiluje projekty i zapisuje nazwę w #include oraz plik, z którego został usunięty, na wypadek, gdyby nie wystąpiły błędy kompilacji.

Niech uruchomi się w nocy, a następnego dnia będziesz mieć 100% poprawną listę plików dołączanych, które możesz usunąć.

Czasami brutalna siła po prostu działa :-)


edycja: a czasami nie :-). Oto trochę informacji z komentarzy:

  1. Czasami możesz usunąć dwa pliki nagłówka osobno, ale nie oba jednocześnie. Rozwiązaniem jest usunięcie plików nagłówkowych podczas uruchamiania i nie przywracanie ich. Znajduje się tutaj lista plików, które można bezpiecznie usunąć, chociaż może istnieć rozwiązanie z większą liczbą plików do usunięcia, których ten algorytm nie znajdzie. (jest to chciwe wyszukiwanie w obszarze plików dołączanych do usunięcia. Znajduje tylko lokalne maksimum)
  2. Mogą wystąpić subtelne zmiany w zachowaniu, jeśli niektóre makra zostaną zdefiniowane inaczej w zależności od niektórych #ifdefs. Myślę, że są to bardzo rzadkie przypadki, a testy jednostkowe, które są częścią kompilacji, powinny wychwycić te zmiany.
Gilad Naor
źródło
1
Uważaj na to - powiedzmy, że istnieją dwa pliki nagłówkowe, które zawierają definicję czegoś. Możesz usunąć oba, ale nie oba. Musisz być bardziej dokładny w podejściu brutalnej siły.
Dominic Rodger,
Być może o to ci chodziło, ale skrypt, który usuwa pojedynczy dołączenie i pozostawia ostatni usunięty załącznik, jeśli został pomyślnie usunięty, załatwi sprawę.
Dominic Rodger,
1
Kiepski pomysł. Jeśli plik nagłówka # definiuje stały BLAH, a inny plik nagłówkowy sprawdza #ifdef BLAH, usunięcie pierwszego pliku nagłówka może nadal się pomyślnie kompilować, ale Twoje zachowanie się zmieniło.
Graeme Perrow,
1
Może to również powodować problemy z nagłówkami systemu, ponieważ różne implementacje mogą zawierać różne rzeczy zawarte w #include <vector>. Nawet jeśli trzymasz się jednego kompilatora, nagłówki mogą zmieniać się w różnych wersjach.
David Thornley,
2
Nie znajdzie to przypadków, w których dołączasz nagłówek zawierający nagłówek, którego naprawdę potrzebujesz.
bk1e
5

Przykro mi, że (ponownie) publikuję tutaj, ludzie często nie rozwijają komentarzy.

Sprawdź mój komentarz do crashmstr, FlexeLint / PC-Lint zrobi to za Ciebie. Komunikat informacyjny 766. Sekcja 11.8.1 mojej instrukcji (wersja 8.0) omawia to.

Jest to również ważne i powtarzaj, dopóki komunikat nie zniknie . Innymi słowy, po usunięciu nieużywanych nagłówków, uruchom ponownie włókna, więcej plików nagłówków może stać się „niepotrzebnych” po usunięciu niektórych niepotrzebnych nagłówków. (Może to zabrzmieć głupio, przeczytaj go powoli i przeanalizuj, ma to sens).

Dan
źródło
Wiem dokładnie, co masz na myśli, a moją reakcją było „Ewwww”. Nienawidzę takiego kodu.
David Thornley,
5

Nigdy nie znalazłem pełnoprawnego narzędzia, które spełni Twoje oczekiwania. Najbliższą rzeczą, której użyłem, jest IncludeManager , który przedstawia wykres drzewa włączenia nagłówka, dzięki czemu można wizualnie dostrzec takie rzeczy, jak nagłówki zawarte tylko w jednym pliku i okrągłe włączenia nagłówków.

Dan Olson
źródło
4

Próbowałem użyć Flexelint (uniksowa wersja PC-Lint) i uzyskałem nieco mieszane wyniki. Jest to prawdopodobne, ponieważ pracuję na bardzo dużej i zawiłej bazie kodu. Zalecam uważne sprawdzenie każdego pliku zgłoszonego jako nieużywany.

Głównym zmartwieniem są fałszywe alarmy. Wiele włączeń tego samego nagłówka jest zgłaszanych jako niepotrzebny nagłówek. Jest to złe, ponieważ Flexelint nie mówi ci, w którym wierszu jest zawarty nagłówek ani gdzie był zawarty wcześniej.

Jeden ze sposobów, w jaki zautomatyzowane narzędzia mogą to zrobić źle:

W A.hpp:

class A { 
  // ...
};

W B.hpp:

#include "A.hpp

class B {
    public:
        A foo;
};

W 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

Jeśli ślepo podążasz za wiadomościami od Flexelint, zepsujesz swoje zależności #include. Jest więcej przypadków patologicznych, ale w zasadzie będziesz musiał sam sprawdzić nagłówki, aby uzyskać najlepsze wyniki.

Bardzo polecam ten artykuł na temat struktury fizycznej i C ++ z blogu Games od wewnątrz. Zalecają kompleksowe podejście do usuwania bałaganu #include:

Wytyczne

Oto destylowany zestaw wytycznych z książki Lakos, które minimalizują liczbę fizycznych zależności między plikami. Używam ich od lat i zawsze byłem bardzo zadowolony z rezultatów.

  1. Każdy plik CPP zawiera najpierw własny plik nagłówka. [fantastyczna okazja]
  2. Plik nagłówka musi zawierać wszystkie pliki nagłówka niezbędne do jego przetworzenia. [fantastyczna okazja]
  3. Plik nagłówkowy powinien mieć minimalną liczbę plików nagłówkowych niezbędnych do jego przetworzenia. [fantastyczna okazja]
Ben Martin
źródło
Książka Lakosa świetnie nadaje się do celów edukacyjnych - poza jego nieaktualnymi spostrzeżeniami na temat technologii kompilatora.
Tom
4

Jeśli używasz Eclipse CDT, możesz wypróbować http://includator.com, który jest bezpłatny dla testerów wersji beta (w momencie pisania tego tekstu) i automatycznie usuwa zbędne #include lub dodaje brakujące. Dla tych użytkowników, którzy mają FlexeLint lub PC-Lint i używają Elicpse CDT, http://linticator.com może być opcją (również bezpłatną w wersji beta). Chociaż wykorzystuje analizę Linta, zapewnia szybkie poprawki do automatycznego usuwania zbędnych instrukcji #include.

PeterSom
źródło
Powodem tego jest to, że nasz dział księgowości nie jest w stanie wystawiać faktur na mniejsze kwoty. Jeśli policzysz czas, który możesz zaoszczędzić, nie jest to zbyt nierozsądne. Kiedyś mamy możliwość otrzymywania płatności kartą kredytową, możemy znacznie obniżyć cenę. Inną opcją byłby sponsor naszych wysiłków rozwojowych. Nasz model finansowania wymaga od nas zysków w celu sfinansowania naszej pracy badawczej. Byłbym szczęśliwy sprzedając licencje znacznie taniej, ale nie mogę. Być może pomożemy to w CDT, a ty dostaniesz go za darmo, ale muszę to jakoś sfinansować. Zapomniałem, możesz spróbować za darmo!
PeterSom,
2

W tym artykule wyjaśniono technikę #include usuwania przy użyciu parsowania Doxygen. To tylko skrypt perla, więc jest dość łatwy w użyciu.

Steve Gury
źródło
1
Skrypt znajduje niektóre dołączenia do usunięcia, ale daje także wiele załączników, których nie można usunąć. Wygląda na to, że nie obsługuje wyliczania klas, wydaje się też, że źle się dzieje z makrem, a czasem z przestrzenią nazw.
Baptiste Wicht
1

Istnieją dwa rodzaje zbędnych plików #include:

  1. Plik nagłówkowy faktycznie nie jest potrzebny modułowi (.c, .cpp)
  2. Plik nagłówkowy jest potrzebny modułowi, ale jest dołączany więcej niż raz, bezpośrednio lub pośrednio.

W moim doświadczeniu są dwa sposoby, które dobrze go wykrywają:

  • gcc -H lub cl.exe / showincludes (rozwiązać problem 2)

    W prawdziwym świecie możesz wyeksportować CFLAGS = -H przed marką, jeśli wszystkie pliki Makefile nie zastępują opcji CFLAGS. Lub, jak użyłem, możesz utworzyć opakowanie cc / g ++, aby wymusić dodanie opcji -H do każdego wywołania $ (CC) i $ (CXX). i dodaj katalog otoki do zmiennej $ PATH, wtedy twój make użyje zamiast tego polecenia otoki. Oczywiście twoje opakowanie powinno wywoływać prawdziwy kompilator gcc. Te sztuczki należy zmienić, jeśli Twój Makefile używa bezpośrednio gcc. zamiast $ (CC) lub $ (CXX) lub według zasad dorozumianych.

    Możesz także skompilować pojedynczy plik, dostosowując go za pomocą wiersza poleceń. Ale jeśli chcesz wyczyścić nagłówki dla całego projektu. Możesz przechwycić wszystkie dane wyjściowe poprzez:

    oczyścić

    zrób 2> i 1 | tee wynik.txt

  • PC-Lint / FlexeLint (rozwiązać problem zarówno 1, jak i 2)

    pamiętaj, aby dodać opcje + e766, to ostrzeżenie dotyczy: nieużywanych plików nagłówka.

    pclint / flint -vf ...

    Spowoduje to, że pliki nagłówkowe pclint będą zawierać pliki nagłówkowe, zagnieżdżone pliki nagłówkowe zostaną odpowiednio wcięte.

zhaorufei
źródło
1

Na zakończenie tej dyskusji: preprocesor c ++ jest w pełni gotowy. Jest to właściwość semantyczna, niezależnie od tego, czy dołączenie jest zbędne. Dlatego z twierdzenia Rice'a wynika, że ​​nie można rozstrzygnąć, czy dołączenie jest zbędne, czy nie. Nie może istnieć program, który (zawsze poprawnie) wykrywa, czy dołączenie jest zbyteczne.

Algoman
źródło
5
Czy poprosiłem o „zawsze poprawne” rozwiązanie? Ta odpowiedź nie jest zbyt produktywna w dyskusji.
shoosh,
1
Cóż, pojawiło się wiele postów omawiających problemy, z którymi taki program musiałby sobie poradzić. Mój post zawiera jednoznaczną i poprawną odpowiedź na tę część dyskusji. I na pewno by mi się to nie podobało, gdyby jakiś program powiedział mi, że mogę bezpiecznie usunąć #include, a wtedy mój kod już się nie kompiluje. (lub gorzej - nadal się kompiluje, ale robi coś inaczej). ŻADNY program taki niesie takie ryzyko.
Algoman
4
Pomiędzy wszystkimi spekulacjami na temat tego, jak trudne byłoby to zadanie i jak MOŻESZ rozwiązać jedną lub drugą przeszkodę, podałem jedyną 100% poprawną odpowiedź. Uważam, że bezczelne jest twierdzenie, że to nie było produktywne ...
Algoman,
1
Przypomniałem sobie, że twierdzenie Rice'a głosi: „Nie może istnieć program, który zawsze może sprawdzić, czy dany program rozwiązuje ten zbędny problem”. Może istnieć kilka programów, które rozwiązują problem zbędnego programu.
Zhe Yang,
1
osobiście uważam, że wkład @ Algomana był bardzo pomocny. uświadamiam sobie, jak trudny jest ten problem.
bogardon
0

Komputer PC Lint firmy Gimpel Software może raportować, kiedy plik dołączania został dołączony więcej niż raz w jednostce kompilacyjnej , ale nie może znaleźć plików dołączonych, które nie są potrzebne w sposób, którego szukasz.

Edycja: może. Zobacz odpowiedź itsmatt

crashmstr
źródło
Jesteś tego pewien ? Nie używałem FlexeLint (tak samo jak PCL) od kilku lat w kodzie C ++, ale nawet ostatnio w kodzie C, mógłbym przysiąc, że widziałem kilka komunikatów (myślę, że to kod 766?) O nieużywanych plikach nagłówka. Właśnie sprawdzone (wersja 8.0), patrz sekcja 11.8.1. instrukcji obsługi.
Dan
0

CLion , C / C ++ IDE od JetBrains, wykrywa zbędne dołączanie po wyjęciu z pudełka. Są one wyszarzone w edytorze, ale istnieją również funkcje optymalizujące zawarte w bieżącym pliku lub całym projekcie .

Odkryłem jednak, że płacisz za tę funkcjonalność; CLion potrzebuje trochę czasu, aby zeskanować i przeanalizować projekt po pierwszym załadowaniu.

congusbongus
źródło