Phát hiện #incin thừa trong C / C ++?


289

Tôi thường thấy rằng phần tiêu đề của một tập tin ngày càng lớn hơn nhưng nó không bao giờ nhỏ hơn. Trong suốt vòng đời của một lớp tệp nguồn có thể đã di chuyển và được tái cấu trúc và rất có thể có một số ít #includeskhông cần phải ở đó nữa. Để chúng ở đó chỉ kéo dài thời gian biên dịch và thêm các phụ thuộc biên dịch không cần thiết. Cố gắng tìm ra những thứ vẫn cần thiết có thể khá tẻ nhạt.

Có một số loại công cụ có thể phát hiện các chỉ thị #include thừa và đề xuất những công cụ nào tôi có thể gỡ bỏ một cách an toàn không?
Liệu lint làm điều này có thể?



1
Câu hỏi được liên kết dường như chỉ giải quyết vấn đề trên Windows, đặc biệt là sử dụng Visual Studio.
2014

7
Bỏ phiếu để mở lại điều này, vì bản sao là về việc sử dụng Visual Studio, cụ thể.
Drew Dormann

Câu trả lời:


42

Nó không tự động, nhưng doxygen sẽ tạo sơ đồ phụ thuộc cho #includedcác tệp. Bạn sẽ phải xem qua chúng một cách trực quan, nhưng chúng có thể rất hữu ích để có được một bức tranh về những gì đang sử dụng những gì.


5
Đây là một cách tuyệt vời để xem chuỗi .. nhìn thấy A -> B -> C -> D và A -> D ngay lập tức cho thấy sự dư thừa.
Tom

34
@Tom: Đó là một ý tưởng khủng khiếp: Đối với nó Nó không hiển thị nếu những điều đó bao gồm có cần thiết hay không và thứ hai, danh sách bao gồm không nên phụ thuộc vào gián tiếp bao gồm có thể thay đổi trong tương lai (Bao gồm dự phòng thường không như vậy dù sao cũng có vấn đề lớn, nhờ bao gồm các trình bảo vệ và ma thuật trình biên dịch), nhưng trên đó các lớp / hàm thực sự được sử dụng trong tệp (Trình biên dịch của bạn không cần phải trải qua hàng ngàn dòng mã mẫu thậm chí không được khởi tạo)
MikeMB

@albert, bạn có thể bao gồm ảnh chụp màn hình này không, và mô tả ngắn gọn nơi cần nhấp vào đầu ra doxygen?
Gabriel Staples

@GabrielStaples Đây không phải là câu trả lời của tôi, vì vậy tôi không muốn thêm thông tin vào đó. Tôi chỉ sửa chữa liên kết (như nơi lưu trữ mà nó đề cập đến đã dừng / thu giữ được sử dụng).
albert

177

Cppclean của Google (liên kết đến: tải xuống , tài liệu ) có thể tìm thấy một số loại vấn đề về C ++ và giờ đây nó có thể tìm thấy #includes thừa.

