2012-01-20 29 views
56

Tôi nhận thức được this question đó đề cập đến "CẢNH BÁO TĨNH" Boost, nhưng tôi muốn hỏi một lần nữa, đặc biệt là, làm thế nào tôi có thể thực hiện một static_warning mà hoạt động tương tự như static_assert nhưng chỉ phát ra một cảnh báo tại thời gian biên dịch chứ không phải một lỗi biên dịch hủy bỏ.Có tồn tại một static_warning không?

Tôi muốn một cái gì đó tương tự như đề xuất của Alexandrescu cho một khẳng định tĩnh trong pre-C++ 11 ngày mà bằng cách nào đó quản lý để in một số thông tin ngữ cảnh hữu ích như là một phần của lỗi. Có thể chấp nhận yêu cầu người dùng bật cảnh báo trình biên dịch chuẩn nhất định để xây dựng này hoạt động (có thể "chuyển đổi con trỏ không hợp lệ" hoặc "phá vỡ quy tắc bí danh nghiêm ngặt") - bất kỳ cảnh báo nào phải là một phần của bình thường biên dịch anyway có thể được sử dụng.

Tóm lại, tôi muốn static_warning(false, "Hello world"); để tạo cảnh báo trình biên dịch bằng cách nào đó sẽ bao gồm chuỗi "hello world" trong thông điệp cảnh báo. Điều này có thể xảy ra, nói trong GCC và MSVC, và làm thế nào?

Tôi vui vẻ sẽ tặng một phần thưởng nhỏ cho bất kỳ giải pháp đặc biệt thông minh nào.


Là một chút giải thích: Tôi có ý tưởng khi nghĩ về this question: Một cảnh báo tĩnh sẽ là một cách hữu ích để theo dõi trong suốt quá trình thời gian biên dịch mẫu chuyên ngành phức tạp, đó là trường hợp khá khó khăn để gỡ lỗi . Một cảnh báo tĩnh có thể được sử dụng như một ngọn hải đăng đơn giản cho trình biên dịch để phát ra "Bây giờ tôi đang biên dịch phần này của mã."


Cập nhật. Lý tưởng nhất, cảnh báo sẽ được kích hoạt trong thiết lập sau:

template <typename T> struct Foo 
{ 
    static_warning(std::is_pointer<T>::value, "Attempting to use pointer type."); 
    // ... 
}; 

int main() { Foo<int> a; Foo<int*> b; } 
+0

Bạn đang tìm kiếm tính di động? Tôi biết rằng một số trình biên dịch thực hiện các móc tương tự cho bộ tiền xử lý ('# error',' # warning', '# message') vì vậy có lẽ nó sẽ có ý nghĩa để thực sự thực hiện những cái đó trong gcc và Clang? –

+1

@VioletGiraffe: '# warning' là về cảnh báo tiền xử lý theo như tôi biết, và không liên quan gì đến các mẫu instanciations. –

+2

