是否存在静态警告?

时间:2012-01-20 02:23:43

标签: c++ static compiler-warnings

我知道this question提到了Boost" STATIC WARNING"但我想再问一次,具体来说,我是如何实施的static_warning的运行方式与static_assert类似,但只在编译时发出警告,而不是中止编译错误。

我喜欢类似于Alexandrescu在C ++前11天中提出的静态断言的提议,它在某种程度上设法打印了一些有用的上下文信息作为错误的一部分。

可以接受要求用户启用某些标准编译器警告以使此构造工作(可能"无效指针转换"或"打破严格的别名规则") - - 无论如何都可以使用任何应该是正常编译一部分的警告。

简而言之,我希望static_warning(false, "Hello world");创建一个编译器警告,该警告应该以某种方式包含字符串" hello world"在警告信息中。这是可能的吗,比如GCC和MSVC,以及如何?

我很乐意为任何特别聪明的解决方案提供小额奖励。


作为一点解释:我在考虑this question时得到了这个想法:静态警告是跟踪复杂模板特化的编译时过程的有用方法,否则很难调试。静态警告可以用作编译器发出的简单信标"我现在正在编译这部分代码。"


更新。理想情况下,警告会在以下设置中触发:

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; }

4 个答案:

答案 0 :(得分:46)

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

可以在命名空间,结构和函数范围调用宏。鉴于输入:

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

Visual C ++ 2010(at / W3或更高版本)说:

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
    ]

Linux上的Clang ++ 3.1产生可以说是更好的输出(颜色未显示):

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.

答案 1 :(得分:14)

这是迄今为止我提出的最好的。它是基本的,并不完全符合您的要求,而是走BOOST_MPL_ASSERT_MSG的路线,因为您的消息必须采用有效标识符的形式。 (据我所知,在警告信息中打印字符串的唯一方法是,您使用的警告也与字符串有关,并打印其内容。)

需要启用未使用变量的警告。在g ++中,这是-Wunused-variable(由-Wall启用),在MSVC中,警告C4101在警告级别3启用。

它显然没有经过严格测试,可以通过几种方式进行增强(在支持的编译器上使用__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__)

测试:

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

在GCC中它产生:

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'

答案 2 :(得分:4)

以下是使用Boost 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__)

它与GMan的解决方案具有相同的限制:消息必须是有效的标识符。这是两个测试

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

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

使用MSVS 2010,第一个测试在没有警告的情况下编译,第二个测试编译时出现警告

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一样,除了它被调整为使用常规设置在各种流行的编译器上生成警告。

答案 3 :(得分:1)

C++14 开始,您可以使用属性 [[deprecated]]

#include <iostream>

[[deprecated]]
void TriassicPeriod() {
    std::clog << "Triassic Period: [251.9 - 208.5] million years ago.\n";
}

[[deprecated("Use NeogenePeriod() instead.")]]
void JurassicPeriod() {
    std::clog << "Jurassic Period: [201.3 - 152.1] million years ago.\n";
}

[[deprecated("Use calcSomethingDifferently(int).")]]    
int calcSomething(int x) {
    return x * 2;
}

int main()
{
    TriassicPeriod();
    JurassicPeriod();
}
GCC (x86-64 gcc 10.3) --std=c++14 -Wall

<source>: In function 'int main()':
<source>:20:20: warning: 'void TriassicPeriod()' is deprecated [-Wdeprecated-declarations]
   20 |     TriassicPeriod();
      |                    ^
<source>:4:6: note: declared here
    4 | void TriassicPeriod() {
      |      ^~~~~~~~~~~~~~
<source>:21:20: warning: 'void JurassicPeriod()' is deprecated: Use NeogenePeriod() instead. [-Wdeprecated-declarations]
   21 |     JurassicPeriod();
      |                    ^
<source>:9:6: note: declared here
    9 | void JurassicPeriod() {
      |      ^~~~~~~~~~~~~~
Compiler returned: 0

MSVC (x64 msvc v19.28 (VS16.9)) /std:c++14 /W4

<source>(20): warning C4996: 'TriassicPeriod': was declared deprecated
<source>(21): warning C4996: 'JurassicPeriod': Use NeogenePeriod() instead.
Compiler returned: 0


借助 C++20,您可以conditionally enable constructors 轻松地使用关键字 requires

#include <type_traits>

template <class T>
struct Foo
{
    Foo() {}

    [[deprecated("Attempting to use pointer type.")]]
    Foo() requires(std::is_pointer<T>::value) {}
};

int main()
{
    Foo<int> a;
    Foo<int*> b;
}
GCC (x86-64 gcc 10.3) --std=c++20 -Wall

<source>: In function 'int main()':
<source>:12:14: warning: 'Foo<T>::Foo() requires  std::is_pointer<_Tp>::value [with T = int*]' is deprecated: Attempting to use pointer type. [-Wdeprecated-declarations]
   12 |   Foo<int *> b;
      |              ^
<source>:6:53: note: declared here
    6 |   [[deprecated("Attempting to use pointer type.")]] Foo() requires(
      |                                                     ^~~
Compiler returned: 0

MSVC (x64 msvc v19.28 (VS16.9)) /std:c++latest /W4

<source>(12): warning C4996: 'Foo<int *>::Foo': Attempting to use pointer type.
Compiler returned: 0


如果你想使用类似于 static_warning(false, "Hello world"); 的东西,出于各种原因,我们不得不使用宏。 例如,请参见 How to pass raw string literals to [[deprecated(message)]] attribute?