Ngoài ra còn có một công cụ dựa trên Clang, bao gồm những gì bạn đang sử dụng , có thể làm điều này. bao gồm những gì bạn đang sử dụng thậm chí có thể đề xuất các khai báo chuyển tiếp (vì vậy bạn không phải #incolee rất nhiều) và tùy ý dọn sạch #inc loại của bạn cho bạn.

Các phiên bản hiện tại của CDT Eclipse cũng có chức năng này được tích hợp sẵn: đi vào menu Nguồn và nhấp vào Sắp xếp bao gồm sẽ sắp xếp thứ tự # bao gồm của bạn, thêm bất kỳ tiêu đề nào mà Eclipse nghĩ rằng bạn đang sử dụng mà không bao gồm trực tiếp chúng và nhận xét bất kỳ tiêu đề nào mà nó không có Tôi nghĩ bạn cần. Tuy nhiên, tính năng này không đáng tin cậy 100%.


2
Nó làm ngay bây giờ. Tôi chỉ bắt đầu sử dụng nó. Xem ghi chú của tôi ở đây. stackoverflow.com/questions/1301850/ Mạnh
Chance

1
Kho lưu trữ cppclean không hoạt động, bây giờ bạn có thể lấy nó ở đây: bitbucket.org/robertmassaioli/cppclean (trang web ban đầu vẫn hữu ích cho một số cách sử dụng ví dụ)
Nick

3
Tôi đã cập nhật liên kết đến một ngã ba cppclean được duy trì: github.com/myint/cppclean
BenC

1
Lưu ý rằng cppclean dường như chỉ tìm thấy chúng trong các tệp tiêu đề, không phải các tệp cpp, từ tài liệu: "Không cần thiết #includes trong các tệp tiêu đề".
Zitrax

1
@wizurd - Tôi đã không theo kịp các phát triển gần đây trong CDT Eclipse, nhưng tôi không nghĩ vậy. iwyu là kỹ lưỡng và tương đối chậm. Phân tích CDT của Eclipse là nhanh (tương tác) và, khi tôi kiểm tra nó, ít chính xác hơn.
Josh Kelley

65

Ngoài ra, hãy kiểm tra bao gồm những gì bạn sử dụng , giải quyết vấn đề tương tự.


6
IMHO câu trả lời này cần nhiều hơn nữa, vì một khi các nút thắt được xử lý, công cụ IWYU của Google sẽ là công cụ dứt khoát cho nhiệm vụ này.
Dan Olson

5
sudo apt-get install iwyu
Andrew Wagner

Có vẻ tuyệt vời - với hai cavaets 1) bản cập nhật cuối tháng 2 năm 2106 2) Bản thân Gogole chỉ sử dụng nó cho C ++, không phải C, mà OP yêu cầu.
Mawg nói rằng phục hồi Monica

Bạn có thể giải thích một chút về cách người dùng nên sử dụng nó? README không rõ lắm về những gì chứa đầu ra của tập lệnh python.
Vua Jester

Tôi đang sử dụng cái này, nhưng nó không phải lúc nào cũng đúng 100%. Có thể 70% lần nó đưa ra những gợi ý chính xác.
InQusitive

25

Vấn đề với việc phát hiện thừa là bao gồm nó không thể chỉ là một trình kiểm tra phụ thuộc loại. Bao gồm không cần thiết là một tệp không cung cấp giá trị gì cho quá trình biên dịch không làm thay đổi mục khác mà các tệp khác phụ thuộc. Có nhiều cách một tệp tiêu đề có thể thay đổi một trình biên dịch, bằng cách xác định một hằng số, xác định lại và / hoặc xóa một macro được sử dụng, thêm một không gian tên làm thay đổi việc tra cứu tên theo cách nào đó. Để phát hiện các mục như không gian tên bạn cần nhiều hơn một bộ xử lý trước, trên thực tế bạn gần như cần một trình biên dịch đầy đủ.

Lint là một công cụ kiểm tra phong cách và chắc chắn sẽ không có khả năng đầy đủ này.

Tôi nghĩ rằng bạn sẽ tìm thấy cách duy nhất để phát hiện một phần thừa là bao gồm xóa, biên dịch và chạy các bộ.


8
Không ai trong số này sẽ là một vấn đề nếu các tập tin bao gồm được đặt ra tốt. Nếu bạn cần bao gồm tệp A trước tệp B, bạn đã làm sai (và tôi đã làm việc với các dự án mà họ đã làm sai).
David Thornley

