2011-03-04 23 views
7

Tôi biết tại sao bao gồm bảo vệ tồn tại, và rằng #pragma once không phải là tiêu chuẩn và do đó không được hỗ trợ bởi tất cả các trình biên dịch, vvCó bất kỳ tình huống nào mà bạn không muốn bao gồm nhân viên bảo vệ không?

Câu hỏi của tôi là của một loại khác nhau:

Có bất kỳ lý do hợp lý để bao giờ không có họ ? Tôi chưa đi qua một tình huống mà về mặt lý thuyết, sẽ có bất kỳ lợi ích của việc không cung cấp bao gồm bảo vệ trong một tập tin đó là có nghĩa là để được bao gồm ở một nơi khác. Có ai có một ví dụ mà có một lợi ích thực tế của việc không có chúng?

Lý do tôi hỏi - với tôi chúng có vẻ khá thừa, vì bạn luôn sử dụng chúng và hành vi của #pragma once cũng có thể được tự động áp dụng cho mọi thứ.

Trả lời

8

Tôi đã thấy các tiêu đề tạo mã tùy thuộc vào các macro được xác định trước khi đưa vào. Trong trường hợp này đôi khi nó muốn xác định các macro đó thành một (các) giá trị), bao gồm tiêu đề, xác định lại các macro và bao gồm lại.
Mọi người thấy như vậy đồng ý rằng nó xấu xí và tốt nhất nên tránh, nhưng đôi khi (giống như mã trong các tiêu đề đã nói được tạo ra bởi một số phương tiện khác) đó là điều xấu xa hơn để làm điều đó.

Ngoài ra, tôi không thể nghĩ ra lý do.

+0

Tôi đã sử dụng đặc biệt này trong thư viện FFTW (đây là từ trước đây, có thể bây giờ nó đã thay đổi). Một số hàm được định nghĩa sao cho chúng có thể được tạo cho các kiểu cơ bản khác nhau: in, double, float, v.v., do đó bạn có thể định nghĩa bao nhiêu kiểu tùy ý và chỉ cần gộp lại tệp đó. Tuy nhiên, đây là thư viện C. Trong C++ tất nhiên chúng ta sẽ sử dụng các mẫu ... –

+0

@the_mandrill: Trong C++, điều này thực sự được sử dụng trong một số tình huống mà các mẫu không phù hợp với vấn đề. Đặc biệt với thư viện tiền xử lý tăng để tự động tạo cùng một mã với một số đối số khác nhau, như trong 'boost :: bind' với 0, 1, 2 ... N đối số. –

+0

@David Rodríguez: Theo như tôi hiểu nó, boost :: bind được thực hiện bằng cách cung cấp các mẫu thích hợp cho tối đa 9 đối số. Chỉnh sửa: Ok Tôi tìm thấy một nơi mà tăng nó để tránh 'trở lại x' tình hình với các chức năng với các đối số mẫu như trả về giá trị (khi loại trả về là void), nhưng tôi đã luôn luôn quản lý để giải quyết những với một chuyên ngành mẫu cụ thể cho loại 'void'. Họ đi tuyến đường mà không bao gồm bảo vệ, nhưng nó không phải là cần thiết để xác định lại một vĩ mô 'return' để giải quyết vấn đề này. – Mephane

0

Có thể có vấn đề nếu bạn có hai tiêu đề trong dự án sử dụng cùng một trình bảo vệ, ví dụ: nếu bạn có hai thư viện của bên thứ ba và cả hai đều có tiêu đề sử dụng biểu tượng bảo vệ bao gồm __CONSTANTS_H__ thì bạn sẽ không thể thành công cả hai tiêu đề trong một đơn vị biên dịch nhất định. Một giải pháp tốt hơn là #pragma once, nhưng một số trình biên dịch cũ hơn không hỗ trợ điều này.

+2

Hơn nữa, '#pragma once' ___ không chuẩn ___ và không đảm bảo phiên bản tiếp theo của trình biên dịch sẽ vẫn hỗ trợ nó, hoặc hỗ trợ cùng ngữ nghĩa. Đối với tôi, đó là một lý do rất tốt để không sử dụng nó. – sbi

+1

@ sbi: đúng - như thường lệ, trường hợp đó là sự cân bằng giữa tính di động và các vấn đề như mô tả ở trên. Tôi đoán bạn luôn có thể làm nổi bật một số bản in thực sự xấu xí nhờ đó bạn kiểm tra phiên bản trình biên dịch và biên dịch và sau đó sử dụng hoặc bao gồm cả bảo vệ hoặc '#pragma once' cho phù hợp, nhưng tôi không chắc tôi muốn thấy điều đó trong mọi tiêu đề. –

+0

Vâng, nếu hai bao gồm bảo vệ va chạm, câu trả lời là để thay đổi một trong số họ, không bỏ qua ... – Mephane

0

Giả sử bạn có thư viện của bên thứ ba và bạn không thể sửa đổi mã của thư viện. Bây giờ giả sử bao gồm các tệp từ thư viện này tạo cảnh báo trình biên dịch. Bạn thường muốn biên dịch mã của riêng bạn ở mức cảnh báo cao, nhưng làm như vậy sẽ tạo ra một bộ cảnh báo lớn từ việc sử dụng thư viện. Bạn có thể viết các tiêu đề ngăn chặn/kích hoạt cảnh báo mà bạn có thể quấn quanh thư viện của bên thứ ba và chúng có thể được bao gồm nhiều lần.

Một loại phức tạp hơn sử dụng là Boost của Preprocessor lặp cấu trúc: http://www.boost.org/doc/libs/1_46_0/libs/preprocessor/doc/index.html