GCC cho phép thuộc tính 'deprecated' được áp dụng cho các biến, kiểu và hàm; điều này có thể bao gồm một thông điệp tùy ý (xem http://gcc.gnu.org/onlinedocs/gcc/Type-Attributes.html#Type-Attributes).Tôi đã thử hack một giải pháp bằng cách sử dụng nó, nhưng cho đến nay các chi tiết lảng tránh tôi; nó có thể là một thành phần giải pháp khả thi, mặc dù. –

Trả lời

37

Playing tắt của bình luận của Michael E:

#if defined(__GNUC__) 
#define DEPRECATE(foo, msg) foo __attribute__((deprecated(msg))) 
#elif defined(_MSC_VER) 
#define DEPRECATE(foo, msg) __declspec(deprecated(msg)) foo 
#else 
#error This compiler is not supported 
#endif 

#define PP_CAT(x,y) PP_CAT1(x,y) 
#define PP_CAT1(x,y) x##y 

namespace detail 
{ 
    struct true_type {}; 
    struct false_type {}; 
    template <int test> struct converter : public true_type {}; 
    template <> struct converter<0> : public false_type {}; 
} 

#define STATIC_WARNING(cond, msg) \ 
struct PP_CAT(static_warning,__LINE__) { \ 
    DEPRECATE(void _(::detail::false_type const&),msg) {}; \ 
    void _(::detail::true_type const&) {}; \ 
    PP_CAT(static_warning,__LINE__)() {_(::detail::converter<(cond)>());} \ 
} 

// Note: using STATIC_WARNING_TEMPLATE changes the meaning of a program in a small way. 
// It introduces a member/variable declaration. This means at least one byte of space 
// in each structure/class instantiation. STATIC_WARNING should be preferred in any 
// non-template situation. 
// 'token' must be a program-wide unique identifier. 
#define STATIC_WARNING_TEMPLATE(token, cond, msg) \ 
    STATIC_WARNING(cond, msg) PP_CAT(PP_CAT(_localvar_, token),__LINE__) 

Macro có thể được gọi tại không gian tên, cấu trúc và phạm vi chức năng. Với đầu vào:

#line 1 
STATIC_WARNING(1==2, "Failed with 1 and 2"); 
STATIC_WARNING(1<2, "Succeeded with 1 and 2"); 

struct Foo 
{ 
    STATIC_WARNING(2==3, "2 and 3: oops"); 
    STATIC_WARNING(2<3, "2 and 3 worked"); 
}; 

void func() 
{ 
    STATIC_WARNING(3==4, "Not so good on 3 and 4"); 
    STATIC_WARNING(3<4, "3 and 4, check"); 
} 

template <typename T> struct wrap 
{ 
    typedef T type; 
    STATIC_WARNING(4==5, "Bad with 4 and 5"); 
    STATIC_WARNING(4<5, "Good on 4 and 5"); 
    STATIC_WARNING_TEMPLATE(WRAP_WARNING1, 4==5, "A template warning"); 
}; 

template struct wrap<int>; 

GCC 4.6 (ở mức cảnh báo mặc định) sản xuất:

 
static_warning.cpp: In constructor ‘static_warning1::static_warning1()’: 
static_warning.cpp:1:1: warning: ‘void static_warning1::_(const detail::false_type&)’ 
    is deprecated (declared at static_warning.cpp:1): Failed with 1 and 2 [-Wdeprecated-declarations] 
static_warning.cpp: In constructor ‘Foo::static_warning6::static_warning6()’: 
static_warning.cpp:6:3: warning: ‘void Foo::static_warning6::_(const detail::false_type&)’ 
    is deprecated (declared at static_warning.cpp:6): 2 and 3: oops [-Wdeprecated-declarations] 
static_warning.cpp: In constructor ‘func()::static_warning12::static_warning12()’: 
static_warning.cpp:12:3: warning: ‘void func()::static_warning12::_(const detail::false_type&)’ 
    is deprecated (declared at static_warning.cpp:12): Not so good on 3 and 4 [-Wdeprecated-declarations] 
static_warning.cpp: In constructor ‘wrap<T>::static_warning19::static_warning19() [with T = int]’: 
static_warning.cpp:24:17: instantiated from here 
static_warning.cpp:19:3: warning: ‘void wrap<T>::static_warning19::_(const detail::false_type&) [with T = int]’ 
    is deprecated (declared at static_warning.cpp:19): Bad with 4 and 5 [-Wdeprecated-declarations] 

Trong khi Visual C++ 2010 (tại/W3 trở lên) nói:

 
warnproj.cpp(1): warning C4996: 'static_warning1::_': Failed with 1 and 2 
warnproj.cpp(1) : see declaration of 'static_warning1::_' 
warnproj.cpp(6): warning C4996: 'Foo::static_warning6::_': 2 and 3: oops 
warnproj.cpp(6) : see declaration of 'Foo::static_warning6::_' 
warnproj.cpp(12): warning C4996: 'func::static_warning12::_': Not so good on 3 and 4 
warnproj.cpp(12) : see declaration of 'func::static_warning12::_' 
warnproj.cpp(19): warning C4996: 'wrap<T>::static_warning19::_': Bad with 4 and 5 
    with 
    [ 
     T=int 
    ] 
warnproj.cpp(19) : see declaration of 'wrap<T>::static_warning19::_' 
    with 
    [ 
     T=int 
    ] 
warnproj.cpp(19) : while compiling class template member function 'wrap<T>::static_warning19::static_warning19(void)' 
    with 
    [ 
     T=int 
    ] 
warnproj.cpp(24) : see reference to class template instantiation 'wrap<T>::static_warning19' being compiled 
    with 
    [ 
     T=int 
    ] 

Clang ++ 3.1 trên Linux cho kết quả đầu ra đẹp hơn (màu không được hiển thị):

 
tst3.cpp:1:1: warning: '_' is deprecated: Failed with 1 and 2 
     [-Wdeprecated-declarations] 
STATIC_WARNING(1==2, "Failed with 1 and 2"); 
^ 
tst3.cpp:24:38: note: expanded from macro 'STATIC_WARNING' 
    PP_CAT(static_warning,__LINE__)() {_(::detail::converter<(cond)>());} \ 
            ^
tst3.cpp:6:3: warning: '_' is deprecated: 2 and 3: oops 
     [-Wdeprecated-declarations] 
    STATIC_WARNING(2==3, "2 and 3: oops"); 
^
tst3.cpp:24:38: note: expanded from macro 'STATIC_WARNING' 
    PP_CAT(static_warning,__LINE__)() {_(::detail::converter<(cond)>());} \ 
            ^
tst3.cpp:12:3: warning: '_' is deprecated: Not so good on 3 and 4 
     [-Wdeprecated-declarations] 
    STATIC_WARNING(3==4, "Not so good on 3 and 4"); 
^
tst3.cpp:24:38: note: expanded from macro 'STATIC_WARNING' 
    PP_CAT(static_warning,__LINE__)() {_(::detail::converter<(cond)>());} \ 
            ^
tst3.cpp:19:3: warning: '_' is deprecated: Bad with 4 and 5 
     [-Wdeprecated-declarations] 
    STATIC_WARNING(4==5, "Bad with 4 and 5"); 
^
tst3.cpp:24:38: note: expanded from macro 'STATIC_WARNING' 
    PP_CAT(static_warning,__LINE__)() {_(::detail::converter<(cond)>());} \ 
            ^
tst3.cpp:23:17: note: in instantiation of member function 
     'wrap<int>::static_warning19::static_warning19' requested here 
template struct wrap<int> 
       ^
4 warnings generated. 
+0

Tại sao bạn không sử dụng thuộc tính gcc 'warning'? – PlasmaHH

+0

@PlasmaHH: Từ gCC texinfo: 'warning (" message ")': "Nếu thuộc tính này được sử dụng trên khai báo hàm và cuộc gọi đến hàm như vậy không bị loại bỏ thông qua ... tối ưu hóa, cảnh báo sẽ bao gồm' tin nhắn' sẽ được chẩn đoán. " Tôi không muốn tối ưu hóa là một mối quan tâm. Sau khi tất cả, các cuộc gọi hàm STATIC_WARNING (...) được thiết kế để được tối ưu hoàn toàn. – Managu

+0

Ah, kỳ lạ, tôi đã bị ấn tượng rằng cảnh báo và không được chấp nhận có cùng yêu cầu ở đó và chỉ có thông điệp khác nhau. – PlasmaHH

15

Dưới đây là tốt nhất mà tôi đã đưa ra cho đến nay. Đó là cơ bản và không hoàn toàn phù hợp với yêu cầu của bạn, nhưng thay vào đó, hãy đi theo tuyến đường của BOOST_MPL_ASSERT_MSG trong đó thư của bạn phải có dạng nhận dạng hợp lệ. (Theo như tôi biết, cách duy nhất bạn có thể nhận được một chuỗi được in trong thông điệp cảnh báo là nếu cảnh báo bạn sử dụng cũng xảy ra với một chuỗi và in nội dung của nó.)

Yêu cầu cảnh báo cho một biến không sử dụng được kích hoạt. Trong g ++ đây là -Wunused-variable (kích hoạt bởi -Wall), và trong MSVC nó cảnh báo C4101 được kích hoạt tại Warning Cấp 3.

Nó rõ ràng không phải là rất thử nghiệm và có thể được tăng cường trong một vài cách (sử dụng __COUNTER__ thay vì __LINE__ về hỗ trợ trình biên dịch, in ấn thông điệp đẹp hơn, sử dụng Boost để đơn giản hóa, vv), nhưng dường như hoàn thành công việc.Dưới đây là các nồi hơi-tấm:

namespace detail 
{ 
    template <bool Condition> 
    struct static_warning; 

    template <> 
    struct static_warning<true> 
    { 
     template <typename Message> 
     static void warn() {} 
    }; 

    template <> 
    struct static_warning<false> 
    { 
     // If you're here because of a warning, please see where the 
     // template was instantiated for the source of the warning. 
     template <typename Message> 
     static void warn() { Message STATIC_WARNING_FAILED; } 
    }; 
} 

#define STATIC_WARNING_DETAIL_EX(cond, msg, line)     \ 
     struct static_warning ## line        \ 
     {               \ 
      class msg {};           \ 
                    \ 
      static_warning ## line()        \ 
      {              \ 
       ::detail::static_warning<(cond)>::     \ 
        warn<void************ (msg::************)()>(); \ 
      }              \ 
     } 

#define STATIC_WARNING_DETAIL(cond, msg, line) \ 
     STATIC_WARNING_DETAIL_EX(cond, msg, line) 

// Use these: 
#define STATIC_WARNING_MSG(cond, msg) \ 
     STATIC_WARNING_DETAIL(cond, msg, __LINE__) 

#define STATIC_WARNING(cond) \ 
     STATIC_WARNING_DETAIL(cond, STATIC_WARNING_FAILED, __LINE__) 

Và một thử nghiệm:

STATIC_WARNING(sizeof(int) == 2); 

int main() 
{ 
    STATIC_WARNING_MSG(sizeof(char) != 1, JUST_KIDDING_ALL_IS_WELL); 
} 

Trong MSVC này sản xuất:

>main.cpp(19): warning C4101: 'STATIC_WARNING_FAILED' : unreferenced local variable 
>   main.cpp(45) : see reference to function template instantiation 'void detail::static_warning<false>::warn<void************(__thiscall static_warning45::STATIC_WARNING_FAILED::* ***********)(void)>(void)' being compiled 
>main.cpp(19): warning C4101: 'STATIC_WARNING_FAILED' : unreferenced local variable 
>   main.cpp(49) : see reference to function template instantiation 'void detail::static_warning<false>::warn<void************(__thiscall main::static_warning49::JUST_KIDDING_ALL_IS_WELL::* ***********)(void)>(void)' being compiled 

Và trong GCC nó tạo ra:

main.cpp: In static member function 'static void detail::static_warning<false>::warn() [with Message = void************ (static_warning39::STATIC_WARNING_FAILED::************)()]': 
main.cpp:39:1: instantiated from here 
main.cpp:19:38: warning: unused variable 'STATIC_WARNING_FAILED' 
main.cpp: In static member function 'static void detail::static_warning<false>::warn() [with Message = void************ (main()::static_warning43::JUST_KIDDING_ALL_IS_WELL::************)()]': 
main.cpp:43:5: instantiated from here 
main.cpp:19:38: warning: unused variable 'STATIC_WARNING_FAILED' 
+3

Điều này là rất thú vị, nhưng nó chỉ xuất hiện để làm việc trong một chức năng miễn phí hoặc bối cảnh toàn cầu. Tôi không thể làm cho nó in bất kỳ cảnh báo từ bên trong một mẫu lớp học ... –

4

Đây là một giải pháp mà sử dụng Boost thư viện MPL:

#include <boost/mpl/eval_if.hpp> 
#include <boost/mpl/identity.hpp> 
#include <boost/mpl/print.hpp> 

#define static_warning_impl2(cond, msg, line) \ 
    struct static_warning_ ## line { \ 
     struct msg {}; \ 
     typedef typename boost::mpl::eval_if_c< \ 
      cond, \ 
      boost::mpl::identity<msg>, \ 
      boost::mpl::print<msg> \ 
     >::type msg ## _; \ 
    } 