9
@David, có nhưng điều đó phụ thuộc vào số năm phát triển trước khi bạn thực hiện đúng. Tôi có thể nói rất chắc chắn rằng tỷ lệ xảy ra có lợi cho ngôi nhà, không phải bạn :(
JaredPar

Có, nhưng tôi thường phát hiện ra điều đó khi sửa đổi chương trình và đột nhiên tôi gặp lỗi biên dịch (nếu tôi may mắn) hoặc một lỗi tối nghĩa. Điều đó dường như giữ cho các tệp #incoide trung thực, ít nhất là về lâu dài.
David Thornley

Tôi nói hoàn toàn ngược lại. Tất cả bạn cần là một trình kiểm tra phụ thuộc loại. Nó có thể không được biên dịch sau khi bạn sắp xếp bao gồm, nhưng đây là những vấn đề cần được xử lý.
Benoît

1
@Benoit, sau đó bạn sẽ bỏ qua một lớp các vấn đề biên dịch nhưng thay đổi về mặt ngữ nghĩa của chương trình của bạn. Xem xét cách #define trong một tệp có thể thay đổi nhánh #if trong tệp khác. Xóa một tiêu đề vẫn có thể cho phép điều này biên dịch với các kết quả khác nhau
JaredPar

15

Tôi nghĩ rằng PCLint sẽ làm điều này, nhưng đã được vài năm kể từ khi tôi nhìn vào nó. Bạn có thể kiểm tra xem nó ra.

Tôi đã xem blog này và tác giả đã nói một chút về việc cấu hình PCLint để tìm thấy bao gồm không sử dụng. Có thể là một giá trị.


Tìm tốt Tôi sẽ phải sử dụng nó.
crashmstr

4
Tôi sử dụng PCLint thường xuyên và nó cho tôi biết các tiêu đề không được sử dụng. Tôi cẩn thận để nhận xét tiêu đề #incoide và biên dịch lại để chắc chắn rằng tiêu đề thực sự không được sử dụng ...
Harold Bamford

Cảm ơn đã xác nhận, Harold.
itmatt

5
quá đắt. không phải là một công cụ khả thi cho quần chúng.

7

Các CScout trình duyệt refactoring có thể phát hiện thừa bao gồm các chỉ thị trong C (tiếc là không C ++) mã. Bạn có thể tìm thấy một mô tả về cách thức hoạt động trong này bài báo.


5

Bạn có thể viết một tập lệnh nhanh xóa một lệnh #incoide duy nhất, biên dịch các dự án và ghi lại tên trong #include và tệp đã bị xóa trong trường hợp không xảy ra lỗi biên dịch.

Hãy để nó chạy trong đêm và ngày hôm sau bạn sẽ có một danh sách chính xác 100% bao gồm các tệp bạn có thể xóa.

Đôi khi brute-force chỉ hoạt động :-)


chỉnh sửa: và đôi khi nó không :-). Dưới đây là một chút thông tin từ các bình luận:

  1. Đôi khi bạn có thể loại bỏ hai tệp tiêu đề riêng biệt, nhưng không phải cả hai cùng nhau. Một giải pháp là loại bỏ các tệp tiêu đề trong quá trình chạy và không đưa chúng trở lại. Điều này sẽ tìm thấy một danh sách các tệp bạn có thể xóa một cách an toàn, mặc dù có thể có một giải pháp với nhiều tệp hơn để loại bỏ thuật toán này sẽ không tìm thấy. (đó là một tìm kiếm tham lam trong không gian bao gồm các tệp để loại bỏ. Nó sẽ chỉ tìm thấy tối đa cục bộ)
  2. Có thể có những thay đổi tinh tế trong hành vi nếu bạn có một số macro được định nghĩa lại khác nhau tùy thuộc vào một số #ifdefs. Tôi nghĩ đây là những trường hợp rất hiếm, và Bài kiểm tra đơn vị là một phần của bản dựng nên nắm bắt những thay đổi này.

1
Hãy cẩn thận về điều này - giả sử có hai tệp tiêu đề bao gồm cả định nghĩa về một cái gì đó. Bạn có thể loại bỏ một trong hai, nhưng không phải cả hai. Bạn sẽ cần phải kỹ lưỡng hơn một chút trong cách tiếp cận vũ phu của mình.
Đaminh Rodger

Có thể đây là những gì bạn muốn nói, nhưng một kịch bản loại bỏ một bao gồm duy nhất và để lại lần xóa cuối cùng bao gồm nếu nó được xóa thành công sẽ thực hiện thủ thuật.
Đaminh Rodger

