是否存在static_warning?
我意识到这个提到Boost的“STATIC WARNING”的问题,但是我想再问一次,具体地说,我该如何实现一个类似于static_assert
的static_warning
,但是只在编译时发出警告 ,而不是中止编译错误。
我想要一些类似于Alexandrescu提出的在C ++之前的11天中静态断言的提议,它以某种方式设法打印一些有用的上下文信息作为错误的一部分。
要求用户启用某些标准的编译器警告才能使这种构造工作(也许是“无效的指针转换”或“打破严格的别名规则”) – 任何应该是正常编译的一部分的警告都是可以接受的使用。
总之,我想static_warning(false, "Hello world");
创build一个编译器警告,应该以某种方式在警告消息中包含string“hello world”。 在GCC和MSVC中这是可能的,以及如何?
我乐意给予任何特别聪明的解决scheme一个小的奖励赏金。
作为一点解释:当我想到这个问题的时候,我得到了这个想法:静态警告将是一个有用的方式来跟踪复杂的模板特化,这是很难debugging的复杂模板特化。 静态警告可以用作编译器发出“我正在编译这部分代码”的简单信号灯。
更新。 理想情况下,警告将在以下设置中触发:
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; }
嘲笑迈克尔·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__)
macros可以在命名空间,结构体和函数作用域中调用。 鉴于input:
#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(默认警告级别)产生:
static_warning.cpp:在构造函数'static_warning1 :: static_warning1()'中: static_warning.cpp:1:1:warning:'void static_warning1 :: _(const detail :: false_type&)' 已弃用(在static_warning.cpp中声明:1): 1和2失败 [-Wdeprecated-declarations] static_warning.cpp:在构造函数'Foo :: static_warning6 :: static_warning6()'中: static_warning.cpp:6:3:warning:'void Foo :: static_warning6 :: _(const detail :: false_type&)' 已弃用(在static_warning.cpp中声明:6): 2和3:oops [-Wdeprecated-declarations] static_warning.cpp:在构造函数'func():: static_warning12 :: static_warning12()'中: static_warning.cpp:12:3:warning:'void func():: static_warning12 :: _(const detail :: false_type&)' 已弃用(在static_warning.cpp中声明:12): 对3和4不太好 [-Wdeprecated-declarations] static_warning.cpp:在构造函数'wrap <T> :: static_warning19 :: static_warning19()[with T = int]': static_warning.cpp:24:17:从这里实例化 static_warning.cpp:19:3:warning:'void wrap <T> :: static_warning19 :: _(const detail :: false_type&)[with T = int]' 已弃用(在static_warning.cpp声明:19): 与4和5 [-Wdeprecated声明]
虽然Visual C ++ 2010(在/ W3或以上)说:
warnproj.cpp(1):warning C4996:'static_warning1 :: _':1和2失败 warnproj.cpp(1):参见“static_warning1 :: _”声明 warnproj.cpp(6):warning C4996:'Foo :: static_warning6 :: _':2 and 3:oops warnproj.cpp(6):参见'Foo :: static_warning6 :: _'声明 warnproj.cpp(12):warning C4996:'func :: static_warning12 :: _':3和4不太好 warnproj.cpp(12):参见“func :: static_warning12 :: _”声明 warnproj.cpp(19):警告C4996:'wrap <T> :: static_warning19 :: _':与4和5错误 同 [ T = INT ] warnproj.cpp(19):看看'wrap <T> :: static_warning19 :: _'的声明 同 [ T = INT ] warnproj.cpp(19):在编译类模板成员函数'wrap <T> :: static_warning19 :: static_warning19(void)' 同 [ T = INT ] warnproj.cpp(24):参见正在编译的类模板实例化'wrap <T> :: static_warning19' 同 [ T = INT ]
Linux上的Clang ++ 3.1产生可以说更好的输出(颜色未显示):
tst3.cpp:1:1:警告:不推荐使用“_”:1和2失败 [-Wdeprecated-声明] STATIC_WARNING(1 == 2,“1和2失败”); ^ tst3.cpp:24:38:注意:从macros'STATIC_WARNING' PP_CAT(static_warning,__ LINE __)(){_(:: detail :: converter <(cond)>());} \ ^ tst3.cpp:6:3:警告:不推荐使用“_”:2和3:oops [-Wdeprecated-声明] STATIC_WARNING(2 == 3,“2 and 3:oops”); ^ tst3.cpp:24:38:注意:从macros'STATIC_WARNING' PP_CAT(static_warning,__ LINE __)(){_(:: detail :: converter <(cond)>());} \ ^ tst3.cpp:12:3:警告:'_'被弃用:3和4不太好 [-Wdeprecated-声明] STATIC_WARNING(3 == 4,“3和4不太好”); ^ tst3.cpp:24:38:注意:从macros'STATIC_WARNING' PP_CAT(static_warning,__ LINE __)(){_(:: detail :: converter <(cond)>());} \ ^ tst3.cpp:19:3:警告:'_'已弃用:与4和5不一致 [-Wdeprecated-声明] STATIC_WARNING(4 == 5,“4和5不好”); ^ tst3.cpp:24:38:注意:从macros'STATIC_WARNING' PP_CAT(static_warning,__ LINE __)(){_(:: detail :: converter <(cond)>());} \ ^ tst3.cpp:23:17:注意:在成员函数的实例化中 'wrap <int> :: static_warning19 :: static_warning19'在这里要求 模板结构包装<int> ^ 生成4个警告。
这是迄今为止我所做的最好的。 这是基本的,并不完全符合你的要求,而是采取BOOST_MPL_ASSERT_MSG
的路线,因为你的消息必须采取有效的标识符的forms。 (据我所知,在警告消息中打印string的唯一方法是如果你使用的警告也是与string有关的,并且打印它的内容。)
它要求启用未使用variables的警告。 在g ++中,这是-Wunused-variable
(由-Wall
启用),并且在MSVC中警告级别为3的C4101。
这显然不是很经过testing,可以通过几种方法加以改进(在支持的编译器上使用__COUNTER__
而不是__LINE__
,更漂亮的消息打印,使用Boost来简化等),但似乎完成了这项工作。 这是锅炉板:
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__)
还有一个testing:
STATIC_WARNING(sizeof(int) == 2); int main() { STATIC_WARNING_MSG(sizeof(char) != 1, JUST_KIDDING_ALL_IS_WELL); }
在MSVC中产生:
>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
而在海湾合作委员会它产生:
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'
这是一个使用Boost MPL库的解决scheme:
#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__)
它与GMan的解决scheme具有相同的限制:消息必须是有效的标识符。 这里有两个testing
static_warning(sizeof(int) == 4, size_of_int_is_not_4);
和
static_warning(sizeof(int) == 2, size_of_int_is_not_2);
MSVS 2010第一次testing编译没有警告,第二次编译与警告
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> ]
代码使用boost :: mpl :: print。 从D.Abrahams和A.Gurtovoy的书“ C ++ Template Metaprogramming” ,第171页:
要生成编译时执行日志,我们需要一种生成诊断消息的方法 – 警告。 由于没有任何单一的构造会导致所有的编译器产生警告(实际上,大多数编译器可以让你完全禁用警告),MPL有一个print
元函数,就像identity
一样,只是它被调整为对各种stream行编译器与他们通常的设置。