#define static_warning_impl1(cond, msg, line) \ 
    static_warning_impl2(cond, msg, line) 

#define static_warning(cond, msg) \ 
    static_warning_impl1(cond, msg, __LINE__) 

Nó đi kèm với các hạn chế tương tự như giải pháp GMAN của: thông báo phải có một định danh hợp lệ. Dưới đây là hai bài kiểm tra

static_warning(sizeof(int) == 4, size_of_int_is_not_4); 

static_warning(sizeof(int) == 2, size_of_int_is_not_2); 

Với MSVS năm 2010 thử nghiệm đầu tiên biên dịch mà không cần cảnh báo, thứ hai biên dịch với các cảnh báo

C:\Libraries\Boost\boost_1_48_0\boost/mpl/print.hpp(51): warning C4308: negative integral constant converted to unsigned type 
    C:\Libraries\Boost\boost_1_48_0\boost/mpl/eval_if.hpp(63) : see reference to class template instantiation 'boost::mpl::print<T>' being compiled 
    with 
    [ 
     T=static_warning_28::size_of_int_is_not_2 
    ] 
    Test.cpp(28) : see reference to class template instantiation 'boost::mpl::eval_if_c<C,F1,F2>' being compiled 
    with 
    [ 
     C=false, 
     F1=boost::mpl::identity<static_warning_28::size_of_int_is_not_2>, 
     F2=boost::mpl::print<static_warning_28::size_of_int_is_not_2> 
    ] 