1
Ý kiến ​​tồi. Nếu tệp tiêu đề # xác định BLAH không đổi và tệp tiêu đề khác kiểm tra #ifdef BLAH, xóa tệp tiêu đề đầu tiên vẫn có thể biên dịch thành công nhưng hành vi của bạn đã thay đổi.
Graeme Perrow

1
Điều này cũng có thể gây ra sự cố với các tiêu đề hệ thống, vì các triển khai khác nhau có thể có những thứ khác nhau được bao gồm trong #include <vector>. Ngay cả khi bạn dính vào một trình biên dịch, các tiêu đề có thể thay đổi trên các phiên bản khác nhau.
David Thornley

2
Điều này sẽ không tìm thấy trường hợp bạn bao gồm một tiêu đề bao gồm tiêu đề mà bạn thực sự cần.
bk1e

5

Xin lỗi để (đăng lại) bài đăng ở đây, mọi người thường không mở rộng bình luận.

Kiểm tra bình luận của tôi để crashmstr, FlexeLint / PC-Lint sẽ làm điều này cho bạn. Thông báo thông tin 766. Mục 11.8.1 trong hướng dẫn của tôi (phiên bản 8.0) thảo luận về điều này.

Ngoài ra, và điều này rất quan trọng, hãy tiếp tục lặp đi lặp lại cho đến khi tin nhắn biến mất . Nói cách khác, sau khi xóa các tiêu đề không được sử dụng, chạy lại lint, nhiều tệp tiêu đề hơn có thể đã trở thành "không cần thiết" khi bạn xóa một số tiêu đề không cần thiết. (Điều đó nghe có vẻ ngớ ngẩn, đọc chậm và phân tích nó, nó có ý nghĩa.)


Tôi biết chính xác ý của bạn và phản ứng của tôi là "Ewwww". Tôi ghét mã như thế.
David Thornley

5

Tôi chưa bao giờ tìm thấy một công cụ chính thức để thực hiện những gì bạn yêu cầu. Thứ gần nhất tôi đã sử dụng là Bao gồm trình quản lý , biểu đồ cây bao gồm tiêu đề của bạn để bạn có thể phát hiện trực quan những thứ như các tiêu đề chỉ có trong một tệp và các vùi tiêu đề tròn.


4

Tôi đã thử sử dụng Flexelint (phiên bản unix của PC-Lint) và có kết quả hơi hỗn hợp. Điều này có thể là do tôi đang làm việc trên một cơ sở mã rất lớn và có nhiều nút. Tôi khuyên bạn nên kiểm tra cẩn thận từng tệp được báo cáo là không sử dụng.

Lo lắng chính là dương tính giả. Nhiều bao gồm cùng một tiêu đề được báo cáo là một tiêu đề không cần thiết. Điều này thật tệ vì Flexelint không cho bạn biết dòng tiêu đề được bao gồm trên hoặc nơi nó được bao gồm trước đó.

Một trong những cách mà các công cụ tự động có thể hiểu sai:

Trong A.hpp:

class A { 
  // ...
};

Trong B.hpp:

#include "A.hpp

class B {
    public:
        A foo;
};

Trong 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

Nếu bạn mù quáng theo dõi các tin nhắn từ Flexelint, bạn sẽ làm hỏng # phụ thuộc của bạn. Có nhiều trường hợp bệnh lý hơn, nhưng về cơ bản, bạn sẽ cần phải tự kiểm tra các tiêu đề để có kết quả tốt nhất.

Tôi đánh giá cao bài viết này về Cấu trúc vật lý và C ++ từ blog Trò chơi từ bên trong. Họ đề xuất một cách tiếp cận toàn diện để dọn dẹp mớ hỗn độn #incoide:

Hướng dẫn