+0

Tôi không thấy làm thế nào không có bao gồm bảo vệ trong wrapper nói sẽ làm cho bất kỳ sự khác biệt. Nếu bạn đã bao gồm trình bao bọc, các cảnh báo sẽ bị vô hiệu hóa cho tiêu đề của bên thứ ba trong * đó * bao gồm rồi, và lần thứ hai bạn bao gồm trình bao bọc, nó sẽ bị bắt bởi trình bảo vệ bao gồm chứ không phải bất kỳ thứ gì. – Mephane

+0

@Mephane: Đó là các tiêu đề chứa các pragma kiểm soát cảnh báo dành riêng cho trình biên dịch mà không bao gồm các bộ bảo vệ, chứ không phải các tiêu đề thư viện bị cảnh báo rải rác. –

3
<cassert> 
<assert.h> 

"Các vĩ mô khẳng định được xác định lại theo quy định của nhà nước hiện hành của NDEBUG mỗi khi < assert.h> được bao gồm. "

4

@sbi đã nói về việc tạo mã, vì vậy hãy để tôi đưa ra một ví dụ.

Giả sử rằng bạn có một đếm rất nhiều mặt hàng, và rằng bạn muốn tạo ra một loạt các chức năng cho mỗi phần tử của nó ...

Một giải pháp là sử dụng bao gồm lừa nhiều này.

// myenumeration.td 
MY_ENUMERATION_META_FUNCTION(Item1) 
MY_ENUMERATION_META_FUNCTION(Item2) 
MY_ENUMERATION_META_FUNCTION(Item3) 
MY_ENUMERATION_META_FUNCTION(Item4) 
MY_ENUMERATION_META_FUNCTION(Item5) 

Sau đó, mọi người chỉ cần sử dụng nó như vậy:

#define MY_ENUMERATION_META_FUNCTION(Item_) \ 
    case Item_: return #Item_; 

char const* print(MyEnum i) 
{ 
    switch(i) { 
    #include "myenumeration.td" 
    } 

    __unreachable__("print"); 
    return 0; // to shut up gcc 
} 

#undef MY_ENUMERATION_META_FUNCTION 

Cho dù điều này là tốt đẹp hoặc hackish là tùy thuộc vào bạn, nhưng rõ ràng nó rất hữu ích không có để thu thập thông qua tất cả các tiện ích chức năng mỗi lần một giá trị mới được thêm vào enum.

+0

Và nếu bạn đặt '#define MY_ENUMERATION_META_FUNCTION' ở đầu myenumeration.td, bạn chỉ có thể bao gồm tệp thứ hai * một lần * và có thể bao gồm tệp đầu tiên một lần. Ví dụ của bạn trông giống như một chu kỳ tùy ý bao gồm sự phụ thuộc có thể tránh được một cách dễ dàng. Nêu tôi sai vui long chân chỉnh tôi. – Mephane

+0

@Mephane: Tôi không hiểu làm thế nào sẽ có một phụ thuộc cyclic cho rằng "enumeration.td" không bao gồm bất cứ điều gì ... Ngoài ra vĩ mô 'MY_ENUMERATION_META_FUNCTION' không có nghĩa là để được định nghĩa trong tập tin liệt kê vì nó sẽ ngăn chặn việc sử dụng nó như một hàm meta, vì vậy nó sẽ đánh bại mục đích để xác định nó ở đó. –

+0

Bây giờ tôi thấy nó. Tệp thứ hai không phải là một tiêu đề khác với mã để sử dụng lại, nhưng thực thi thực tế và mỗi lần triển khai có thể xác định phiên bản riêng của họ là 'MY_ENUMERATION_META_FUNCTION'. Và có, nó hoàn toàn giống như một hack. Liệu việc có thể làm những thứ như thế này là một lợi ích thực sự hay không sẽ là chủ quan của khóa học, nhưng bây giờ tôi thấy khả năng đó. – Mephane

0

Vấn đề với #pragma một lần và lý do không phải là một phần của tiêu chuẩn, là nó không phải lúc nào cũng hoạt động ở mọi nơi. Làm thế nào để trình biên dịch biết nếu hai tệp có cùng một tệp hay không, nếu được bao gồm từ các đường dẫn khác nhau?

Hãy suy nghĩ về điều đó, điều gì sẽ xảy ra nếu trình biên dịch tạo ra lỗi và không bao gồm tệp cần phải có? Điều gì sẽ xảy ra nếu nó bao gồm một tập tin hai lần, mà nó không nên có? Bạn sẽ khắc phục điều đó như thế nào?

Với bảo vệ bao gồm, điều tồi tệ nhất có thể xảy ra là phải mất một chút thời gian để biên dịch.

Chỉnh sửa: Kiểm tra chủ đề này trên comp.std.C++ "#pragma một lần trong tiêu chuẩn ISO chưa?"

http://groups.google.com/group/comp.std.c++/browse_thread/thread/c527240043c8df92

+0

Tôi đặc biệt không yêu cầu sự khác biệt giữa '#pragma once' và' # define' bao gồm cả bảo vệ, nhưng đối với các tình huống hợp lệ nơi bạn đặc biệt muốn bao gồm một tệp nhiều lần. – Mephane

+0

Ok, tôi nghĩ bạn đã hỏi tại sao chúng tôi không có '#pragma once' ở mọi nơi theo mặc định. Giống như câu cuối cùng ... –

+0

Tôi đã hỏi thêm về hiệu ứng mong muốn; '#pragma once' là một phương tiện để đạt được hiệu ứng đó, cũng như các bảo vệ bao gồm. – Mephane

Các vấn đề liên quan