Mã này sử dụng tăng :: mpl: :in. Từ cuốn sách C++ Template Metaprogramming bởi D. Abrahams và A. Gurtovoy, trang 171:

Để tạo một bản ghi thực hiện thời gian biên dịch, chúng tôi cần một cách để tạo ra một thông điệp chẩn đoán - một cảnh báo. Bởi vì không có cấu trúc đơn nào khiến tất cả các trình biên dịch tạo ra cảnh báo (thực ra, hầu hết các trình biên dịch đều cho phép bạn vô hiệu cảnh báo), MPL có một giao thức print giống như identity ngoại trừ việc nó được điều chỉnh để tạo ra cảnh báo về nhiều loại phổ biến trình biên dịch với cài đặt thông thường của họ.

+0

Tôi nghĩ bạn đang thiếu' #include < boost/mpl/eval_if.hpp> ', và cũng' typename' ở phía trước 'boost :: eval_if_c'. Dù sao, tôi không thể làm điều này để in bất cứ điều gì (GCC 4.6.2); biên dịch chỉ đi qua mà không có bất kỳ thông báo nào cả ... –

+0

Tôi đã sửa lỗi #include còn thiếu và tên tệp bị thiếu. Nếu mã không tạo ra bất kỳ cảnh báo nào với GCC 4.6.2, thì đó có thể là lỗi tăng :: mpl :: print. Bạn đang sử dụng phiên bản Boost nào? – user763305

+0

Điều gì xảy ra trên GCC 4.6.2 nếu bạn biên dịch #include NEWLINE boost :: mpl :: print :: nhập a; – user763305

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