Đây là một bộ hướng dẫn được chắt lọc từ cuốn sách của Lakos nhằm giảm thiểu số lượng phụ thuộc vật lý giữa các tệp. Tôi đã sử dụng chúng trong nhiều năm và tôi luôn thực sự hài lòng với kết quả.

  1. Mỗi tệp cpp bao gồm tệp tiêu đề của riêng nó đầu tiên. [bắn tỉa]
  2. Một tệp tiêu đề phải bao gồm tất cả các tệp tiêu đề cần thiết để phân tích nó. [bắn tỉa]
  3. Một tệp tiêu đề nên có số lượng tệp tiêu đề tối thiểu cần thiết để phân tích nó. [bắn tỉa]

Cuốn sách của Lakos rất tốt cho giáo dục - ngoài những quan sát lỗi thời của ông về công nghệ biên dịch.
Tom

4

Nếu bạn đang sử dụng CDT của Eclipse, bạn có thể thử http://includator.com miễn phí cho những người thử nghiệm beta (tại thời điểm viết bài này) và tự động xóa #includes thừa hoặc thêm những cái còn thiếu. Đối với những người dùng có FlexeLint hoặc PC-Lint và đang sử dụng Elicpse CDT, http://linticator.com có thể là một tùy chọn (cũng miễn phí cho bản thử nghiệm beta). Mặc dù nó sử dụng phân tích của Lint, nhưng nó cung cấp các bản sửa lỗi nhanh để tự động xóa các câu lệnh #include thừa.


Lý do cho điều đó là bộ phận kế toán của chúng tôi không thể lập hóa đơn số tiền ít hơn. Nếu bạn đếm thời gian bạn có thể tiết kiệm thì đó không phải là điều vô lý. Một khi, chúng tôi có khả năng nhận thanh toán bằng thẻ tín dụng, chúng tôi có thể hạ giá đáng kể. Một lựa chọn khác sẽ là một nhà tài trợ cho những nỗ lực phát triển của chúng tôi. Mô hình tài chính của chúng tôi đòi hỏi chúng tôi phải đạt được lợi nhuận để tài trợ cho công việc nghiên cứu của mình. Tôi sẽ rất vui khi bán giấy phép rẻ hơn nhiều, nhưng không thể. Có thể chúng tôi sẽ đóng góp nó cho CDT và bạn nhận được nó miễn phí, nhưng tôi phải tài trợ bằng cách nào đó. Tôi quên mất, bạn có thể thử miễn phí!
PeterSom

2

Bài viết này giải thích một kỹ thuật loại bỏ #incoide bằng cách sử dụng phân tích cú pháp Doxygen. Đó chỉ là một tập lệnh perl, vì vậy nó khá dễ sử dụng.


1
Kịch bản tìm thấy một số bao gồm để loại bỏ nhưng nó cũng cung cấp rất nhiều bao gồm không thể xóa được. Có vẻ như nó không hỗ trợ lớp enum, dường như nó cũng có một thời gian tồi tệ với macro và đôi khi với không gian tên.
Baptiste Wicht



1

Có hai loại tệp #incoide thừa:

  1. Một tệp tiêu đề thực sự không cần thiết cho mô-đun (.c, .cpp)
  2. Một tệp tiêu đề là cần thiết bởi mô-đun nhưng được bao gồm nhiều lần, trực tiếp hoặc gián tiếp.

Có 2 cách theo kinh nghiệm của tôi hoạt động tốt để phát hiện ra nó:

  • gcc -H hoặc cl.exe / showincludes (giải quyết vấn đề 2)

    Trong thế giới thực, bạn có thể xuất CFLAGS = -H trước khi thực hiện, nếu tất cả các Makefile không ghi đè các tùy chọn CFLAGS. Hoặc như tôi đã sử dụng, bạn có thể tạo một trình bao bọc cc / g ++ để thêm các tùy chọn -H buộc vào mỗi lệnh gọi $ (CC) và $ (CXX). và thêm thư mục của trình bao bọc vào biến $ PATH, thay vào đó, make của bạn sẽ sử dụng lệnh trình bao bọc thay thế. Tất nhiên trình bao bọc của bạn sẽ gọi trình biên dịch gcc thực sự. Thủ thuật này cần thay đổi nếu Makefile của bạn sử dụng gcc trực tiếp. thay vì $ (CC) hoặc $ (CXX) hoặc theo quy tắc ngụ ý.

    Bạn cũng có thể biên dịch một tệp bằng cách điều chỉnh với dòng lệnh. Nhưng nếu bạn muốn làm sạch tiêu đề cho toàn bộ dự án. Bạn có thể nắm bắt tất cả đầu ra bằng cách:

    Làm sạch

    thực hiện 2> & 1 | kết quả tee

  • PC-Lint / FlexeLint (giải quyết vấn đề cả 1 và 2)

    đảm bảo thêm các tùy chọn + e766, cảnh báo này là về: các tệp tiêu đề không được sử dụng.

    pclint / đá lửa -vf ...

    Điều này sẽ gây ra các tệp tiêu đề bao gồm đầu ra pclint, các tệp tiêu đề lồng nhau sẽ được thụt lề một cách thích hợp.


