Is there static_warning?

I know this question that mentions Boost "STATIC WARNING", but I would like to ask again, in particular, how could I implement static_warning , which works similarly to static_assert , but only generates a warning at compile time, and does not static_assert the compilation error.

I would like something similar to Alexandrescu's suggestion for a static statement in pre-C ++ for 11 days, which somehow was able to print some useful contextual information as part of the error.

It would be acceptable to require the user to turn on certain standard compiler warnings so that this construction works (possibly “invalid pointer conversion” or “violates strict alias rules”) - any warning that should be part of normal compilation can be used anyway.

In short, I want static_warning(false, "Hello world"); Generated a compiler warning, which should somehow include the string "hello world" in the warning message. Is this possible, say, in GCC and MSVC, and how?

I would gladly give a small reward for the reward for any particularly smart decision.




As a small explanation: I had an idea to think about this issue : a static warning would be a useful way to track the compilation process of complex specialized templates that are otherwise quite difficult to debug. A static warning can be used as a simple beacon for the compiler to emit "I'm compiling this part of the code now."




Update. Ideally, a warning will be triggered in the following setting:

 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; } 
+65
c ++ static compiler-warnings
Jan 20 '12 at 2:23
source share
3 answers

Reproduction of commentary by 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__) 

A macro can be invoked in a namespace, structure, and region. Given 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 (at the default warning level) produces:

 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]

While Visual C ++ 2010 (at / W3 or higher) says:

 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 on Linux gives a nicer result (color not shown):

 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.
+46
Jan 24 '12 at 16:20
source share

Here is the best I've come up with so far. It is basic and does not quite meet your requirements, but instead goes the route BOOST_MPL_ASSERT_MSG , in which your message should take the form of a valid identifier. (As far as I know, the only way to get the line printed in the warning message is that the warning you used was also associated with the lines and printed its contents.)

This requires that a warning about an unused variable be turned on. In g ++, this is -Wunused-variable ( -Wall ), and in MSVC this is warning C4101, which is enabled at warning level 3.

It is clearly not very tested and can be expanded in several ways (use __COUNTER__ instead of __LINE__ for supported compilers, more beautiful message printing, use Boost to simplify, etc.), but it seems to do the job, Here’s the boiler plate:

 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__) 

And the test:

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

In MSVC, this produces:

 >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 

And at GCC, he produces:

 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' 
+14
Jan 20 2018-12-12T00:
source share

Here is a solution using the Boost MPL library:

 #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__) 

It comes with the same restriction as the GMan solution: the message must be a valid identifier. Here are two tests.

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

and

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

With MSVS 2010, the first test compiles with no warnings, the second compiles with a warning

 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> ] 

The code uses boost :: mpl :: print. From the book C ++ Template Metaprogramming D. Abraham and A. Gurtova, p. 171:

To create a compile-time execution log, we need a way to generate a diagnostic message — a warning. Since there is no single construct that will cause all compilers to generate a warning (indeed, most compilers allow you to turn off warnings altogether), MPL has a print metafunction that is similar to identity , except that it is configured to generate a warning about many popular compilers with their usual settings.

+4
Jan 26 2018-12-12T00:
source share



All Articles