1

Để kết thúc cuộc thảo luận này: bộ tiền xử lý c ++ đã hoàn tất. Nó là một tài sản ngữ nghĩa, cho dù bao gồm là không cần thiết. Do đó, theo định lý của Rice rằng không thể xác định được liệu bao gồm có thừa hay không. Không thể có một chương trình, mà (luôn luôn chính xác) phát hiện xem một bao gồm có thừa hay không.


5
Tôi đã yêu cầu một giải pháp "luôn luôn đúng"? Câu trả lời này không hiệu quả cho cuộc thảo luận.
shoosh

1
Vâng, đã có rất nhiều bài viết thảo luận về các vấn đề mà một chương trình như vậy sẽ phải giải quyết. Bài viết của tôi đưa ra một câu trả lời kết luận và chính xác cho phần đó của cuộc thảo luận. Và tôi vì một người sẽ không thích nó, nếu một chương trình nói với tôi, tôi có thể xóa #incolee một cách an toàn và sau đó mã của tôi không biên dịch nữa. (hoặc tệ hơn - vẫn biên dịch nhưng làm một cái gì đó khác nhau). BẤT K program chương trình như vậy chịu rủi ro này.
Algoman

4
Giữa tất cả các ĐẶC BIỆT về mức độ khó của nó và cách bạn NHIỀU giải quyết một trở ngại này hay trở ngại khác, tôi đã cho bạn câu trả lời đúng 100%. Tôi thấy khá bất lịch sự khi nói rằng điều này không hiệu quả ...
Algoman

1
Tôi nhớ rằng định lý của Rice nói rằng "Không thể có một chương trình luôn có thể kiểm tra xem một chương trình nhất định có giải quyết được vấn đề bao gồm thừa này không". Có thể có một vài chương trình giải quyết vấn đề bao gồm thừa.
Zhe Yang

1
cá nhân tôi thấy đầu vào của @ Algoman rất hữu ích. làm cho tôi nhận ra vấn đề này khó khăn như thế nào.
bogardon

1

Dưới đây là một cách đơn giản để xác định tiêu đề thừa bao gồm . Nó không hoàn hảo nhưng loại bỏ "rõ ràng" không cần thiết bao gồm. Loại bỏ những thứ này đi một chặng đường dài trong việc làm sạch mã.

Các tập lệnh có thể được truy cập trực tiếp trên GitHub.


0

PC Lint của Gimpel Software có thể báo cáo khi một tệp bao gồm đã được đưa vào nhiều lần trong một đơn vị biên dịch , nhưng nó không thể tìm thấy các tệp không cần thiết theo cách bạn đang tìm kiếm.

Chỉnh sửa: Nó có thể. Xem câu trả lời của nó


Bạn có chắc chắn về điều đó? Tôi đã không sử dụng FlexeLint (giống như PCL) trong một vài năm về mã C ++, nhưng ngay cả gần đây trên mã C, tôi có thể thề rằng tôi đã thấy một vài thông báo (tôi nghĩ đó là mã 766?) Về các tệp tiêu đề không được sử dụng. Chỉ cần kiểm tra (v8.0), xem phần 11.8.1. hướng dẫn sử dụng.
Dan

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.